diff --git a/doc/formats/maps.md b/doc/formats/maps.md index 58f7a72..2c6046c 100644 --- a/doc/formats/maps.md +++ b/doc/formats/maps.md @@ -518,8 +518,6 @@ Around 001841A0: mission objectives! 00184240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ``` -The size of the trailer for Chapter01 is 139,483 bytes. - Relative offsets from the start of the trailer, we have: | Offset | Text | @@ -581,30 +579,38 @@ TINYSQUAD.MAP.Trailer 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: +The size of the trailer for Chapter01 is 139,483 bytes, assuming it starts at +`0x163890`. However, things may be a lot more sensible if we drop 3 bytes off +the start of that to get the fields into little-endian alignment. Have I made a +maths error above somewhere? Is it some sort of alignment thing? Do those 3 +bytes actually have meaning? + +Ignoring them for now, 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` | +| 0 | 4 | Map maximum X + 1 | +| 4 | 4 | Map maximum Y + 1 | +| 8 | 4 | Map minimum X | +| 12 | 4 | Map minimum Y | +| 16 | 4 | Number of character records | +| 20 | 4 | Padding? - 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? | +| 32 | 4 | Number of thingies (26 vs 1) | +| 36 | 20 | Padding? | + +56 bytes of data is interesting because the value of that first, ignored byte is +0x38 - perhaps it's a skip value + 2 bytes of padding? It's just weird. Keep +ignoring it for now. 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 +thingies, 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. +more characters with each other. Other notes... Characters are organised into Squads somehow. @@ -615,6 +621,18 @@ 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. +Generating a map with no characters at all, the trailer is 2,447 bytes, and the +mission title starts at 0x3B (59). So we can say we have 20 bytes of padding as +a first approximation? + +The "trailer trailer", for want of a better term, seems to be organised as: + +| Offset | Size | Meaning | +| ----- | ---- | ------- | +| 0 | 255 | Title | +| 255 | 2048 | Briefing | +| 2304 | 85 | ??? - each byte is 1 or 0. Spaced so it may be partly uint32 | + ## Soldiers At War @@ -695,3 +713,11 @@ xxd -s 0xc0 -c 13 -l 260 -g 13 BIGGESTMAP.MAP 000000e7: 00 00 85 01 00 00 00 00 00 ff 00 00 1f ............. # ... ``` + +This can be interpreted more or less the same way as the Chaos Gate maps now, +and the `soldiers-at-war` branch contains a hacked-up implementation that kind +of works \o/. + +Does the same trailer apply? + + diff --git a/internal/maps/maps.go b/internal/maps/maps.go index a687d1b..253b321 100644 --- a/internal/maps/maps.go +++ b/internal/maps/maps.go @@ -48,25 +48,40 @@ 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 +type TrailerHeader struct { + Discard1 [3]byte // No idea what this lot is + MaxWidth uint32 + MaxLength uint32 + MinWidth uint32 + MinLength uint32 + NumCharacters uint32 - Padding1 uint32 - Unknown6 uint16 - Unknown7 uint16 - Unknown8 uint32 + + Unknown1 uint32 + Unknown2 uint16 + Unknown3 uint16 + Unknown4 uint32 NumThingies uint32 - Padding2 [36]byte + Padding1 [20]byte +} + +type TrailerTrailer struct { + Title [255]byte + Briefing [2048]byte + Unknown1 [85]uint8 // Maybe? each contains either 0 or 1? Hard to say } type Character struct { Unknown1 uint32 } +type Characters []Character + +// TODO. These are triggers/reactors/etc. +type Thingy struct {} + +type Thingies []Thingy + func (h Header) Width() int { return int(h.MaxWidth - h.MinWidth) } @@ -189,7 +204,14 @@ func (h Header) Check() []error { type GameMap struct { Header Cells - // TODO: parse this into sections + TrailerHeader + Characters + Thingies + TrailerTrailer + + // TODO: parse this into sections. This is the text that comes from the + // .TXT file. Maybe we don't need it at all since it should be duplicated in + // the trailer. Text string } @@ -305,5 +327,37 @@ func loadMapFile(filename string) (*GameMap, error) { return nil, fmt.Errorf("Error parsing cells for %s: %v", filename, err) } + if err := binary.Read(zr, binary.LittleEndian, &out.TrailerHeader); err != nil { + return nil, fmt.Errorf("Error parsing trailer header for %s: %v", filename, err) + } + + log.Printf("Trailer Header: %#+v", out.TrailerHeader) + /* + // TODO: until we know how large each character record should be, we can't read this lot + + out.Characters = make(Characters, int(out.TrailerHeader.NumCharacters)) + if err := binary.Read(zr, binary.LittleEndian, &out.Characters); err != nil { + return nil, fmt.Errorf("Error parsing characters for %s: %v", filename, err) + } + + out.Thingies = make(Thingies, int(out.TrailerHeader.NumThingies)) + if err := binary.Read(zr, binary.LittleEndian, &out.Thingies); err != nil { + return nil, fmt.Errorf("Error parsing thingies for %s: %v", filename, err) + } + + if err := binary.Read(zr, binary.LittleEndian, &out.TrailerTrailer); err != nil { + return nil, fmt.Errorf("Error parsing trailer trailer for %s: %v", filename, err) + } + log.Printf("Trailer Trailer: %s", out.TrailerTrailer.String()) +*/ return &out, nil } + +func (t *TrailerTrailer) String() string { + return fmt.Sprintf( + "title=%q briefing=%q rest=%#+v", + strings.TrimRight(string(t.Title[:]), "\x00"), + strings.TrimRight(string(t.Briefing[:]), "\x00"), + t.Unknown1, + ) +}