Decode WarHammer.ani
This commit is contained in:
@@ -61,6 +61,68 @@ 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
|
obvious correspondence between the commented-on names (`SMOKE01`?) and the
|
||||||
viewed frames... but then, I've not viewed all the frames.
|
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`
|
## `Idx/WarHammer.idx`
|
||||||
|
|
||||||
@@ -68,163 +130,7 @@ viewed frames... but then, I've not viewed all the frames.
|
|||||||
theory) is more reasonable.
|
theory) is more reasonable.
|
||||||
|
|
||||||
Here's a list of operations on the file when `WH40K_TD.EXE` is instructed to
|
Here's a list of operations on the file when `WH40K_TD.EXE` is instructed to
|
||||||
place an Ultramarine squad:
|
place a single Librarian:
|
||||||
|
|
||||||
<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>
|
<details>
|
||||||
|
|
||||||
@@ -366,69 +272,123 @@ read(<WarHammer.ani>, "\xbe\xe5\xb6\x09\x0c\x0f\x00\x00", 8) = 8
|
|||||||
Notable is that we read from `idx` **before** we read from `ani` - so it does
|
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.
|
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
|
So what are we doing here? What did we read? Here's what I get:
|
||||||
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`
|
### Type 1 record
|
||||||
|
|
||||||
This file seems relevant as it says whether or not particular animations exist
|
From `0x84`:
|
||||||
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
|
# xxd -s 0x84 -c 12 -e -l 12 -u orig/Idx/WarHammer.idx
|
||||||
00 x x x x x x x x x x x x x x x x x x x x x x x
|
00000084: 00097C30 00000098 0000F888 0|..........
|
||||||
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
|
The first read contains `0x097C30`. The second (+5) read, at `0x097C30`,
|
||||||
surely be used to map between set-of-sprites and `AnimAction`.
|
contains `0x0984D8`. We then read 20, followed by 78, bytes, and go on to read
|
||||||
|
from the `.ani` file.
|
||||||
|
|
||||||
I think we still need the data in `.idx` for a full picture, though. Things we
|
The whole start of the file looks like a directory of the same kind of records
|
||||||
still need:
|
(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:
|
||||||
|
|
||||||
* Mapping of character type to sprite directory index in `WarHammer.ani`
|
Is there anything in here that can link us to what we're reading from the `.ani`
|
||||||
* Number of frames in each AnimAction
|
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.
|
||||||
|
|
||||||
Either of these could be hardcoded, or dynamic.
|
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`)...
|
||||||
|
|
||||||
|
|
||||||
|
| Offset | Size | Meaning |
|
||||||
|
| ------ | ---- | ------- |
|
||||||
|
| 0 | 4 | Offset of type 2 record(s) |
|
||||||
|
| 4 | 4 | Unknown |
|
||||||
|
| 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.
|
||||||
|
|
||||||
|
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? | 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`!
|
||||||
|
@@ -19,7 +19,7 @@ module Obj
|
|||||||
|
|
||||||
def self.parse(data)
|
def self.parse(data)
|
||||||
hdr = new(*data[0..SIZE - 1].unpack("VVVVV"))
|
hdr = new(*data[0..SIZE - 1].unpack("VVVVV"))
|
||||||
pp hdr
|
# pp hdr
|
||||||
hdr.validate!(data.bytes.size)
|
hdr.validate!(data.bytes.size)
|
||||||
hdr
|
hdr
|
||||||
end
|
end
|
||||||
@@ -96,7 +96,7 @@ module Obj
|
|||||||
DirEntry.parse(rel_data.byteslice(rel_offset, DirEntry::SIZE))
|
DirEntry.parse(rel_data.byteslice(rel_offset, DirEntry::SIZE))
|
||||||
end
|
end
|
||||||
|
|
||||||
pp entries
|
# pp entries
|
||||||
|
|
||||||
new(entries)
|
new(entries)
|
||||||
end
|
end
|
||||||
@@ -380,6 +380,18 @@ def correlate(filenames)
|
|||||||
pp results
|
pp results
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def directory(filename, num)
|
||||||
|
data = File.read(filename).force_encoding("BINARY")
|
||||||
|
|
||||||
|
hdr = Obj::Header.parse(data)
|
||||||
|
dir = Obj::SpriteDir.parse(data[hdr.dir_range])
|
||||||
|
entry = dir.entries[num]
|
||||||
|
|
||||||
|
puts "Sprite directory starts at 0x#{hdr.dir_offset.to_s(16)}"
|
||||||
|
puts "Directory entry for sprite #{num} is at 0x#{(hdr.dir_offset + (Obj::DirEntry::SIZE * num)).to_s(16)}"
|
||||||
|
puts "Sprite #{num} is at 0x#{(hdr.data_offset + entry.rel_offset).to_s(16)} and is #{entry.sprite_size} bytes"
|
||||||
|
end
|
||||||
|
|
||||||
def sprites(filename)
|
def sprites(filename)
|
||||||
obj = load_obj(filename)
|
obj = load_obj(filename)
|
||||||
|
|
||||||
@@ -514,6 +526,8 @@ def unknown16(filenames)
|
|||||||
end
|
end
|
||||||
|
|
||||||
case command = ARGV.shift
|
case command = ARGV.shift
|
||||||
|
when "directory" then
|
||||||
|
directory(ARGV[0], ARGV[1].to_i)
|
||||||
when "unknown16" then
|
when "unknown16" then
|
||||||
unknown16(ARGV)
|
unknown16(ARGV)
|
||||||
when "sprites" then
|
when "sprites" then
|
||||||
|
Reference in New Issue
Block a user