diff --git a/doc/formats/maps.md b/doc/formats/maps.md index 55785fe..58f7a72 100644 --- a/doc/formats/maps.md +++ b/doc/formats/maps.md @@ -392,8 +392,11 @@ well-aligned amount. Investigation has so far suggested the following: * `Cell[0]` seems related to doors and canisters. Observed: + * Nothing special: 0x38 + * ???: 0x39 * Imperial crate: 0x28 * Door: 0xB8 + * `Cell[1]` seems related to special placeables (but not triggers). Bitfield. Observed: * 0x01: Reactor * 0x20: Door or door lock? @@ -408,12 +411,12 @@ Investigation has so far suggested the following: * `Cell[7]` Object 2 (Right) Area (Sets/*.set lookup) * `Cell[6]` Object 2 (Right) Sprite + active flag * `Cell[9]` Object 3 (Center) Area (Sets/*.set lookup) -* `Cell[10]` Object 3 (Right) Sprite + active flag -* `Cell[11]` all 255? +* `Cell[10]` Object 3 (Center) Sprite + active flag +* `Cell[11]` all 255? Vehicle? * `Cell[12]` all 0? * `Cell[13]` all 0? * `Cell[14]` all 0? -* `Cell[15]` shows squad positions, MP start positions, etc, as 0x04 +* `Cell[15]` shows squad positions, MP start positions, etc, as 0x04. Bitfield? Mapping the altar in Chapter01 to the map01 set suggests it's a palette entry lookup, 0-indexed. `U` debug in WH40K_TD.exe says the cell's `Object 3-Center` @@ -515,10 +518,102 @@ Around 001841A0: mission objectives! 00184240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ``` -Since all the files are exactly the same length uncompressed, I'm going to -assume these are all a fixed number of fixed-size records when looking into it. +The size of the trailer for Chapter01 is 139,483 bytes. +Relative offsets from the start of the trailer, we have: +| Offset | Text | +| -------- | ---- | +| `0xEE` | Mania | +| `0x78A` | Dagon | +| `0xE26` | Nihasa | +| `0x14C2` | Samnu | +| `0x1b5e` | Bael | +| `0x2896` | Gigamen | +| `0x2f32` | Valefor | +| `0x35ce` | Baalberith | +| `0x3c6a` | Fenriz | +| `0x4306` | #Character | +| `0x49a2` | Apollyon | + +So there are 1692 bytes between each name (the names probably don't come at the +start of each block, but it's still a useful stride). Presumably `#Character` is +a space for one of the player characters, while the others specify an NPC placed +on the map. + +There's 56 of these records between the first and last name we see - `Ahpuch`. + +Then there are a number of other strings that seem related to triggers / events, +including lots that say `NO FILE`. The first two are 96 bytes apart; from then +on they seem to be placed variably apart from each other; I've seen 96, 256, and +352 byte offsets. + +At 0x20916 the mission objective is readable. + +At 0x2092a the mission description is readable. + +Generating another map with just 5 characters on it, things look different: + +* Trailer size is 13543 bytes +* There are only 5 names +* There are none of the trigger/event strings +* Mission title is found at 0x2b93 +* Mission briefing is found at 0x2c92 + +Since the trailer is a variable size, there must be a header that tells us how +many of each type of record to read. Peeking at the differences in `vbindiff`: + +``` +Chapter01.MAP.Trailer +0000 0000: 38 00 00 68 00 00 00 50 00 00 00 1A 00 00 00 14 8..h...P ........ +0000 0010: 00 00 00 3A 00 00 00 00 38 25 00 04 00 00 00 00 ...:.... 8%...... +0000 0020: 00 00 00 1A 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0000 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0000 0040: 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 ........ ........ +0000 0050: 00 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 ...2.... ........ + +TINYSQUAD.MAP.Trailer +0000 0000: 38 00 00 4B 00 00 00 3C 00 00 00 37 00 00 00 28 8..K...< ...7...( +0000 0010: 00 00 00 05 00 00 00 00 2B 3A 00 04 00 00 00 05 ........ +:...... +0000 0020: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0000 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0000 0040: 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 ........ ........ +0000 0050: 00 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 ...2.... ........ +``` + +here's a first guess at a header: + +| Offset | Size | Meaning | +| ------ | ---- | ------- | +| 0 | 2 | ??? - invariant `38 00` | +| 2 | 2 | ??? - varies | +| 4 | 4 | ??? - varies | +| 8 | 4 | ??? - varies | +| 12 | 4 | ??? - varies | +| 16 | 4 | Number of character records (58 vs 5) | +| 20 | 4 | ??? - invariant `00 00 00 00` | +| 24 | 2 | ??? - varies. Seems related to character/squad position? | +| 26 | 2 | ??? - invariant `00 04` | +| 28 | 4 | ??? - varies (0 vs 5) | +| 32 | 4 | Number of objective/event records (26 vs 1) | +| 36 | 36? | Padding? | + +0x4b contains the next non-null byte; is the gap between the the number of +objectives, and it, padding? Minus a bit? 0x50 is another non-null byte. Then +it's all zeroes until one byte before the first name at 0xee. + +It's hard to say where the alignment should be at this point. We need to compare +more characters with each other. We might also be able to determine framing by +looking at the *end* of the character block. + +Characters are organised into Squads somehow. + +Individual cells seem to have a flag to say "We have a character in us", but not +the number for the character themselves, so the coordinates must be in the +per-character records also. There are several candidates for this. + +Placing a single character at (64,49) causes those bytes to show up at four +offsets - 0x18 (!), 0x1F4, 0x1F8, and 0x6C8. ## Soldiers At War diff --git a/internal/maps/maps.go b/internal/maps/maps.go index c72a81f..a687d1b 100644 --- a/internal/maps/maps.go +++ b/internal/maps/maps.go @@ -48,6 +48,25 @@ type Header struct { // Need to investigate the rest of the header too } +type Trailer struct { + Unknown1 uint16 + Unknown2 uint16 + Unknown3 uint32 + Unknown4 uint32 + Unknown5 uint32 + NumCharacters uint32 + Padding1 uint32 + Unknown6 uint16 + Unknown7 uint16 + Unknown8 uint32 + NumThingies uint32 + Padding2 [36]byte +} + +type Character struct { + Unknown1 uint32 +} + func (h Header) Width() int { return int(h.MaxWidth - h.MinWidth) }