From 691368cba7d17de241ee871bf4492e504628ff15 Mon Sep 17 00:00:00 2001 From: dk Date: Mon, 29 Jun 2020 17:21:09 -0700 Subject: [PATCH] Decouple bitmuncher (#495) * adding comments to d2interface for linter * moved d2render renderer interfaces and types into d2interface * fixed most lint errors for monstats loader * decouple bitmuncher to interface --- d2common/bitmuncher.go | 103 ++++++++++++++---- d2common/d2fileformats/d2dcc/dcc_direction.go | 18 +-- .../d2dcc/dcc_direction_frame.go | 4 +- d2common/d2interface/bitmuncher.go | 18 +++ d2common/d2interface/bitstream.go | 8 ++ 5 files changed, 118 insertions(+), 33 deletions(-) create mode 100644 d2common/d2interface/bitmuncher.go create mode 100644 d2common/d2interface/bitstream.go diff --git a/d2common/bitmuncher.go b/d2common/bitmuncher.go index eff22ce3..48900745 100644 --- a/d2common/bitmuncher.go +++ b/d2common/bitmuncher.go @@ -1,66 +1,117 @@ package d2common +import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + +// BitMuncher is used for parsing files that are not byte-aligned such as the DCC files. type BitMuncher struct { data []byte - Offset int - BitsRead int + offset int + bitsRead int } -func CreateBitMuncher(data []byte, offset int) *BitMuncher { - return &BitMuncher{ - data: data, - Offset: offset, - BitsRead: 0, - } +const ( + twosComplimentNegativeOne = 4294967295 + byteLen = 8 + oneBit = 0x01 + fourBytes = byteLen * 4 +) + +// CreateBitMuncher Creates a BitMuncher +func CreateBitMuncher(data []byte, offset int) d2interface.BitMuncher { + return (&BitMuncher{}).Init(data, offset) } -func CopyBitMuncher(source *BitMuncher) *BitMuncher { - return &BitMuncher{ - source.data, - source.Offset, - 0, - } +// CopyBitMuncher Creates a copy of the source BitMuncher +func CopyBitMuncher(source d2interface.BitMuncher) d2interface.BitMuncher { + return source.Copy() } +// Init initializes the BitMuncher with data and an offset +func (v *BitMuncher) Init(data []byte, offset int) d2interface.BitMuncher { + v.data = data + v.offset = offset + v.bitsRead = 0 + + return v +} + +// Copy returns a copy of a BitMuncher +func (v BitMuncher) Copy() d2interface.BitMuncher { + v.bitsRead = 0 + return &v +} + +// Offset returns the offset of the BitMuncher +func (v *BitMuncher) Offset() int { + return v.offset +} + +// SetOffset sets the offset of the BitMuncher +func (v *BitMuncher) SetOffset(n int) { + v.offset = n +} + +// BitsRead returns the number of bits the BitMuncher has read +func (v *BitMuncher) BitsRead() int { + return v.bitsRead +} + +// SetBitsRead sets the number of bits the BitMuncher has read +func (v *BitMuncher) SetBitsRead(n int) { + v.bitsRead = n +} + +// GetBit reads a bit and returns it as uint32 func (v *BitMuncher) GetBit() uint32 { - result := uint32(v.data[v.Offset/8]>>uint(v.Offset%8)) & 0x01 - v.Offset++ - v.BitsRead++ + result := uint32(v.data[v.offset/byteLen]>>uint(v.offset%byteLen)) & oneBit + v.offset++ + v.bitsRead++ + return result } +// SkipBits skips bits, incrementing the offset and bits read func (v *BitMuncher) SkipBits(bits int) { - v.Offset += bits - v.BitsRead += bits + v.offset += bits + v.bitsRead += bits } +// GetByte reads a byte from data func (v *BitMuncher) GetByte() byte { - return byte(v.GetBits(8)) + return byte(v.GetBits(byteLen)) } +// GetInt32 reads an int32 from data func (v *BitMuncher) GetInt32() int32 { - return v.MakeSigned(v.GetBits(32), 32) + return v.MakeSigned(v.GetBits(fourBytes), fourBytes) } +// GetUInt32 reads an unsigned uint32 from data func (v *BitMuncher) GetUInt32() uint32 { - return v.GetBits(32) + return v.GetBits(fourBytes) } +// GetBits given a number of bits to read, reads that number of +// bits and retruns as a uint32 func (v *BitMuncher) GetBits(bits int) uint32 { if bits == 0 { return 0 } + result := uint32(0) for i := 0; i < bits; i++ { result |= v.GetBit() << uint(i) } + return result } +// GetSignedBits Given a number of bits, reads that many bits and returns as int func (v *BitMuncher) GetSignedBits(bits int) int { return int(v.MakeSigned(v.GetBits(bits), bits)) } +// MakeSigned converts a uint32 value into an int32 func (v *BitMuncher) MakeSigned(value uint32, bits int) int32 { if bits == 0 { return 0 @@ -73,13 +124,17 @@ func (v *BitMuncher) MakeSigned(value uint32, bits int) int32 { if (value & (1 << uint(bits-1))) == 0 { return int32(value) } - // We need to extend the signed bit out so that the negative value representation still works with the 2s compliment rule. - result := uint32(4294967295) + + // We need to extend the signed bit out so that the negative value + // representation still works with the 2s compliment rule. + result := uint32(twosComplimentNegativeOne) + for i := byte(0); i < byte(bits); i++ { if ((value >> uint(i)) & 1) == 0 { result -= uint32(1 << uint(i)) } } + // Force casting to a signed value return int32(result) } diff --git a/d2common/d2fileformats/d2dcc/dcc_direction.go b/d2common/d2fileformats/d2dcc/dcc_direction.go index 4fde58e0..07e0c8f4 100644 --- a/d2common/d2fileformats/d2dcc/dcc_direction.go +++ b/d2common/d2fileformats/d2dcc/dcc_direction.go @@ -3,6 +3,8 @@ package d2dcc import ( "log" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common" ) @@ -34,7 +36,7 @@ type DCCDirection struct { } // CreateDCCDirection creates an instance of a DCCDirection. -func CreateDCCDirection(bm *d2common.BitMuncher, file *DCC) *DCCDirection { //nolint:funlen // Can't reduce +func CreateDCCDirection(bm d2interface.BitMuncher, file *DCC) *DCCDirection { //nolint:funlen // Can't reduce var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32} result := &DCCDirection{} @@ -130,29 +132,29 @@ func CreateDCCDirection(bm *d2common.BitMuncher, file *DCC) *DCCDirection { //no result.PixelBuffer = nil // Verify that everything we expected to read was actually read (sanity check)... - if equalCellsBitstream.BitsRead != result.EqualCellsBitstreamSize { + if equalCellsBitstream.BitsRead() != result.EqualCellsBitstreamSize { log.Panic("Did not read the correct number of bits!") } - if pixelMaskBitstream.BitsRead != result.PixelMaskBitstreamSize { + if pixelMaskBitstream.BitsRead() != result.PixelMaskBitstreamSize { log.Panic("Did not read the correct number of bits!") } - if encodingTypeBitsream.BitsRead != result.EncodingTypeBitsreamSize { + if encodingTypeBitsream.BitsRead() != result.EncodingTypeBitsreamSize { log.Panic("Did not read the correct number of bits!") } - if rawPixelCodesBitstream.BitsRead != result.RawPixelCodesBitstreamSize { + if rawPixelCodesBitstream.BitsRead() != result.RawPixelCodesBitstreamSize { log.Panic("Did not read the correct number of bits!") } - bm.SkipBits(pixelCodeandDisplacement.BitsRead) + bm.SkipBits(pixelCodeandDisplacement.BitsRead()) return result } //nolint:gocognit nolint:gocyclo // Can't reduce -func (v *DCCDirection) generateFrames(pcd *d2common.BitMuncher) { +func (v *DCCDirection) generateFrames(pcd d2interface.BitMuncher) { pbIdx := 0 for _, cell := range v.Cells { @@ -255,7 +257,7 @@ func (v *DCCDirection) generateFrames(pcd *d2common.BitMuncher) { } //nolint:funlen nolint:gocognit // can't reduce -func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher) { +func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp d2interface.BitMuncher) { var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4} lastPixel := uint32(0) diff --git a/d2common/d2fileformats/d2dcc/dcc_direction_frame.go b/d2common/d2fileformats/d2dcc/dcc_direction_frame.go index d5cb69e4..63ad37e7 100644 --- a/d2common/d2fileformats/d2dcc/dcc_direction_frame.go +++ b/d2common/d2fileformats/d2dcc/dcc_direction_frame.go @@ -3,6 +3,8 @@ package d2dcc import ( "log" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common" ) @@ -24,7 +26,7 @@ type DCCDirectionFrame struct { } // CreateDCCDirectionFrame Creates a DCCDirectionFrame for a DCC. -func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection) *DCCDirectionFrame { +func CreateDCCDirectionFrame(bits d2interface.BitMuncher, direction *DCCDirection) *DCCDirectionFrame { result := &DCCDirectionFrame{} bits.GetBits(direction.Variable0Bits) // Variable0 diff --git a/d2common/d2interface/bitmuncher.go b/d2common/d2interface/bitmuncher.go new file mode 100644 index 00000000..2c668449 --- /dev/null +++ b/d2common/d2interface/bitmuncher.go @@ -0,0 +1,18 @@ +package d2interface + +type BitMuncher interface { + Init(data []byte, offset int) BitMuncher + Copy() BitMuncher + Offset() int + SetOffset(int) + BitsRead() int + SetBitsRead(int) + GetBit() uint32 + SkipBits(bits int) + GetByte() byte + GetInt32() int32 + GetUInt32() uint32 + GetBits(bits int) uint32 + GetSignedBits(bits int) int + MakeSigned(value uint32, bits int) int32 +} diff --git a/d2common/d2interface/bitstream.go b/d2common/d2interface/bitstream.go new file mode 100644 index 00000000..5076bb91 --- /dev/null +++ b/d2common/d2interface/bitstream.go @@ -0,0 +1,8 @@ +package d2interface + +type BitStream interface { + ReadBits(bitCount int) int + PeekByte() int + EnsureBits(bitCount int) bool + WasteBits(bitCount int) +}