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

## `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`. The names map
very well to the descriptions I came up with when observing the sprites.

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.

## `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 a single Librarian:

<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.

So what are we doing here? What did we read? Here's what I get:

### Type 1 record

From `0x84`:

```
# xxd -s 0x84 -c 12 -e -l 12 -u orig/Idx/WarHammer.idx 
00000084: 00097C30 00000098 0000F888  0|..........
```

The first read contains `0x097C30`. The second (+5) read, at `0x097C30`,
contains `0x0984D8`. We then read 20, followed by 78, bytes, and go on to read
from the `.ani` file.

The whole start of the file looks like a directory of the same kind of records
(call them type 1). The record at offset 0 is empty, as are the last few, but
the rest have always-increasing offsets in the first and third position. The
first appears to be for a "tactical marine", or at least, it is read (similarly
to the librarian) when placing a "tactical squad". That has an offset of 
0x1800` in the first position, which gives us space for 512 of these 12-byte
records. We can say they look like:

Is there anything in here that can link us to what we're reading from the `.ani`
file? From it, we read 14 entries from the sprite directory, starting at
byte offset `0x07C600` and direntry offset 63676 (`0xF8BC`). We then load 10
sprites. The first is at byte offset `0x9CD491C`, and is 3688 bytes.

Looking at that sprite in the object viewer, it is the librarian \o/ - facing
south \o/. However, it's not the sprite we see in `WH40K_TD.exe`. That one is,
I think, number 63688 (`0xF8C8`) - 12 sprites on. Nothing matches these numbers.

However, the **first** librarian sprite is at index 63624 (`0xF888`), which
matches the value at offset 8. This, then, must be the link.

If the first sprite is 0, the displayed sprite is 64 (`0x40`)...

We still need to know how to go from "librarian" to "index 11", though. The
`CTYPE_LIBRARIAN` value in `HasAction.ani` gives librarians an 8...


| Offset | Size | Meaning |
| ------ | ---- | ------- |
| 0      | 4    | Offset of type 2 records |
| 4      | 4    | Number of type 2 records |
| 8      | 4    | First sprite in `WarHammer.ani` for this record |

### Type 2 record(s)

From `0x097C30`:

```
# xxd -s 0x00097C30 -g 1 -c 12 -l 60 -u orig/Idx/WarHammer.idx 
00097c30: 02 01 01 33 50 83 09 00 0D 00 00 00  ...3P.......
00097c3c: 02 01 02 33 B2 83 09 00 0D 00 00 00  ...3........
00097c48: 02 01 03 33 14 84 09 00 0D 00 00 00  ...3........
00097c54: 02 01 04 33 76 84 09 00 0D 00 00 00  ...3v.......
00097c60: 02 01 05 33 D8 84 09 00 0D 00 00 00  ...3........
```

Next, we read 5x 12-byte records - 60 bytes total - from that offset in the type
1 record. The address of the next read is embedded in the fifth, which is where
the reads of type 2 records stop - so we were searching for it.

In the first 12-byte record, we have a close offset: `0x098350`. So we have
1,824 bytes available in this block of type 2 records - enough for 152 of them,
which is the number specified in the second position of the type 1 header.

What is the significance of the fifth 12-byte read? Why do we move onto type 3
records when we reach it? When we place the librarian, he is **facing** south,
and that facing is the fifth one in the listing (N, NE, E, SE, S). It's all I
can come up with.

Perhaps this is the fifth facing of the first action? Looking ahead in the file,
we can see that the third byte counts from 1 to 8 and falls again, so this is a
tempting idea.

If so, since we know the librarian has 23 actions, we'd expect room for 23 * 8
type 2 records in this block. That would need 2208 bytes, and we only have 1824
- enough for 19 animations, which is quite close.

Looking at the librarian in the `ani` file, we see they have 1055 sprites in
total, but I haven't counted the actions yet.

| Offset | Size | Meaning |
| ------ | ---- | ------- |
| 0      | 2?   | ActionID? Static per each group of 8 type-2 records? |
| 2      | 1?   | Counts up from `01` to `08` in each group of 8 type-2 records? |
| 3      | 1?   | Is `0x33` for all but the last 4 groups of 8 type-2 records? |
| 4      | 4    | Position of type 3 record |
| 8      | 4?   | ??? - small values though. Count of frames? |

### Type 3 record

From `0x0984D8`:

```
# xxd -s 0x984D8 -g 1 -c 12 -l 20 -u orig/Idx/WarHammer.idx 
000984d8: 34 00 40 00 40 00 01 00 00 00 00 00  4.@.@.......
000984e4: 00 00 00 00 00 00 00 00

# xxd -s 0x984EC -g 1 -c 12 -l 78 -u orig/Idx/WarHammer.idx
000984ec: 00 00 06 00 04 00 00 00 06 00 04 00  ............
000984f8: 00 00 06 00 04 00 00 00 05 00 04 00  ............
00098504: 00 00 05 00 04 00 00 00 03 00 04 00  ............
00098510: 00 00 04 00 FC FF 00 00 05 00 FC FF  ............
0009851c: 00 00 06 00 FC FF 00 00 05 00 FC FF  ............
00098528: 00 00 06 00 FC FF 00 00 06 00 FC FF  ............
00098534: 00 00 00 00 00 00    
```

Here, in the first read, we see `34 00` and `40 00`. These are the **relative**
offsets of the frames we load. 

| Offset | Size | Meaning |
| ------ | ---- | ------- |
| 0      | 2    | First sprite in animation (relative offset) |
| 2      | 2    | Last sprite in animation (relative offset)?  |
| 4      | 2?   | Could also be last sprite in animation? |
| 6      | 2?   | ??? |
| 8      | 12?  | ??? - unset in this case |

The remaining 78-byte chunk is impenetrable so far, but we should now have the
information we need to display all the animated sequences in `WarHammer.ani`!

How do we know it needs to be 78 bytes? One option is multiplying the final
field of the type 2 record by 6. Maybe we have 6 bytes of description per frame,
or maybe it's unrelated to frames?
