`WarHammer.ani` turns out to be a regular `obj` file; `WarHammer.idx` is partially decoded, but I'm struggling to link it to the former in a reasonable way at the moment.
12 KiB
Anim/WarHammer.ani
This turns out to simply be an obj
file. However, some
other files are implicated in its interpretation:
Data/AniObDef.dat
Idx/WarHammer.idx
Including comments, the former is 4098 lines, giving approx. 45 lines for each
of the ~188 characters in the ani
. That doesn't seem many, and there's no
obvious correspondence between the commented-on names (SMOKE01
?) and the
viewed frames... but then, I've not viewed all the frames.
Still, I think a focus in WarHammer.idx
(1,880,078 bytes, binary, so around
10KiB per character, in theory) is more reasonable.
Here's a list of operations on the file when WH40K_TD.EXE
is instructed to
place an Ultramarine squad:
# Operation 1
_llseek(34, 12, [12], SEEK_SET) = 0
read(34, "\0\30\0\0X\4\0\0\0\0\0\0", 12) = 12
# Operation 2
_llseek(34, 6144, [6144], SEEK_SET) = 0
read(34, "\2\6\1\0 L\0\0\r\0\0\0", 12) = 12
_llseek(34, 6156, [6156], SEEK_SET) = 0
read(34, "\2\6\2\0\202L\0\0\r\0\0\0", 12) = 12
_llseek(34, 6168, [6168], SEEK_SET) = 0
read(34, "\2\6\3\0\344L\0\0\r\0\0\0", 12) = 12
_llseek(34, 6180, [6180], SEEK_SET) = 0
read(34, "\2\6\4\0FM\0\0\r\0\0\0", 12) = 12
_llseek(34, 6192, [6192], SEEK_SET) = 0
read(34, "\2\6\5\0\250M\0\0\r\0\0\0", 12) = 12
# Operation 3
_llseek(34, 19880, [19880], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
read(34, "\0\0\6\0\4\0\0\0\6\0\4\0\0\0\6\0\4\0\0\0\5\0\4\0\0\0\5\0\4\0\0\0"..., 78) = 78
# Operation 4: repeat 1
# Operation 5: repeat 2
# Operation 6: repeat 3
# Operation 7
_llseek(34, 24, [24], SEEK_SET) = 0
read(34, "0\212\1\0X\4\0\0\210&\0\0", 12) = 12
# Operation 8
_llseek(34, 100912, [100912], SEEK_SET) = 0
read(34, "\2\6\1\0P\276\1\0\r\0\0\0", 12) = 12
_llseek(34, 100924, [100924], SEEK_SET) = 0
read(34, "\2\6\2\0\262\276\1\0\r\0\0\0", 12) = 12
_llseek(34, 100936, [100936], SEEK_SET) = 0
read(34, "\2\6\3\0\24\277\1\0\r\0\0\0", 12) = 12
_llseek(34, 100948, [100948], SEEK_SET) = 0
read(34, "\2\6\4\0v\277\1\0\r\0\0\0", 12) = 12
_llseek(34, 100960, [100960], SEEK_SET) = 0
read(34, "\2\6\5\0\330\277\1\0\r\0\0\0", 12) = 12
# Operation 9
_llseek(34, 114648, [114648], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
# Operation 10
_llseek(34, 114648, [114648], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
read(34, "\0\0\6\0\4\0\0\0\6\0\4\0\0\0\6\0\4\0\0\0\5\0\4\0\0\0\5\0\4\0\0\0"..., 78) = 78
# Operation 11: repeat 7
# Operation 12: repeat 8
# Operation 13: repeat 9
# lots of repeats
So what are we doing here? What did we read? Here's another view of it:
# Operation 1: read 12 bytes from 0x0C
0000000C 00 18 00 00 58 04 00 00 00 00 00 00 ....X.......
# Operation 7: read 12 bytes from offset 0x18
00000018 30 8A 01 00 58 04 00 00 88 26 00 00 0...X....&..
# Operation 2: 5 times, read 12 bytes from offset 0x1800
00001800 02 06 01 00 20 4C 00 00 0D 00 00 00 .... L......
0000180C 02 06 02 00 82 4C 00 00 0D 00 00 00 .....L......
00001818 02 06 03 00 E4 4C 00 00 0D 00 00 00 .....L......
00001824 02 06 04 00 46 4D 00 00 0D 00 00 00 ....FM......
00001830 02 06 05 00 A8 4D 00 00 0D 00 00 00 .....M......
# Operation 3a: read 20 bytes from offset 0x4DA8
00004DA8 34 00 40 00 40 00 01 00 00 00 00 00 4.@.@.......
00004DB4 00 00 00 00 00 00 00 00 ........
# Operation 3b: read 78 more bytes
00004DBC 00 00 06 00 04 00 00 00 06 00 04 00
00004DC8 00 00 06 00 04 00 00 00 05 00 04 00
00004DD4 00 00 05 00 04 00 00 00 03 00 04 00
00004DE0 00 00 04 00 FC FF 00 00 05 00 FC FF
00004DEC 00 00 06 00 FC FF 00 00 05 00 FC FF
00004DF8 00 00 06 00 FC FF 00 00 06 00 FC FF
00004E04 00 00 00 00 00 00
# Operation 8: 5 times, read 12 bytes from offset 0x18A30
00018A30 02 06 01 00 50 BE 01 00 0D 00 00 00 ....P.......
00018A3C 02 06 02 00 B2 BE 01 00 0D 00 00 00 ............
00018A48 02 06 03 00 14 BF 01 00 0D 00 00 00 ............
00018A54 02 06 04 00 76 BF 01 00 0D 00 00 00 ....v.......
00018A60 02 06 05 00 D8 BF 01 00 0D 00 00 00 ............
# Operation 9a: read 20 bytes from offset 0x1BFD8
0001BFD8 34 00 40 00 40 00 01 00 00 00 00 00 4.@.@.......
0001BFE4 00 00 00 00 00 00 00 00 ........
# Operation 9b: read 78 more bytes
0001BFEC 00 00 06 00 04 00 00 00 06 00 04 00
0001BFF8 00 00 06 00 04 00 00 00 05 00 04 00
0001C004 00 00 05 00 04 00 00 00 03 00 04 00
0001C010 00 00 04 00 FC FF 00 00 05 00 FC FF
0001C01C 00 00 06 00 FC FF 00 00 05 00 FC FF
0001C028 00 00 06 00 FC FF 00 00 06 00 FC FF
0001C034 00 00 00 00 00 00
Operation 1 contains [18 00]
. Operation 2, at 0x1800
, contains [A8 4D]
-
and operation 3 starts at 0x4DA8
. The pattern repeats for operations
7 -> 8 -> 9.
Assumimg operations 1 & 7 represent one type of record, while 2 & 8 represent another, that would give us a 511-entry directory starting at 0xC. We know the first 4 bytes represent an offset to find the type of record represented by 2 & 8. We don't know about the other 8 bytes.
Offset | Size | Meaning |
---|---|---|
0 | 4 | Position of type 2 record(s) |
4 | ? | Unknown |
The records represented by operations 2 & 8 both read 5x 12-byte records, but it looks like they're arranged in blocks of 8. Nothing in type 1 records indicates how many type 2 records there are.
Offset | Size | Meaning |
---|---|---|
0 | 2? | 02 06 - ??? |
2 | 2? | n xx - ??? n counts from 1 to 8 |
4 | 4 | Position of type 3 record |
The two type 3a and 3b records are identical to each other, it's hard to know what's what.
None of the unknown numbers, however sliced, seem to be sprite indices for
Anim/WarHammer.ani
.
Here's a trace of both idx
(fd 34) and ani
(fd 14) files when placing a
single librarian for me to dig into in more detail.
read(14, "~\337\2\0 \0\0\0\360\373\26\0\20\374\26\0\245J\303\30\0\0\0\0\0\0\0\0(\222R\0", 32) = 32
_llseek(34, 132, [132], SEEK_SET) = 0
read(34, "0|\t\0\230\0\0\0\210\370\0\0", 12) = 12
_llseek(34, 132, [132], SEEK_SET) = 0
read(34, "0|\t\0\230\0\0\0\210\370\0\0", 12) = 12
_llseek(34, 621616, [621616], SEEK_SET) = 0
read(34, "\2\1\0013P\203\t\0\r\0\0\0", 12) = 12
_llseek(34, 621628, [621628], SEEK_SET) = 0
read(34, "\2\1\0023\262\203\t\0\r\0\0\0", 12) = 12
_llseek(34, 621640, [621640], SEEK_SET) = 0
read(34, "\2\1\0033\24\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 621652, [621652], SEEK_SET) = 0
read(34, "\2\1\0043v\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 621664, [621664], SEEK_SET) = 0
read(34, "\2\1\0053\330\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 623832, [623832], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
_llseek(14, 509440, [509440], SEEK_SET) = 0
read(14, "\fM\266\th\16\0\0", 8) = 8
_llseek(14, 509448, [509448], SEEK_SET) = 0
read(14, "t[\266\t\376\16\0\0", 8) = 8
_llseek(14, 509456, [509456], SEEK_SET) = 0
read(14, "rj\266\tg\17\0\0", 8) = 8
_llseek(14, 509464, [509464], SEEK_SET) = 0
read(14, "\331y\266\t\251\17\0\0", 8) = 8
_llseek(14, 509472, [509472], SEEK_SET) = 0
read(14, "\202\211\266\t\273\17\0\0", 8) = 8
_llseek(14, 509480, [509480], SEEK_SET) = 0
read(14, "=\231\266\t\10\20\0\0", 8) = 8
_llseek(14, 509488, [509488], SEEK_SET) = 0
read(14, "E\251\266\t\321\17\0\0", 8) = 8
_llseek(14, 509496, [509496], SEEK_SET) = 0
read(14, "\26\271\266\t\1\17\0\0", 8) = 8
_llseek(14, 509504, [509504], SEEK_SET) = 0
read(14, "\27\310\266\t\304\16\0\0", 8) = 8
_llseek(14, 509512, [509512], SEEK_SET) = 0
read(14, "\333\326\266\t\343\16\0\0", 8) = 8
_llseek(14, 509520, [509520], SEEK_SET) = 0
read(14, "\276\345\266\t\f\17\0\0", 8) = 8
_llseek(14, 509528, [509528], SEEK_SET) = 0
read(14, "\312\364\266\tA\17\0\0", 8) = 8
_llseek(14, 509536, [509536], SEEK_SET) = 0
read(14, "\v\4\267\t\246\17\0\0", 8) = 8
_llseek(14, 509440, [509440], SEEK_SET) = 0
read(14, "\fM\266\th\16\0\0", 8) = 8
_llseek(14, 164448540, [164448540], SEEK_SET) = 0
read(14, "\367\0\n\0015\0T\0\0\0\0\0P\16\0\0\324q;\1\0\0\0\0\200\23\207**+*+"..., 3688) = 3688
_llseek(14, 509448, [509448], SEEK_SET) = 0
read(14, "t[\266\t\376\16\0\0", 8) = 8
_llseek(14, 164452228, [164452228], SEEK_SET) = 0
read(14, "\365\0\10\0017\0W\0\0\0\0\0\346\16\0\0\324q;\1\0\0\0\0\200\25\3*\212+*,"..., 3838) = 3838
_llseek(14, 509456, [509456], SEEK_SET) = 0
read(14, "rj\266\tg\17\0\0", 8) = 8
_llseek(14, 164456066, [164456066], SEEK_SET) = 0
read(14, "\364\0\10\19\0Z\0\0\0\0\0O\17\0\0\324q;\1\0\0\0\0\200\30\201*\5+\200\33"..., 3943) = 3943
_llseek(14, 509464, [509464], SEEK_SET) = 0
read(14, "\331y\266\t\251\17\0\0", 8) = 8
_llseek(14, 164460009, [164460009], SEEK_SET) = 0
read(14, "\356\0\7\1B\0[\0\0\0\0\0\221\17\0\0\324q;\1\0\0\0\0\200\"\201*\200\37\0\200"..., 4009) = 4009
_llseek(14, 509472, [509472], SEEK_SET) = 0
read(14, "\202\211\266\t\273\17\0\0", 8) = 8
_llseek(14, 164464018, [164464018], SEEK_SET) = 0
read(14, "\356\0\n\1C\0\\\0\0\0\0\0\243\17\0\0\324q;\1\0\0\0\0\200#\3)\3+\200\32"..., 4027) = 4027
_llseek(14, 509480, [509480], SEEK_SET) = 0
read(14, "=\231\266\t\10\20\0\0", 8) = 8
_llseek(14, 164468045, [164468045], SEEK_SET) = 0
read(14, "\354\0\t\1C\0Z\0\0\0\0\0\360\17\0\0\324q;\1\0\0\0\0\200$\201*\200\36\0\200"..., 4104) = 4104
_llseek(14, 509488, [509488], SEEK_SET) = 0
read(14, "E\251\266\t\321\17\0\0", 8) = 8
_llseek(14, 164472149, [164472149], SEEK_SET) = 0
read(14, "\356\0\t\1?\0V\0\0\0\0\0\271\17\0\0\324q;\1\0\0\0\0\200\35\212&&H)*"..., 4049) = 4049
_llseek(14, 509496, [509496], SEEK_SET) = 0
read(14, "\26\271\266\t\1\17\0\0", 8) = 8
_llseek(14, 164476198, [164476198], SEEK_SET) = 0
read(14, "\366\0\10\0015\0[\0\0\0\0\0\351\16\0\0\324q;\1\0\0\0\0\200\20\201*\4+\206*"..., 3841) = 3841
_llseek(14, 509504, [509504], SEEK_SET) = 0
read(14, "\27\310\266\t\304\16\0\0", 8) = 8
_llseek(14, 164480039, [164480039], SEEK_SET) = 0
read(14, "\367\0\7\0013\0[\0\0\0\0\0\254\16\0\0\324q;\1\0\0\0\0\200\33\210+,+,,"..., 3780) = 3780
_llseek(14, 509512, [509512], SEEK_SET) = 0
read(14, "\333\326\266\t\343\16\0\0", 8) = 8
_llseek(14, 164483819, [164483819], SEEK_SET) = 0
read(14, "\370\0\7\1A\0_\0\0\0\0\0\313\16\0\0\324q;\1\0\0\0\0\200\34\203,,*\200\""..., 3811) = 3811
_llseek(14, 509520, [509520], SEEK_SET) = 0
read(14, "\276\345\266\t\f\17\0\0", 8) = 8
_llseek(14, 164487630, [164487630], SEEK_SET) = 0
read(14, "\370\0\7\1H\0`\0\0\0\0\0\364\16\0\0\324q;\1\0\0\0\0\200\35\201,\200*\0\200"..., 3852) = 3852
_llseek(14, 509528, [509528], SEEK_SET) = 0
read(14, "\312\364\266\tA\17\0\0", 8) = 8
_llseek(14, 164491482, [164491482], SEEK_SET) = 0
read(14, "\373\0\10\1;\0Z\0\0\0\0\0)\17\0\0\324q;\1\0\0\0\0\200\36\202+,\200\33\0"..., 3905) = 3905
_llseek(14, 509536, [509536], SEEK_SET) = 0
read(14, "\v\4\267\t\246\17\0\0", 8) = 8
_llseek(14, 164495387, [164495387], SEEK_SET) = 0
read(14, "\366\0\n\18\0Y\0\0\0\0\0\216\17\0\0\324q;\1\0\0\0\0\200\32\t+\205*\4,"..., 4006) = 4006
_llseek(34, 623832, [623832], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
read(34, "\0\0\6\0\4\0\0\0\6\0\4\0\0\0\6\0\4\0\0\0\5\0\4\0\0\0\5\0\4\0\0\0"..., 78) = 78
_llseek(34, 132, [132], SEEK_SET) = 0
read(34, "0|\t\0\230\0\0\0\210\370\0\0", 12) = 12
_llseek(34, 621616, [621616], SEEK_SET) = 0
read(34, "\2\1\0013P\203\t\0\r\0\0\0", 12) = 12
_llseek(34, 621628, [621628], SEEK_SET) = 0
read(34, "\2\1\0023\262\203\t\0\r\0\0\0", 12) = 12
_llseek(34, 621640, [621640], SEEK_SET) = 0
read(34, "\2\1\0033\24\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 621652, [621652], SEEK_SET) = 0
read(34, "\2\1\0043v\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 621664, [621664], SEEK_SET) = 0
read(34, "\2\1\0053\330\204\t\0\r\0\0\0", 12) = 12
_llseek(34, 623832, [623832], SEEK_SET) = 0
read(34, "4\0@\0@\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) = 20
Notable is that we still load 5 type 2 records, even though there's just a single librarian, and 8 compass points. Why 5?