From 5702d96cac7f188c80bba06adb211f9759062329 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Fri, 5 Feb 2021 14:45:22 +0100 Subject: [PATCH] ds1 encoder: fixed bug, when decoded and encoded back data wasn't the same = records' encoding methods was rewritten to use streamWriter.Pushbit --- d2common/d2datautils/stream_writer.go | 62 ++++++++++++++++++- d2common/d2fileformats/d2ds1/ds1.go | 10 +-- .../d2ds1/floor_shadow_record.go | 22 ++++--- d2common/d2fileformats/d2ds1/wall_record.go | 23 +++---- 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/d2common/d2datautils/stream_writer.go b/d2common/d2datautils/stream_writer.go index 432fcdbb..0656a658 100644 --- a/d2common/d2datautils/stream_writer.go +++ b/d2common/d2datautils/stream_writer.go @@ -4,7 +4,9 @@ import "bytes" // StreamWriter allows you to create a byte array by streaming in writes of various sizes type StreamWriter struct { - data *bytes.Buffer + data *bytes.Buffer + bitOffset int + bitCache byte } // CreateStreamWriter creates a new StreamWriter instance @@ -28,6 +30,64 @@ func (v *StreamWriter) PushBytes(b ...byte) { } } +func (v *StreamWriter) PushBit(b bool) { + if b { + v.bitCache |= (1 << v.bitOffset) + } + v.bitOffset++ + + if v.bitOffset != bitsPerByte { + return + } + + v.PushBytes(v.bitCache) + v.bitCache = 0 + v.bitOffset = 0 +} + +func (v *StreamWriter) PushBits(b byte, bits int) { + val := b + for i := 0; i < bits; i++ { + if val&1 == 1 { + v.PushBit(true) + } else { + v.PushBit(false) + } + + val >>= 1 + } +} + +func (v *StreamWriter) PushBits16(b uint16, bits int) { + val := b + for i := 0; i < bits; i++ { + if val&1 == 1 { + v.PushBit(true) + } else { + v.PushBit(false) + } + val >>= 1 + } +} + +func (v *StreamWriter) PushBits32(b uint32, bits int) { + val := b + for i := 0; i < bits; i++ { + if val&1 == 1 { + v.PushBit(true) + } else { + v.PushBit(false) + } + val >>= 1 + } +} + +func (v *StreamWriter) ForcePushBits() { + for i := 0; i < bitsPerByte-v.bitOffset; i++ { + v.PushBit(0 != 0) + } +} + // PushInt16 writes a int16 word to the stream func (v *StreamWriter) PushInt16(val int16) { v.PushUint16(uint16(val)) diff --git a/d2common/d2fileformats/d2ds1/ds1.go b/d2common/d2fileformats/d2ds1/ds1.go index 6717f3e2..f469931e 100644 --- a/d2common/d2fileformats/d2ds1/ds1.go +++ b/d2common/d2fileformats/d2ds1/ds1.go @@ -534,23 +534,23 @@ func (ds1 *DS1) Marshal() []byte { switch layerStreamType { case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4: wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1) - dw = ds1.Tiles[y][x].Walls[wallIndex].Encode() + ds1.Tiles[y][x].Walls[wallIndex].Encode(sw) 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 + sw.PushUint32(dw) case d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2: floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1) - dw = ds1.Tiles[y][x].Floors[floorIndex].Encode() + ds1.Tiles[y][x].Floors[floorIndex].Encode(sw) case d2enum.LayerStreamShadow: - dw = ds1.Tiles[y][x].Shadows[0].Encode() + ds1.Tiles[y][x].Shadows[0].Encode(sw) case d2enum.LayerStreamSubstitute: - dw = ds1.Tiles[y][x].Substitutions[0].Unknown + sw.PushUint32(ds1.Tiles[y][x].Substitutions[0].Unknown) } - sw.PushUint32(dw) } } } diff --git a/d2common/d2fileformats/d2ds1/floor_shadow_record.go b/d2common/d2fileformats/d2ds1/floor_shadow_record.go index 61da47d1..7713cc62 100644 --- a/d2common/d2fileformats/d2ds1/floor_shadow_record.go +++ b/d2common/d2fileformats/d2ds1/floor_shadow_record.go @@ -1,5 +1,9 @@ package d2ds1 +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils" +) + // FloorShadowRecord represents a floor or shadow record in a DS1 file. type FloorShadowRecord struct { Prop1 byte @@ -28,14 +32,12 @@ func (f *FloorShadowRecord) Decode(dw uint32) { f.hidden = byte((dw & 0x80000000) >> 31) //nolint:gomnd // Bitmask } -// Encode encodes floor-shadow record -func (f *FloorShadowRecord) Encode() (dw uint32) { - dw |= uint32(f.Prop1) & 0xFF //nolint:gomnd // Bitmask - dw |= (uint32(f.Sequence) & 0x3F) << 8 //nolint:gomnd // Bitmask - dw |= (uint32(f.Unknown1) & 0xFC) << 14 //nolint:gomnd // Bitmask - dw |= (uint32(f.Style) & 0x3F) << 20 //nolint:gomnd // Bitmask - dw |= (uint32(f.Unknown2) & 0x7C) << 26 //nolint:gomnd // Bitmask - dw |= (uint32(f.hidden) & 0x01) << 31 //nolint:gomnd // Bitmask - - return dw +// Encode adds Floor's bits to stream writter given +func (f *FloorShadowRecord) Encode(sw *d2datautils.StreamWriter) { + sw.PushBits32(uint32(f.Prop1), 8) + sw.PushBits32(uint32(f.Sequence), 6) + sw.PushBits32(uint32(f.Unknown1), 6) + sw.PushBits32(uint32(f.Style), 6) + sw.PushBits32(uint32(f.Unknown2), 5) + sw.PushBits32(uint32(f.hidden), 1) } diff --git a/d2common/d2fileformats/d2ds1/wall_record.go b/d2common/d2fileformats/d2ds1/wall_record.go index 4124a74a..855594ee 100644 --- a/d2common/d2fileformats/d2ds1/wall_record.go +++ b/d2common/d2fileformats/d2ds1/wall_record.go @@ -1,6 +1,9 @@ package d2ds1 -import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" +) // WallRecord represents a wall record. type WallRecord struct { @@ -21,16 +24,14 @@ func (w *WallRecord) Hidden() bool { return w.hidden > 0 } -// Encode encodes wall record -func (w *WallRecord) Encode() (dw uint32) { - dw |= uint32(w.Prop1) & 0xFF //nolint:gomnd // Bitmask - dw |= (uint32(w.Sequence) & 0x3F) << 8 //nolint:gomnd // Bitmask - dw |= (uint32(w.Unknown1) & 0xFC) << 14 //nolint:gomnd // Bitmask - dw |= (uint32(w.Style) & 0x3F) << 20 //nolint:gomnd // Bitmask - dw |= (uint32(w.Unknown2) & 0x7C) << 26 //nolint:gomnd // Bitmask - dw |= (uint32(w.hidden) & 0x01) << 31 //nolint:gomnd // Bitmask - - return dw +// Encode adds wall's record's bytes into stream writer given +func (w *WallRecord) Encode(sw *d2datautils.StreamWriter) { + sw.PushBits32(uint32(w.Prop1), 8) + sw.PushBits32(uint32(w.Sequence), 6) + sw.PushBits32(uint32(w.Unknown1), 6) + sw.PushBits32(uint32(w.Style), 6) + sw.PushBits32(uint32(w.Unknown2), 5) + sw.PushBits32(uint32(w.hidden), 1) } // Decode decodes wall record