From 9f565740669560e9b2d9d4290550dd329b107bff Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Fri, 5 Feb 2021 12:52:51 +0100 Subject: [PATCH] data encoder: ds1 --- d2common/d2fileformats/d2ds1/ds1.go | 171 +++++++++++++++++- .../d2ds1/floor_shadow_record.go | 7 +- d2common/d2fileformats/d2ds1/wall_record.go | 7 +- d2core/d2map/d2maprenderer/renderer.go | 8 +- d2core/d2map/d2maprenderer/tile_cache.go | 6 +- 5 files changed, 180 insertions(+), 19 deletions(-) diff --git a/d2common/d2fileformats/d2ds1/ds1.go b/d2common/d2fileformats/d2ds1/ds1.go index a27b8e7e..50c9a5bd 100644 --- a/d2common/d2fileformats/d2ds1/ds1.go +++ b/d2common/d2fileformats/d2ds1/ds1.go @@ -43,6 +43,10 @@ type DS1 struct { NumberOfShadowLayers int32 // ShadowNum number of shadow layer used NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths + unknown1 []byte + layerStreamTypes []d2enum.LayerStreamType + unknown2 uint32 + npcIndexes []int } // LoadDS1 loads the specified DS1 file @@ -127,7 +131,10 @@ func LoadDS1(fileData []byte) (*DS1, error) { if ds1.Version >= v9 && ds1.Version <= v13 { // Skipping two dwords because they are "meaningless"? - br.SkipBytes(8) //nolint:gomnd // We don't know what's here + ds1.unknown1, err = br.ReadBytes(8) //nolint:gomnd // We don't know what's here + if err != nil { + return nil, err + } } if ds1.Version >= v4 { @@ -146,7 +153,7 @@ func LoadDS1(fileData []byte) (*DS1, error) { } } - layerStream := ds1.setupStreamLayerTypes() + ds1.layerStreamTypes = ds1.setupStreamLayerTypes() ds1.Tiles = make([][]TileRecord, ds1.Height) @@ -160,7 +167,7 @@ func LoadDS1(fileData []byte) (*DS1, error) { } } - err = ds1.loadLayerStreams(br, layerStream) + err = ds1.loadLayerStreams(br) if err != nil { return nil, err } @@ -245,7 +252,10 @@ func (ds1 *DS1) loadSubstitutions(br *d2datautils.StreamReader) error { } if ds1.Version >= v18 { - _, _ = br.ReadUInt32() + ds1.unknown2, err = br.ReadUInt32() + if err != nil { + return err + } } numberOfSubGroups, err := br.ReadInt32() @@ -360,6 +370,8 @@ func (ds1 *DS1) loadNPCs(br *d2datautils.StreamReader) error { for idx, ds1Obj := range ds1.Objects { if ds1Obj.X == int(npcX) && ds1Obj.Y == int(npcY) { objIdx = idx + ds1.npcIndexes = append(ds1.npcIndexes, idx) + break } } @@ -418,7 +430,7 @@ func (ds1 *DS1) loadNpcPaths(br *d2datautils.StreamReader, objIdx, numPaths int) return err } -func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2enum.LayerStreamType) error { +func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader) error { var err error var dirLookup = []int32{ @@ -427,8 +439,8 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e 0x0F, 0x10, 0x11, 0x12, 0x14, } - for lIdx := range layerStream { - layerStreamType := layerStream[lIdx] + for lIdx := range ds1.layerStreamTypes { + layerStreamType := ds1.layerStreamTypes[lIdx] for y := 0; y < int(ds1.Height); y++ { for x := 0; x < int(ds1.Width); x++ { @@ -445,7 +457,7 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e ds1.Tiles[y][x].Walls[wallIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Walls[wallIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Walls[wallIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask - ds1.Tiles[y][x].Walls[wallIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask + ds1.Tiles[y][x].Walls[wallIndex].hidden = byte((dw & 0x80000000) >> 31) //nolint:gomnd // Bitmask case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2, d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4: wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1) @@ -466,14 +478,14 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e ds1.Tiles[y][x].Floors[floorIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Floors[floorIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Floors[floorIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask - ds1.Tiles[y][x].Floors[floorIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask + ds1.Tiles[y][x].Floors[floorIndex].hidden = byte((dw & 0x80000000) >> 31) //nolint:gomnd // Bitmask case d2enum.LayerStreamShadow: ds1.Tiles[y][x].Shadows[0].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Shadows[0].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Shadows[0].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Shadows[0].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask ds1.Tiles[y][x].Shadows[0].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask - ds1.Tiles[y][x].Shadows[0].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask + ds1.Tiles[y][x].Shadows[0].hidden = byte((dw & 0x80000000) >> 31) //nolint:gomnd // Bitmask case d2enum.LayerStreamSubstitute: ds1.Tiles[y][x].Substitutions[0].Unknown = dw } @@ -483,3 +495,142 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e return err } + +// Marshal encodes ds1 back to byte slice +// nolint:funlen,gocognit // no need to change +func (ds1 *DS1) Marshal() []byte { + // create stream writer + sw := d2datautils.CreateStreamWriter() + + // Step 1 - encode header + sw.PushInt32(ds1.Version) + sw.PushInt32(ds1.Width - 1) + sw.PushInt32(ds1.Height - 1) + + if ds1.Version >= v8 { + sw.PushInt32(ds1.Act - 1) + } + + if ds1.Version >= v10 { + sw.PushInt32(ds1.SubstitutionType) + } + + if ds1.Version >= v3 { + sw.PushInt32(int32(len(ds1.Files))) + + for _, i := range ds1.Files { + sw.PushBytes([]byte(i)...) + + // separator + sw.PushBytes(0) + } + } + + if ds1.Version >= v9 && ds1.Version <= v13 { + sw.PushBytes(ds1.unknown1...) + } + + if ds1.Version >= v4 { + sw.PushInt32(ds1.NumberOfWalls) + + if ds1.Version >= v16 { + sw.PushInt32(ds1.NumberOfFloors) + } + } + + // Step 2 - encode layers + for lIdx := range ds1.layerStreamTypes { + layerStreamType := ds1.layerStreamTypes[lIdx] + + for y := 0; y < int(ds1.Height); y++ { + for x := 0; x < int(ds1.Width); x++ { + dw := uint32(0) + + switch layerStreamType { + case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4: + wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1) + dw |= uint32(ds1.Tiles[y][x].Walls[wallIndex].Prop1) & 0xFF //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].Sequence) & 0x3F) << 8 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].Unknown1) & 0xFC) << 14 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].Style) & 0x3F) << 20 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].Unknown2) & 0x7C) << 26 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].hidden) & 0x01) << 31 //nolint:gomnd // Bitmask + case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2, + d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4: + wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1) + dw |= uint32(ds1.Tiles[y][x].Walls[wallIndex].Type) + dw |= (uint32(ds1.Tiles[y][x].Walls[wallIndex].Zero) & 0xFFFFFF00) << 8 //nolint:gomnd // Bitmask + + case d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2: + floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1) + dw |= uint32(ds1.Tiles[y][x].Floors[floorIndex].Prop1) & 0xFF //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Floors[floorIndex].Sequence) & 0x3F) << 8 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Floors[floorIndex].Unknown1) & 0xFC) << 14 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Floors[floorIndex].Style) & 0x3F) << 20 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Floors[floorIndex].Unknown2) & 0x7C) << 26 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Floors[floorIndex].hidden) & 0x01) << 31 //nolint:gomnd // Bitmask + case d2enum.LayerStreamShadow: + dw |= uint32(ds1.Tiles[y][x].Shadows[0].Prop1) & 0xFF //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Shadows[0].Sequence) & 0x3F) << 8 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Shadows[0].Unknown1) & 0xFC) << 14 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Shadows[0].Style) & 0x3F) << 20 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Shadows[0].Unknown2) & 0x7C) << 26 //nolint:gomnd // Bitmask + dw |= (uint32(ds1.Tiles[y][x].Shadows[0].hidden) & 0x01) << 31 //nolint:gomnd // Bitmask + case d2enum.LayerStreamSubstitute: + dw = ds1.Tiles[y][x].Substitutions[0].Unknown + } + + sw.PushUint32(dw) + } + } + } + + // Step 3 - encode objects + if !(ds1.Version < v2) { + sw.PushInt32(int32(len(ds1.Objects))) + + for _, i := range ds1.Objects { + sw.PushUint32(uint32(i.Type)) + sw.PushUint32(uint32(i.ID)) + sw.PushUint32(uint32(i.X)) + sw.PushUint32(uint32(i.Y)) + sw.PushUint32(uint32(i.Flags)) + } + } + + // Step 4 - encode substitutions + if ds1.Version >= v12 && (ds1.SubstitutionType == subType1 || ds1.SubstitutionType == subType2) { + sw.PushUint32(ds1.unknown2) + + sw.PushUint32(uint32(len(ds1.SubstitutionGroups))) + + for _, i := range ds1.SubstitutionGroups { + sw.PushInt32(i.TileX) + sw.PushInt32(i.TileY) + sw.PushInt32(i.WidthInTiles) + sw.PushInt32(i.HeightInTiles) + sw.PushInt32(i.Unknown) + } + } + + // Step 5.1 - encode npc's + sw.PushUint32(uint32(len(ds1.npcIndexes))) + + // Step 5.2 - enoce npcs' paths + for _, i := range ds1.npcIndexes { + sw.PushUint32(uint32(len(ds1.Objects[i].Paths))) + sw.PushUint32(uint32(ds1.Objects[i].X)) + sw.PushUint32(uint32(ds1.Objects[i].Y)) + + for _, j := range ds1.Objects[i].Paths { + sw.PushUint32(uint32(j.Position.X())) + sw.PushUint32(uint32(j.Position.Y())) + + if ds1.Version >= v15 { + sw.PushUint32(uint32(j.Action)) + } + } + } + + return sw.GetBytes() +} diff --git a/d2common/d2fileformats/d2ds1/floor_shadow_record.go b/d2common/d2fileformats/d2ds1/floor_shadow_record.go index 0b569457..ee2808ad 100644 --- a/d2common/d2fileformats/d2ds1/floor_shadow_record.go +++ b/d2common/d2fileformats/d2ds1/floor_shadow_record.go @@ -7,8 +7,13 @@ type FloorShadowRecord struct { Unknown1 byte Style byte Unknown2 byte - Hidden bool + hidden byte RandomIndex byte Animated bool YAdjust int } + +// Hidden returns if floor/shadow is hidden +func (f *FloorShadowRecord) Hidden() bool { + return f.hidden > 0 +} diff --git a/d2common/d2fileformats/d2ds1/wall_record.go b/d2common/d2fileformats/d2ds1/wall_record.go index 5a6571fa..4c879eef 100644 --- a/d2common/d2fileformats/d2ds1/wall_record.go +++ b/d2common/d2fileformats/d2ds1/wall_record.go @@ -11,7 +11,12 @@ type WallRecord struct { Unknown1 byte Style byte Unknown2 byte - Hidden bool + hidden byte RandomIndex byte YAdjust int } + +// Hidden returns if wall is hidden +func (w *WallRecord) Hidden() bool { + return w.hidden > 0 +} diff --git a/d2core/d2map/d2maprenderer/renderer.go b/d2core/d2map/d2maprenderer/renderer.go index 0691d582..e7fb8b67 100644 --- a/d2core/d2map/d2maprenderer/renderer.go +++ b/d2core/d2map/d2maprenderer/renderer.go @@ -359,19 +359,19 @@ func (mr *MapRenderer) renderPass4(target d2interface.Surface, startX, startY, e func (mr *MapRenderer) renderTilePass1(tile *d2mapengine.MapTile, target d2interface.Surface) { for _, wall := range tile.Components.Walls { - if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() { + if !wall.Hidden() && wall.Prop1 != 0 && wall.Type.LowerWall() { mr.renderWall(wall, mr.viewport, target) } } for _, floor := range tile.Components.Floors { - if !floor.Hidden && floor.Prop1 != 0 { + if !floor.Hidden() && floor.Prop1 != 0 { mr.renderFloor(floor, target) } } for _, shadow := range tile.Components.Shadows { - if !shadow.Hidden && shadow.Prop1 != 0 { + if !shadow.Hidden() && shadow.Prop1 != 0 { mr.renderShadow(shadow, target) } } @@ -379,7 +379,7 @@ func (mr *MapRenderer) renderTilePass1(tile *d2mapengine.MapTile, target d2inter func (mr *MapRenderer) renderTilePass2(tile *d2mapengine.MapTile, target d2interface.Surface) { for _, wall := range tile.Components.Walls { - if !wall.Hidden && wall.Type.UpperWall() { + if !wall.Hidden() && wall.Type.UpperWall() { mr.renderWall(wall, mr.viewport, target) } } diff --git a/d2core/d2map/d2maprenderer/tile_cache.go b/d2core/d2map/d2maprenderer/tile_cache.go index 56991a59..0503f732 100644 --- a/d2core/d2map/d2maprenderer/tile_cache.go +++ b/d2core/d2map/d2maprenderer/tile_cache.go @@ -34,19 +34,19 @@ func (mr *MapRenderer) generateTileCache() { tile := &tiles[idx] for i := range tile.Components.Floors { - if !tile.Components.Floors[i].Hidden && tile.Components.Floors[i].Prop1 != 0 { + if !tile.Components.Floors[i].Hidden() && tile.Components.Floors[i].Prop1 != 0 { mr.generateFloorCache(&tile.Components.Floors[i]) } } for i := range tile.Components.Shadows { - if !tile.Components.Shadows[i].Hidden && tile.Components.Shadows[i].Prop1 != 0 { + if !tile.Components.Shadows[i].Hidden() && tile.Components.Shadows[i].Prop1 != 0 { mr.generateShadowCache(&tile.Components.Shadows[i]) } } for i := range tile.Components.Walls { - if !tile.Components.Walls[i].Hidden && tile.Components.Walls[i].Prop1 != 0 { + if !tile.Components.Walls[i].Hidden() && tile.Components.Walls[i].Prop1 != 0 { mr.generateWallCache(&tile.Components.Walls[i]) } }