From 961a213752e8fc9f14cabfb736e981bb37db8597 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sun, 18 Mar 2018 15:39:50 +0000 Subject: [PATCH] Discover how frames are encoded, use that knowledge to (finally) get the viewport locked in --- cmd/view-map/main.go | 13 +++++++------ doc/formats/maps.md | 26 ++++++++++--------------- internal/maps/maps.go | 44 ++++++++++++++++++++++++++----------------- internal/sets/sets.go | 19 +++++++++---------- 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/cmd/view-map/main.go b/cmd/view-map/main.go index 7da9ae6..9613080 100644 --- a/cmd/view-map/main.go +++ b/cmd/view-map/main.go @@ -28,7 +28,7 @@ var ( type env struct { gameMap *maps.GameMap - set sets.MapSet + set *sets.MapSet } type runState struct { @@ -159,8 +159,8 @@ func present(win *pixelgl.Window, state *runState) { // Draw the boundary rect := pixel.R( - float64(gameMap.MinWidth)-1, float64(gameMap.MinLength)-1, - float64(gameMap.MaxWidth)+1, float64(gameMap.MaxLength)+1, + float64(gameMap.MinWidth)-0.5, float64(gameMap.MinLength)-0.5, + float64(gameMap.MaxWidth)+0.5, float64(gameMap.MaxLength)+0.5, ) imd.Color = pixel.RGB(255, 0, 0) @@ -210,9 +210,10 @@ func makeColour(cell *maps.Cell, colIdx int) pixel.RGBA { scale = mult(0.004) case 14: scale = mult(0.004) + case 15: + scale = mult(1.0) default: scale = mult(0.01) // close to maximum resolution, low-value fields will be lost - } col := scale(float64(cell.At(colIdx))) @@ -290,9 +291,9 @@ func runStep(win *pixelgl.Window, state *runState) *runState { log.Printf("%#v -> %d,%d", vec, x, y) cell := state.env.gameMap.Cells.At(x, y, state.zIdx) log.Printf( - "x=%d y=%d z=%d Object0SurfaceArea=%d Object3CenterArea=%d (%s) SquadRelated=%d", + "x=%d y=%d z=%d SurfaceTile=%d (%s) SurfaceFrame=%d SquadRelated=%d", x, y, state.zIdx, - cell.Object0SurfaceArea, cell.Object3CenterArea, state.env.set.Palette[int(cell.Object3CenterArea)], + cell.Surface.Index(), state.env.set.Palette[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/maps.md b/doc/formats/maps.md index 6e28a7c..3c8b769 100644 --- a/doc/formats/maps.md +++ b/doc/formats/maps.md @@ -287,7 +287,7 @@ Skipping the header we know about, we have data like this: It would be very neat if the per-cell data started at 0x100 and took 16 bytes per coordinate, but to get objects in my map to line up properly with cells in -WH40K_TD.exe, I've had to start parsing these rows at `0x0120` instead. Still +WH40K_TD.exe, I've had to start parsing these rows at `0x0110` instead. Still tentative! Total number of possible coordinates is 100x130x7 = 91,000 = 1,456,000 bytes. @@ -379,8 +379,8 @@ I've added a `view-map` command to explore the data graphically. Each of the 16 bytes in a cell row must have a function; comparing a known map to how it looks in WH40K_TD.exe can help me to unravel that function. -Here's a side-by-side comparison of Chapter3.MAP, investigating CellIndex=3 and -Z index = 0 +Here's an (out-of-date) side-by-side comparison of Chapter3.MAP, investigating +CellIndex=3 and Z index = 0. ![Map comparison](img/chapter_01_cell_index_3.png) @@ -400,17 +400,15 @@ Investigation has so far suggested the following: * 0x40: Animated object * `Cell[2]` hasn't been seen with a value > 0 yet * `Cell[3]` Object 0 (Surface) Area (Sets/*.set lookup) -* `Cell[4]` Unsure at present, but it varies between a narrow range of values. - I've seen 128 - 147. Broadly matches the terrain layout. -* `Cell[5]` Object 1 (Left) Area (Sets/*.set lookup) **ASSUMED**. - * It's in the right place, and there seems to be correspondence, but not as - neatly as the other 3 columns. Often off-by-1 -* `Cell[6]` Wide range of values, 0 - 161 observed. Seems to have identity with - some blood splatters, etc +* `Cell[4]` Object 0 (Surface) Sprite + ??? + * Bottom bits encode the sprite (frame number in the .obj file) + * 0x80 is set too. A flag? +* `Cell[5]` Object 1 (Left) Area (Sets/*.set lookup) +* `Cell[6]` Object 1 (Surface) Sprite + ??? * `Cell[7]` Object 2 (Right) Area (Sets/*.set lookup) -* `Cell[8]` Wide range of values, 0 - 159 observed. +* `Cell[6]` Object 2 (Right) Sprite + ??? * `Cell[9]` Object 3 (Center) Area (Sets/*.set lookup) -* `Cell[10]` Varies from 0 - 248. Unclear what for, broadly follows terrain +* `Cell[10]` Object 3 (Right) Sprite + ??? * `Cell[11]` all 255? * `Cell[12]` all 0? * `Cell[13]` all 0? @@ -435,10 +433,6 @@ So `CellIdx == 9` points to the center object's Area, looked up in the set file! ![Pinning down cell index 9](img/chapter01_cell_index_9.png -I still see weird artifacts on middle Z-layers making me think I'm off the -stride or perhaps there's a data block partway through or something. Or maybe -the data's just born that way. - ## Trailer Assuming the theory above is correct, we have trailer data starting at diff --git a/internal/maps/maps.go b/internal/maps/maps.go index ff9f5d7..d788f53 100644 --- a/internal/maps/maps.go +++ b/internal/maps/maps.go @@ -26,7 +26,7 @@ const ( CellSize = 16 // seems to be - cellDataOffset = 0x120 // tentatively + cellDataOffset = 0x110 // tentatively cellCount = MaxHeight * MaxLength * MaxWidth ) @@ -68,18 +68,28 @@ func (h Header) MapSetFilename() string { return string(h.SetName[0:idx:idx]) + ".set" } +type ObjRef struct { + AreaByte byte + FrameAndUnknownByte byte +} + +// The index into a set palette to retrieve the object +func (o ObjRef) Index() int { + return int(o.AreaByte) +} + +func (o ObjRef) Frame() int { + return int(o.FrameAndUnknownByte - 0x80) +} + type Cell struct { DoorAndCanisterRelated byte DoorLockAndReactorRelated byte Unknown2 byte - Object0SurfaceArea byte - Unknown4 byte - Object1LeftArea byte - Unknown6 byte - Object2RightArea byte - Unknown8 byte - Object3CenterArea byte - Unknown10 byte + Surface ObjRef + Left ObjRef + Right ObjRef + Center ObjRef Unknown11 byte Unknown12 byte Unknown13 byte @@ -96,21 +106,21 @@ func (c *Cell) At(n int) byte { case 2: return c.Unknown2 case 3: - return c.Object0SurfaceArea + return c.Surface.AreaByte case 4: - return c.Unknown4 + return c.Surface.FrameAndUnknownByte case 5: - return c.Object1LeftArea + return c.Left.AreaByte case 6: - return c.Unknown6 + return c.Left.FrameAndUnknownByte case 7: - return c.Object2RightArea + return c.Right.AreaByte case 8: - return c.Unknown8 + return c.Right.FrameAndUnknownByte case 9: - return c.Object3CenterArea + return c.Center.AreaByte case 10: - return c.Unknown10 + return c.Center.FrameAndUnknownByte case 11: return c.Unknown11 case 12: diff --git a/internal/sets/sets.go b/internal/sets/sets.go index a64924e..c785996 100644 --- a/internal/sets/sets.go +++ b/internal/sets/sets.go @@ -19,13 +19,13 @@ type MapSet struct { Palette []string } -func LoadSets(dir string) (map[string]MapSet, error) { +func LoadSets(dir string) (map[string]*MapSet, error) { fis, err := ioutil.ReadDir(dir) if err != nil { return nil, err } - out := make(map[string]MapSet, len(fis)) + out := make(map[string]*MapSet, len(fis)) for _, fi := range fis { filename := filepath.Join(dir, fi.Name()) @@ -48,35 +48,34 @@ func LoadSets(dir string) (map[string]MapSet, error) { return out, nil } -func LoadSet(filename string) (MapSet, error) { - var out MapSet - var err error +func LoadSet(filename string) (*MapSet, error) { + out := &MapSet{} s, err := asciiscan.New(filename) if err != nil { - return out, err + return nil, err } defer s.Close() out.Description, err = s.ConsumeString() if err != nil { - return out, err + return nil, err } out.Defs, err = consumeDefs(s) if err != nil { - return out, err + return nil, err } for { str, err := s.ConsumeString() if err != nil { if err == io.EOF { - err = nil + return out, nil } - return out, err + return nil, err } out.Palette = append(out.Palette, str)