Files
ordoor/doc/formats/obj.md

7.4 KiB

Obj format information

The names of Obj files are highly suggestive of them being objects that can be placed on maps. Each map cell seems to have space for four objects to be placed on it - a SURFACE, LEFT, RIGHT, and CENTER. Maps reference objects in a space-efficient way via sets, which seem to be a kind of object palette.

Assign

The Assign/ directory contains a matching .asn file for each Obj/*.obj. It's a plain-text format which seems to assign properties to frames, and has references to a <name>.flc file which does not exist in the tree.

Theory: .obj files were originally generated from .flc files. This is an AutoDesk format for visual data, so this suggests the .obj files contain pixels \o/

blank.asn references 6 frames (0-5):

# single pixel tile
# transpix.obj/.asn   
#/--> transpix.flc
#

0-5:DEF 1;   


0-5:TYPE 13;


END OF FILE

jungtil.asn references 18 frames (0-17):

# jungle floor
# jungtil.obj/.asn
#   /--> d:\warflics\missions\jungtil.flc
#

0:DEF 2;
1-11:DEF 454;   


#damaged frames!!!!
12:DEF 2;   
13-16:DEF 454;   
17:DEF 454;


0:TYPE 2;
1-11:TYPE 0;
12:TYPE 2;
13-16:TYPE 0;   
17:TYPE 0;


0:DESTROY 12;  
1-3:DESTROY 13;  
4-6:DESTROY 14;  
7-9:DESTROY 15;  
10-11:DESTROY 16;  
17:DESTROY 15;

1-11:Dmg1Lnk 17;

END OF FILE

So it seems this visual data can have quite complicated attributes. At a minimum we see:

  • TYPE
  • DEF
  • DESTROY
  • Dmg1Lnk

The type field may tell us what format each sprite is in.

OBJ container structure

.obj files represent visual data. They contain a number of sprites, which are assigned attributes in .asn files and referenced from .map files.

The container format is worked out, but the per-sprite data is still unknown, so I'm documenting the former here while still investigating the latter.

The file begins with a header, with all values 32-bit little-endians:

Offset Meaning
0x0000 Number of sprites
0x0004 Offset of sprite directory
0x0008 Size of sprite directory
0x000c Offset of the sprite data
0x0010 Size of the sprite data

The sprite directory contains an 8-byte record per sprite. That record contains two 32-bit little-endians:

Offset Meaning
0x0000 Relative offset of sprite in data block
0x0004 Size of sprite in data block

For sanity checks, we can ensure that:

  • The header, sprite directory and data blocks do not overlap or extend past the end of the file
  • None of the individual sprites overlap each other or extend past the end of the data block

Sprite structure

If the type field of an .asn field does tell us how to interpret sprite data, I'll need to split this up per type. For now, I'm investigating a small number of files in depth, and comparing across files in a shallow manner, so all I need to do is manually select sets with the same type in the latter case.

First, the blank.obj file. blank.asn helpfully tells us that it's a single- pixel tile and assigns it a type of 13. There are six sprites, all of which are identical. Data dump:

-------------------------------------------------------------------------------
        0  1  2  3  4  5  6  7       01234567     0   1   2   3   4   5   6   7
-------------------------------------------------------------------------------
0x0000 10 01 61 01 01 00 01 00     |   a      |  16   1  97   1   1   0   1   0
0x0008 00 00 00 00 03 00 00 00     |          |   0   0   0   0   3   0   0   0
0x0010 00 00 00 00 00 00 00 00     |          |   0   0   0   0   0   0   0   0
0x0018 01 fd 00                    |          |   1 253   0

Total size is 27 bytes, so if there is only one pixel, it's tempting to suggest that the first 24 bytes are header, and the final 3 bytes represent that one pixel.

I think this is rendered as a 1px dot with the colour #ff00ff in WH40K_TD.exe:

blank.obj as rendered by WH40K_TD.exe

In the 64x64 tile, the dot is in the very centre.

The colour itself doesn't show up in the data directly, so it's not a simple RGB array of pixels. WH40K_TD.exe is a 16-bit-colour application and may use a palette, but these factoids don't help me make immediate sense of the data. If there's a palette in the first 24 bytes, it's not obvious.

There are 45 TYPE 13 .obj files in the game. Comparing the above with the start of the second sprite in pillar.obj:

----------------------------------------------
        0  1  2  3  4  5  6  7       01234567
----------------------------------------------
0x0000 fa 00 25 01 2e 00 48 00     |       H  | 250   0  37   1  46   0  72   0 
0x0008 00 00 00 00 f4 0c 00 00     |          |   0   0   0   0 244  12   0   0 
0x0010 00 00 00 00 00 00 00 00     |          |   0   0   0   0   0   0   0   0 
0x0018 80 0f 90 1a 4b 1a 1b 4b     |     K  K | 128  15 144  26  75  26  27  75 
0x0020 1a 4c 1a 4b 19 19 49 19     |  L K  I  |  26  76  26  75  25  25  73  25 
0x0028 4a 18 4a 80 0f 00 80 0b     | J J      |  74  24  74 128  15   0 128  11 
0x0030 98 1a 4c 1a 4c 1a 9e 1a     |   L L    | 152  26  76  26  76  26 158  26 
0x0038 4c 1b 4b 1a 1a 49 19 48     | L K  I H |  76  27  75  26  26  73  25  72 
0x0040 18 18 48 19 4b 19 4b 19     |   H K K  |  24  24  72  25  75  25  75  25 
0x0048 9d 80 0b 00 80 08 9e 1a     |          | 157 128  11   0 128   8 158  26 
0x0050 4a 1a 1b 4d 9e 1a 4c 1b     | J  M  L  |  74  26  27  77 158  26  76  27 
0x0058 1b 4c 1b 4c 1a 1a 49 18     |  L L  I  |  27  76  27  76  26  26  73  24 
0x0060 48 17 49 18 19 4a 19 4b     | H I  J K |  72  23  73  24  25  74  25  75 
0x0068 19 9d 4a 1a 49 80 08 00     |   J I    |  25 157  74  26  73 128   8   0 
0x0070 80 07 a0 19 4b 1a 1b 4c     |     K  L | 128   7 160  25  75  26  27  76 
0x0078 1b 1b 4d 9e 1b 4c 1b 4d     |   M  L M |  27  27  77 158  27  76  27  77 

Total size is 3340. The first 24 bytes look quite different to the remainder of this file, lending weight to the 24-byte header theory.

Line-by-line comparison:

a 0x0000 10 01 61 01 01 00 01 00 | a | 16 1 97 1 1 0 1 0 b 0x0000 fa 00 25 01 2e 00 48 00 | H | 250 0 37 1 46 0 72 0

a 0x0008 00 00 00 00 03 00 00 00 | | 0 0 0 0 3 0 0 0 b 0x0008 00 00 00 00 f4 0c 00 00 | | 0 0 0 0 244 12 0 0

a 0x0010 00 00 00 00 00 00 00 00 | | 0 0 0 0 0 0 0 0 b 0x0010 00 00 00 00 00 00 00 00 | | 0 0 0 0 0 0 0 0

Assuming a 24-byte header, 0x0c matches "remaining pixeldata" size in both cases

  • 3 and 3316.

It's odd that the last 8 bytes of this putative header are empty in both cases. I need to see if I can find any examples that store it.

0x04 makes sense as height & width, 2 16-bit values, in blank.obj where we know it's 1x1 pixel.

It makes less sense as such for pillar.obj where the individual sprites fit into the 64x64x64 volume of a cell:

pillar.obj as individual sprites

WH40K_TD.exe crashes trying to load a set referencing pillar in it unless it's in the CENTER position. Interesting.

Next: keep adding cases to the header comparison above until I make sense of it all. Since pillar.obj is composable, it's not the best example I could have picked. Need more non-composable ones, in case that makes a difference.

Offset Purpose
0x0000 ?
0x0004 ?
0x0008 ?
0x000c Size of remaining pixeldata
0x0010 Padding?
0x0014 Padding?