2018-03-18 04:23:34 +00:00
|
|
|
# Obj format information
|
|
|
|
|
|
|
|
The names of Obj files are highly suggestive of them being objects that can be
|
|
|
|
placed on [maps](map.md). 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/
|
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
`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):
|
2018-03-18 04:23:34 +00:00
|
|
|
|
|
|
|
```
|
|
|
|
# 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
|
|
|
|
```
|
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
So it seems this visual data can have quite complicated attributes. At a minimum
|
|
|
|
we see:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
* `TYPE`
|
|
|
|
* `DEF`
|
|
|
|
* `DESTROY`
|
|
|
|
* `Dmg1Lnk`
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
The `type` field **may** tell us what format each sprite is in.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
## OBJ container structure
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
`.obj` files represent visual data. They contain a number of sprites, which are
|
|
|
|
assigned attributes in `.asn` files and referenced from `.map` files.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
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.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
The file begins with a header, with all values 32-bit little-endians:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
| 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 |
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
The sprite directory contains an 8-byte record per sprite. That record contains
|
|
|
|
two 32-bit little-endians:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
| Offset | Meaning |
|
|
|
|
| ------ | ------- |
|
|
|
|
| 0x0000 | Relative offset of sprite in data block |
|
|
|
|
| 0x0004 | Size of sprite in data block |
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
For sanity checks, we can ensure that:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
* 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
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
## Sprite structure
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
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.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
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:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
|
|
|
```
|
2018-03-20 02:20:02 +00:00
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
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
|
2018-03-18 04:23:34 +00:00
|
|
|
```
|
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
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.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
I think this is rendered as a 1px dot with the colour `#ff00ff` in WH40K_TD.exe:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|

|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
In the tile, the dot is in the very centre.
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
The colour itself doesn't show up in the data directly, so it's not a simple RGB
|
2018-03-21 05:08:24 +00:00
|
|
|
array of pixels. WH40K_TD.exe is a 256-colour application, so the pixel data may
|
|
|
|
account for 1 of the 3 bytes.
|
|
|
|
|
|
|
|
All sprites seem to end with 0x00 - like a GIF image descriptor, this may say
|
|
|
|
"end of data".
|
|
|
|
|
|
|
|
So what's the 1? Simple RLE, like FLIC type 15 BYTE_RUN? - "Repeat 253 one time"?
|
2018-03-18 04:23:34 +00:00
|
|
|
|
2018-03-20 02:20:02 +00:00
|
|
|
There are 45 `TYPE 13` .obj files in the game. Comparing the above with the
|
|
|
|
start of the second sprite in `pillar.obj`:
|
2018-03-18 04:23:34 +00:00
|
|
|
|
|
|
|
```
|
2018-03-20 02:20:02 +00:00
|
|
|
----------------------------------------------
|
|
|
|
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
|
2018-03-18 04:23:34 +00:00
|
|
|
```
|
|
|
|
|
2018-03-20 12:02:07 +00:00
|
|
|
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.
|
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Line-by-line comparisons of first 16 bytes (all TYPE 13). 0x10..0x17 are all
|
|
|
|
0x00 in all examples so far.
|
|
|
|
|
|
|
|
```
|
|
|
|
a: blank.obj sprite 0, 1x1 tile (27 bytes)
|
|
|
|
b: pillar.obj sprite 1 (3340 bytes)
|
|
|
|
c: altar.obj sprite 0 (7465 bytes)
|
|
|
|
d: altar.obj sprite 1 (7368 bytes)
|
2018-03-20 12:02:07 +00:00
|
|
|
|
|
|
|
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
|
2018-03-21 05:08:24 +00:00
|
|
|
c 0x0000 d6 00 16 01 74 00 67 00 | t g | 214 0 22 1 116 0 103 0
|
|
|
|
d 0x0000 d6 00 19 01 74 00 64 00 | t d | 214 0 25 1 116 0 100 0
|
|
|
|
|
2018-03-20 12:02:07 +00:00
|
|
|
|
|
|
|
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
|
2018-03-21 05:08:24 +00:00
|
|
|
c 0x0008 00 00 00 00 11 1d 00 00 | | 0 0 0 0 17 29 0 0
|
|
|
|
d 0x0008 00 00 00 00 b0 1c 00 00 | | 0 0 0 0 176 28 0 0
|
|
|
|
```
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Assuming a 24-byte header, 0x0c matches "remaining pixeldata" size in all cases.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
0x04 makes sense as x,y dimension in `blank.obj` where we *know* it's 1x1 pixel.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
The pillar has more Y than X; the altar more X than Y, suggesting 0x04-0x05 are
|
|
|
|
X and 0x06-0x07 are Y. Measuring the rendered pixels by WH40K_TD.exe gives a
|
|
|
|
close correspondence in all cases.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
WH40K_TD.exe crashes trying to load a set referencing `pillar` in it unless it's
|
|
|
|
in the CENTER position. Interesting.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Not all pixeldatas are evenly divisible by 3. blank.obj seems to be the special
|
|
|
|
case.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
The volume represented by a cell is a little odd. We see three faces of a fake
|
|
|
|
3D volume of size 64x64x32(ish). This is presented in an isomorphic fashion, so
|
|
|
|
the height is 32px at the leftmost and rightmost extents and 96 in the centre.
|
|
|
|
Overall width is 128px, and the minimum rectangle covering the whole space would
|
|
|
|
be 128x96, or 12288 bytes at 8bpp.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Or it could be represented as the three planes separately. So for an altar, we'd
|
|
|
|
have 64x64 pixels, plus 32x64 pixels, plus 32x64 pixels (minus a few around the
|
|
|
|
edges, perhaps) - 4096 + 2048 + 2048 = 8192. The sprite is only slightly smaller
|
|
|
|
than that.
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Or perhaps we draw a normal X/Y rectangle which is then skewed appropriately.
|
|
|
|
That seems like it would be odd for pixeldata though?
|
2018-03-20 12:02:07 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
Bytes per pixel (intuited from hypothetical x,y) for the four frames so far:
|
|
|
|
|
|
|
|
```
|
|
|
|
a: 1 * 1 = 1. 27 - 24 = 3. 24 bits / pixel
|
|
|
|
b: 46 * 72 = 3312. 3340 - 24 = 3316. 8 bits / pixel with 4 bytes left over.
|
|
|
|
c: 116 * 103 = 11948. 7465 - 24 = 7439. 4.9 bits / pixel
|
|
|
|
d: 116 * 100 = 11600. 7368 - 24 = 7344. 5.1 bits / pixel
|
|
|
|
```
|
2018-03-20 12:02:07 +00:00
|
|
|
|
|
|
|
| Offset | Purpose |
|
|
|
|
| ------ | ------- |
|
|
|
|
| 0x0000 | ?
|
2018-03-21 05:08:24 +00:00
|
|
|
| 0x0004 | x,y size (16 bits each) |
|
|
|
|
| 0x0008 | ? (blank in all cases so far)
|
2018-03-20 12:02:07 +00:00
|
|
|
| 0x000c | Size of remaining pixeldata |
|
2018-03-21 05:08:24 +00:00
|
|
|
| 0x0010 | Padding? |
|
|
|
|
| 0x0014 | Padding? |
|
|
|
|
|
|
|
|
We still don't know what the first 32 bits are all about. Perhaps they can help
|
|
|
|
to explain the differences in putative bpp.
|
|
|
|
|
|
|
|
0x002-0x003 changes in step with total number of pixels?
|
|
|
|
|
|
|
|
| ID | 0x2-0x3 | dec LE | Number of pixels |
|
|
|
|
| -- | ------- | ------ | ---------------- |
|
|
|
|
| a | 61 01 | 353 | 1 |
|
|
|
|
| b | 25 01 | 293 | 3312 |
|
|
|
|
| d | 19 01 | 281 | 11600 |
|
|
|
|
| c | 16 01 | 278 | 11948 |
|
|
|
|
|
|
|
|
Not clear what it means. If anything!
|
|
|
|
|
|
|
|
WH40K_TD.exe loops around "ReadInMissionFLCs", incl. address 0x0041dd10, where
|
|
|
|
it loads in all the .asc and .obj files in a set.
|
|
|
|
|
|
|
|
```
|
|
|
|
break *0x41DD10
|
|
|
|
```
|
2018-03-19 11:42:57 +00:00
|
|
|
|
2018-03-21 05:08:24 +00:00
|
|
|
This lets me focus very narrowly on what happens when loading sprites, and
|
|
|
|
might give clues.
|
2018-03-19 11:42:57 +00:00
|
|
|
|