From 738abfc4a8120c914b9fb66df3fbb01fbac303c5 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sun, 18 Mar 2018 17:27:32 +0000 Subject: [PATCH] Finish the Set/ implementation --- cmd/loader/main.go | 11 +++--- cmd/view-map/main.go | 2 +- doc/formats/index.md | 6 ++-- doc/formats/sets.md | 36 ++++++++++---------- internal/sets/sets.go | 78 ++++++++++++++++++++++++++++--------------- 5 files changed, 78 insertions(+), 55 deletions(-) diff --git a/cmd/loader/main.go b/cmd/loader/main.go index 41e7a49..2c9f062 100644 --- a/cmd/loader/main.go +++ b/cmd/loader/main.go @@ -122,12 +122,9 @@ func loadSets() { } for key, mapSet := range mapSets { - fmt.Printf(" * `%s`: Defs=%#v len(palette)=%d\n", key, mapSet.Defs, len(mapSet.Palette)) - if key == "map01.set" { - for i, objName := range mapSet.Palette { - fmt.Printf(" %d. %s\n", i, objName) - } - } - + fmt.Printf(" * `%s`: surface expected=%d actual=%d\n", key, mapSet.SurfaceCount, len(mapSet.SurfacePalette)) + fmt.Printf(" * `%s`: left expected=%d actual=%d\n", key, mapSet.LeftCount, len(mapSet.LeftPalette)) + fmt.Printf(" * `%s`: right expected=%d actual=%d\n", key, mapSet.RightCount, len(mapSet.RightPalette)) + fmt.Printf(" * `%s`: center expected=%d actual=%d\n", key, mapSet.CenterCount, len(mapSet.CenterPalette)) } } diff --git a/cmd/view-map/main.go b/cmd/view-map/main.go index 9613080..4d13508 100644 --- a/cmd/view-map/main.go +++ b/cmd/view-map/main.go @@ -293,7 +293,7 @@ func runStep(win *pixelgl.Window, state *runState) *runState { log.Printf( "x=%d y=%d z=%d SurfaceTile=%d (%s) SurfaceFrame=%d SquadRelated=%d", x, y, state.zIdx, - cell.Surface.Index(), state.env.set.Palette[int(cell.Surface.Index())], cell.Surface.Frame(), + cell.Surface.Index(), state.env.set.SurfacePalette[int(cell.Surface.Index())], cell.Surface.Frame(), cell.SquadRelated, ) log.Printf("CellIdx%d=%d. Full cell data: %#v", state.cellIdx, cell.At(state.cellIdx), cell) diff --git a/doc/formats/index.md b/doc/formats/index.md index 37a14c6..540e273 100644 --- a/doc/formats/index.md +++ b/doc/formats/index.md @@ -81,7 +81,7 @@ remake. * `*.TXT` * [`Obj/`](obj.md) * `*.obj` # must be visual data of some sort, one per placeable map object? -* `Pic/` +* [✓] `Pic/` * `*.pcx` # Standard .pcx format * `RandomMaps/` * `*.chk` # multiplayer. worry about these another day @@ -89,7 +89,7 @@ remake. * `Save_G/` * `*.sav` # savedata, gzip-compressed, custom format * `*.txt` # Seems to be a copy of one of Maps/*.txt -* [`Sets/`](sets.md) +* [✓] [`Sets/`](sets.md) * `Data.chk` # checksums? Mentions all the .set files * `*.set` # plain text, related to maps. Editor has a concept of map sets, which these must be * [✓] `SMK/` @@ -119,4 +119,4 @@ Phew. * [`Maps/*.txt`](maps.md#associated-txt-file) * [`Sets/*.set`](sets.md) * [`Sounds/wh40k.ds`](sounds.md) - * `Wav/*.wav` \ No newline at end of file + * `Wav/*.wav` diff --git a/doc/formats/sets.md b/doc/formats/sets.md index 906fbee..2b4f199 100644 --- a/doc/formats/sets.md +++ b/doc/formats/sets.md @@ -6,11 +6,11 @@ The map file loads a set, and can then reference objects by a small number. Since a maximally-filled map file seems to be able to reference 91,000 * 4 objects, this is a necessary optimization for 1998-era hardware. +Complete parser implementation [here](../../internal/sets.md). + ## Structure -These files are plain-text. - -We handily have a `template.set`, which looks like: +These files are plain-text. A `template.set` is handily included: ``` set template @@ -45,25 +45,26 @@ blank The files are of varying lengths. `template.set` is 220 lines, `map10.set` only 83. -So it's a line-based format that goes: +Whitespace and comments (`#`) are ignored. We have the following lines: -* Set description -* Blank line (optional, missing in `GEN_JUN.set`, `GEN_WAS.set`, others) -* `Defs` -* Blank line -* 4 space-separated numbers, variable between sets -* At least one blank line, sometimes 2 -* A list of object names, sometimes with # comments on the right hand side +* Set description (informational only) +* `Defs` literal +* 4 space-separated numbers - count of entries in each of four subsections: + * Surface + * Left + * Right + * Center +* A list of sum(counts) .asn/.obj filename prefixes, sometimes with comments * They all seem to end with a comment of some sort, e.g. `# meaningless comment` -Questions: +The groups in the `Defs` section are equivalent to the four types of object +referenced in each map cell. -What are the `Defs` for? Is it `Defaults` or `Definitions`? The values are -quite variable between files. +The `.MAP` files reference these tiles as palette entries. Each cell in the map +has an object index and frame number. I *suspect* the object index is relative +to the correct section of the palette. TODO: check this. -Is whitespace significant in the list of objects? First assumption is no. - -Is it a simple 0-indexed palette or do maps embed an absolute line number? +## Remaining questions Do positions in the palette have special meaning? e.g. is a particular range always reserved for walls? @@ -74,4 +75,3 @@ Are there any special values that don't appear as files in the `Obj/` directory? Once the map format is fleshed out a little more, can investigate by creating a map with a single object from the set in it and seeing what line that works out to be. - diff --git a/internal/sets/sets.go b/internal/sets/sets.go index c785996..cf9d4a4 100644 --- a/internal/sets/sets.go +++ b/internal/sets/sets.go @@ -2,7 +2,6 @@ package sets import ( "fmt" - "io" "io/ioutil" "path/filepath" "strconv" @@ -13,10 +12,17 @@ import ( type MapSet struct { Description string - Defs [4]byte // TODO: work out what these are for + + SurfaceCount int + LeftCount int + RightCount int + CenterCount int // TODO: is there any more structure than this? Should I preserve empty lines? - Palette []string + SurfacePalette []string + LeftPalette []string + RightPalette []string + CenterPalette []string } func LoadSets(dir string) (map[string]*MapSet, error) { @@ -63,57 +69,77 @@ func LoadSet(filename string) (*MapSet, error) { return nil, err } - out.Defs, err = consumeDefs(s) - if err != nil { + if err := consumeDefs(s, out); err != nil { return nil, err } - for { - str, err := s.ConsumeString() - if err != nil { - if err == io.EOF { - return out, nil - } - - return nil, err - } - - out.Palette = append(out.Palette, str) + if err := consumePalette(s, out.SurfaceCount, &out.SurfacePalette); err != nil { + return nil, fmt.Errorf("Failed to read surface palette: %v", err) + } + if err := consumePalette(s, out.LeftCount, &out.LeftPalette); err != nil { + return nil, fmt.Errorf("Failed to read left palette: %v", err) + } + if err := consumePalette(s, out.RightCount, &out.RightPalette); err != nil { + return nil, fmt.Errorf("Failed to read right palette: %v", err) + } + if err := consumePalette(s, out.CenterCount, &out.CenterPalette); err != nil { + return nil, fmt.Errorf("Failed to read center palette: %v", err) } return out, nil } -func consumeDefs(scanner *asciiscan.Scanner) ([4]byte, error) { - var out [4]byte - +func consumeDefs(scanner *asciiscan.Scanner, in *MapSet) error { expectDefs, err := scanner.ConsumeString() if err != nil { - return out, err + return err } if expectDefs != "Defs" { - return out, fmt.Errorf("Couldn't find Defs section") + return fmt.Errorf("Couldn't find Defs section") } defs, err := scanner.ConsumeString() if err != nil { - return out, err + return err } parts := strings.SplitN(defs, " ", -1) if len(parts) != 4 { - return out, fmt.Errorf("Defs section did not have 4 components") + return fmt.Errorf("Defs section did not have 4 components") } + ints := make([]int, 4) + for i, part := range parts { n, err := strconv.ParseInt(part, 10, 8) if err != nil { - return out, err + return err } - out[i] = byte(n) // safe as we specify 8 bits to ParseInt + ints[i] = int(n) // safe as we specify 8 bits to ParseInt } - return out, nil + in.SurfaceCount = ints[0] + in.LeftCount = ints[1] + in.RightCount = ints[2] + in.CenterCount = ints[3] + + return nil +} + +func consumePalette(scanner *asciiscan.Scanner, n int, in *[]string) error { + out := make([]string, 0, n) + + for i := 0; i < n; i++ { + str, err := scanner.ConsumeString() + if err != nil { + return err // EOF is bad: the number of entries is explicit + } + + out = append(out, str) + } + + *in = out + return nil }