Files
ordoor/doc/formats/obj.md

275 lines
9.4 KiB
Markdown
Raw Normal View History

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/
`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
```
So it seems this visual data can have quite complicated attributes. At a minimum
we see:
2018-03-18 04:23:34 +00:00
* `TYPE`
* `DEF`
* `DESTROY`
* `Dmg1Lnk`
2018-03-18 04:23:34 +00:00
The `type` field **may** tell us what format each sprite is in.
2018-03-18 04:23:34 +00:00
## OBJ container structure
2018-03-18 04:23:34 +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
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
The file begins with a header, with all values 32-bit little-endians:
2018-03-18 04:23:34 +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
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
| 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
For sanity checks, we can ensure that:
2018-03-18 04:23:34 +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
## Sprite structure
2018-03-18 04:23:34 +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
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
```
-------------------------------------------------------------------------------
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
```
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
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
![blank.obj as rendered by WH40K_TD.exe](img/blank.obj.png)
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
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
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
```
----------------------------------------------
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
```
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)
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
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-21 05:08:24 +00:00
Assuming a 24-byte header, 0x0c matches "remaining pixeldata" size in all cases.
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-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-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-21 05:08:24 +00:00
Not all pixeldatas are evenly divisible by 3. blank.obj seems to be the special
case.
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-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-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-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
```
| 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)
| 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