# `Anim/WarHammer.ani`

This turns out to simply be an [`obj`](obj.md) file.

The first 1,064 sprites are all of the same Ultramarine, carryng a bolter. There
are eight "facing" orientations:

* North
* Northeast
* East
* Southeast
* South
* Southwest
* West
* Northwest

For each orientation, an action is pictured in a variable number of frames. The
final frame for each action appears to be "stationary".

* Walk (13 frames)
* Run (9 frames)
* Crouch down (8 frames)
* Stand up (8 frames)
* Take aim (standing) (6 frames)
* Fire (standing) (6 frames)
* Relax aim (standing) (6 frames) 
* Throw grenade (standing) (18 frames)
* Take aim (crouched) (5 frames)
* Fire (crouched) (5 frames)
* Relax aim (crouched) (5 frames)
* Throw grenade (crouched) (17 frames)
* Draw melee weapon (standing) (10 frames)
* Strike down with melee weapon (standing) (8 frames)
* Stab with melee weapon (standing) (9 frames)

Added together and multiplied by 87, that's 1064.

The next sprite is a walking-north action for an ultramarine with a flamer. The
total number of frames for this character is 1120 - 56 additional frames, or 7
per orientation. Could be an extra action, or an extra frame per action.

Also notable is that while the bolter showed muzzle flash in the animation, the
flamer only showed a tiny hint of fire. I think the animation for spewing flame
is held elsewhere.

I strongly suspect the actions and the number of frames in each action are
configurable. So, what other files are implicated in its interpretation? Here's
a few possibilities:

* `Data/AniObDef.dat`
* `Data/Coordinates.dat`
* `Data/HasAction.dat`
* `Data/VehicDef.dat`
* `Data/WeapDef.dat`
- `Idx/WarHammer.idx`

## `Data/AniObDef.dat`

Including comments, this 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.


## `Idx/WarHammer.idx`

`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:

<details>

```
# 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
```

</details>

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 |
| 8      | 4    | ??? |

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` and `ani` files when placing a single librarian for
me to dig into in more detail. No other files are touched when doing this.

<details>

```
_llseek(<WarHammer.idx>, 132, [132], SEEK_SET) = 0
read(<WarHammer.idx>, "\x30\x7c\x09\x00\x98\x00\x00\x00\x88\xf8\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 132, [132], SEEK_SET) = 0
read(<WarHammer.idx>, "\x30\x7c\x09\x00\x98\x00\x00\x00\x88\xf8\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 621616, [621616], SEEK_SET) = 0
read(<WarHammer.idx>, "\x02\x01\x01\x33\x50\x83\x09\x00\x0d\x00\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 621628, [621628], SEEK_SET) = 0
read(<WarHammer.idx>, "\x02\x01\x02\x33\xb2\x83\x09\x00\x0d\x00\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 621640, [621640], SEEK_SET) = 0
read(<WarHammer.idx>, "\x02\x01\x03\x33\x14\x84\x09\x00\x0d\x00\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 621652, [621652], SEEK_SET) = 0
read(<WarHammer.idx>, "\x02\x01\x04\x33\x76\x84\x09\x00\x0d\x00\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 621664, [621664], SEEK_SET) = 0
read(<WarHammer.idx>, "\x02\x01\x05\x33\xd8\x84\x09\x00\x0d\x00\x00\x00", 12) = 12

_llseek(<WarHammer.idx>, 623832, [623832], SEEK_SET) = 0
read(<WarHammer.idx>, "\x34\x00\x40\x00\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20) = 20

_llseek(<WarHammer.idx>, 0, [623852], SEEK_CUR) = 0
_llseek(<WarHammer.idx>, 623852, [623852], SEEK_SET) = 0
_llseek(<WarHammer.idx>, 623930, [623930], SEEK_SET) = 0

_llseek(<WarHammer.ani>, 509440, [509440], SEEK_SET) = 0
read(<WarHammer.ani>, "\x0c\x4d\xb6\x09\x68\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509448, [509448], SEEK_SET) = 0
read(<WarHammer.ani>, "\x74\x5b\xb6\x09\xfe\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509456, [509456], SEEK_SET) = 0
read(<WarHammer.ani>, "\x72\x6a\xb6\x09\x67\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509464, [509464], SEEK_SET) = 0
read(<WarHammer.ani>, "\xd9\x79\xb6\x09\xa9\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509472, [509472], SEEK_SET) = 0
read(<WarHammer.ani>, "\x82\x89\xb6\x09\xbb\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509480, [509480], SEEK_SET) = 0
read(<WarHammer.ani>, "\x3d\x99\xb6\x09\x08\x10\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509488, [509488], SEEK_SET) = 0
read(<WarHammer.ani>, "\x45\xa9\xb6\x09\xd1\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509496, [509496], SEEK_SET) = 0
read(<WarHammer.ani>, "\x16\xb9\xb6\x09\x01\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509504, [509504], SEEK_SET) = 0
read(<WarHammer.ani>, "\x17\xc8\xb6\x09\xc4\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509512, [509512], SEEK_SET) = 0
read(<WarHammer.ani>, "\xdb\xd6\xb6\x09\xe3\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509520, [509520], SEEK_SET) = 0
read(<WarHammer.ani>, "\xbe\xe5\xb6\x09\x0c\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509528, [509528], SEEK_SET) = 0
read(<WarHammer.ani>, "\xca\xf4\xb6\x09\x41\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509536, [509536], SEEK_SET) = 0
read(<WarHammer.ani>, "\x0b\x04\xb7\x09\xa6\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 509440, [509440], SEEK_SET) = 0
read(<WarHammer.ani>, "\x0c\x4d\xb6\x09\x68\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164448540, [164448540], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf7\x00\x0a\x01\x35\x00\x54\x00\x00\x00\x00\x00\x50\x0e\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x13\x87\x2a\x2a\x2b\x2a\x2b"..., 3688) = 3688

_llseek(<WarHammer.ani>, 509448, [509448], SEEK_SET) = 0
read(<WarHammer.ani>, "\x74\x5b\xb6\x09\xfe\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164452228, [164452228], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf5\x00\x08\x01\x37\x00\x57\x00\x00\x00\x00\x00\xe6\x0e\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x15\x03\x2a\x8a\x2b\x2a\x2c"..., 3838) = 3838

_llseek(<WarHammer.ani>, 509456, [509456], SEEK_SET) = 0
read(<WarHammer.ani>, "\x72\x6a\xb6\x09\x67\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164456066, [164456066], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf4\x00\x08\x01\x39\x00\x5a\x00\x00\x00\x00\x00\x4f\x0f\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x18\x81\x2a\x05\x2b\x80\x1b"..., 3943) = 3943

_llseek(<WarHammer.ani>, 509464, [509464], SEEK_SET) = 0
read(<WarHammer.ani>, "\xd9\x79\xb6\x09\xa9\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164460009, [164460009], SEEK_SET) = 0
read(<WarHammer.ani>, "\xee\x00\x07\x01\x42\x00\x5b\x00\x00\x00\x00\x00\x91\x0f\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x22\x81\x2a\x80\x1f\x00\x80"..., 4009) = 4009

_llseek(<WarHammer.ani>, 509472, [509472], SEEK_SET) = 0
read(<WarHammer.ani>, "\x82\x89\xb6\x09\xbb\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164464018, [164464018], SEEK_SET) = 0
read(<WarHammer.ani>, "\xee\x00\x0a\x01\x43\x00\x5c\x00\x00\x00\x00\x00\xa3\x0f\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x23\x03\x29\x03\x2b\x80\x1a"..., 4027) = 4027

_llseek(<WarHammer.ani>, 509480, [509480], SEEK_SET) = 0
read(<WarHammer.ani>, "\x3d\x99\xb6\x09\x08\x10\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164468045, [164468045], SEEK_SET) = 0
read(<WarHammer.ani>, "\xec\x00\x09\x01\x43\x00\x5a\x00\x00\x00\x00\x00\xf0\x0f\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x24\x81\x2a\x80\x1e\x00\x80"..., 4104) = 4104

_llseek(<WarHammer.ani>, 509488, [509488], SEEK_SET) = 0
read(<WarHammer.ani>, "\x45\xa9\xb6\x09\xd1\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164472149, [164472149], SEEK_SET) = 0
read(<WarHammer.ani>, "\xee\x00\x09\x01\x3f\x00\x56\x00\x00\x00\x00\x00\xb9\x0f\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x1d\x8a\x26\x26\x48\x29\x2a"..., 4049) = 4049

_llseek(<WarHammer.ani>, 509496, [509496], SEEK_SET) = 0
read(<WarHammer.ani>, "\x16\xb9\xb6\x09\x01\x0f\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164476198, [164476198], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf6\x00\x08\x01\x35\x00\x5b\x00\x00\x00\x00\x00\xe9\x0e\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x10\x81\x2a\x04\x2b\x86\x2a"..., 3841) = 3841

_llseek(<WarHammer.ani>, 509504, [509504], SEEK_SET) = 0
read(<WarHammer.ani>, "\x17\xc8\xb6\x09\xc4\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164480039, [164480039], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf7\x00\x07\x01\x33\x00\x5b\x00\x00\x00\x00\x00\xac\x0e\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x1b\x88\x2b\x2c\x2b\x2c\x2c"..., 3780) = 3780

_llseek(<WarHammer.ani>, 509512, [509512], SEEK_SET) = 0
read(<WarHammer.ani>, "\xdb\xd6\xb6\x09\xe3\x0e\x00\x00", 8) = 8

_llseek(<WarHammer.ani>, 164483819, [164483819], SEEK_SET) = 0
read(<WarHammer.ani>, "\xf8\x00\x07\x01\x41\x00\x5f\x00\x00\x00\x00\x00\xcb\x0e\x00\x00\xd4\x71\x3b\x01\x00\x00\x00\x00\x80\x1c\x83\x2c\x2c\x2a\x80\x22"..., 3811) = 3811

_llseek(<WarHammer.ani>, 509520, [509520], SEEK_SET) = 0
read(<WarHammer.ani>, "\xbe\xe5\xb6\x09\x0c\x0f\x00\x00", 8) = 8

```

</details>

Notable is that we read from `idx` **before** we read from `ani` - so it does
seem like the former should tell us where to pull from the latter.

We still load 5 type 2 records, even though there's just a single librarian, and
8 compass points. Why 5? After looking in `idx`, we load 10 sprites from `ani`,
which is at least a multiple. Do any of the records we load from `idx` specify
sprite directory offsets that match?

## `Data/HasAction.dat`

This file seems relevant as it says whether or not particular animations exist
for the different types of character, which maps directly to what is stored in
the .ani file - and so must affect lookups thereof.

Fortunately, it's commented extensively. For each "Character Type", there are
36 different possible animations.

Here's a table representation of the data:

```
    Tac  Ass  Dev  Term Apo  Tech Chp  Lib  Cpt  CMar CLrd CChp CSrc CTrm Kbz  BTh  BL   FHnd LoC  Flm  PHr  BHr  Cult
00  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x  
01  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x  
02  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x  
03                                                                                                                   
04                                                                                                                   
05                                                                                                                   
06  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x         x    x    x              x  
07  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x  
08  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x                                       x  
09                                                                                                                   
10                                                                                                                   
11                                                                                                                   
12                                                                                                                   
13                                                                                                                   
14  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
15  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
16  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
17  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
18  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
19  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
20  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
21  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
22  x    x    x         x    x    x    x    x    x    x    x    x         x                                       x  
23  x    x    x         x    x    x    x    x    x    x    x    x         x                                          
24  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x                                     
25  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x       
26  x    x    x         x    x    x    x    x    x    x    x    x         x    x    x    x         x                 
27  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x            
28  x    x    x         x    x    x    x    x    x    x    x    x    x    x                                          
29       x                                                                     x              x                      
30                      x                                                                                            
31                           x                                                                                       
32                                                              x                             x    x                 
33  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x              x  
34  x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x    x  
35                                                                             x    x    x    x    x    x    x       
```

`WarHammer.ani` doesn't have blank sprites for the unchecked cells, so this must
surely be used to map between set-of-sprites and `AnimAction`.

I think we still need the data in `.idx` for a full picture, though. Things we
still need:

* Mapping of character type to sprite directory index in `WarHammer.ani`
* Number of frames in each AnimAction

Either of these could be hardcoded, or dynamic.
