Skip to content
This repository has been archived by the owner on Dec 20, 2021. It is now read-only.

bitmuncher: add missing methods from stream reader #1123

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
3 changes: 2 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ linters-settings:
# to possibly non-intuitive layout of struct fields (harder to read). Disable
# `fieldalignment` check here until we evaluate if it is worthwhile.
- fieldalignment
# https://github.com/golangci/golangci-lint/issues/1973
- sigchanyzer
lll:
line-length: 140
misspell:
Expand Down Expand Up @@ -68,7 +70,6 @@ linters:
- nakedret
- prealloc
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- stylecheck
Expand Down
51 changes: 47 additions & 4 deletions d2common/d2datautils/bitmuncher.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package d2datautils

import "log"

// BitMuncher is used for parsing files that are not byte-aligned such as the DCC files.
type BitMuncher struct {
data []byte
Expand All @@ -12,6 +14,7 @@ const (
byteLen = 8
oneBit = 0x01
fourBytes = byteLen * 4
twoBytes = byteLen * 2
)

// CreateBitMuncher Creates a BitMuncher
Expand Down Expand Up @@ -60,12 +63,12 @@ func (v *BitMuncher) SetBitsRead(n int) {
}

// GetBit reads a bit and returns it as uint32
func (v *BitMuncher) GetBit() uint32 {
result := uint32(v.data[v.offset/byteLen]>>uint(v.offset%byteLen)) & oneBit
func (v *BitMuncher) GetBit() bool {
result := v.data[v.offset/byteLen] >> uint(v.offset%byteLen) & oneBit
v.offset++
v.bitsRead++

return result
return result == 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about it? result will be stored in a single-byte variable (instead of 4-byte uint32)

}

// SkipBits skips bits, incrementing the offset and bits read
Expand All @@ -79,6 +82,16 @@ func (v *BitMuncher) GetByte() byte {
return byte(v.GetBits(byteLen))
}

// GetInt16 reads an int16 from data
func (v *BitMuncher) GetInt16() int16 {
return int16(v.MakeSigned(v.GetBits(twoBytes), twoBytes))
}

// GetUInt16 reads an unsigned uint16 from data
func (v *BitMuncher) GetUInt16() uint16 {
return uint16(v.GetBits(twoBytes))
}

// GetInt32 reads an int32 from data
func (v *BitMuncher) GetInt32() int32 {
return v.MakeSigned(v.GetBits(fourBytes), fourBytes)
Expand All @@ -94,11 +107,34 @@ func (v *BitMuncher) GetUInt32() uint32 {
func (v *BitMuncher) GetBits(bits int) uint32 {
if bits == 0 {
return 0
} else if bits > fourBytes {
log.Panicf("BitMuncher - GetBits: number of bits to read must be less or equal to %d", fourBytes)
}

result := uint32(0)

for i := 0; i < bits; i++ {
result |= v.GetBit() << uint(i)
bit := v.GetBit()

var value uint32

if bit {
value = 1
} else {
value = 0
}

result |= value << uint(i)
}

return result
}

// GetBytes returns byte slice of `n` length.
func (v *BitMuncher) GetBytes(n int) (result []byte) {
result = make([]byte, n)
for i := 0; i < n; i++ {
result[i] = v.GetByte()
}

return result
Expand Down Expand Up @@ -136,3 +172,10 @@ func (v *BitMuncher) MakeSigned(value uint32, bits int) int32 {
// Force casting to a signed value
return int32(result)
}

// Align aligns bit muncher to bytes
func (v *BitMuncher) Align() {
if o := v.Offset() % byteLen; o > 0 {
v.SkipBits(byteLen - o)
}
}
62 changes: 60 additions & 2 deletions d2common/d2datautils/bitmuncher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,18 @@ func TestBitmuncherReadBit(t *testing.T) {

var result byte

for i := 0; i < bitsPerByte; i++ {
for i := byte(0); i < bitsPerByte; i++ {
v := bm.GetBit()
result |= byte(v) << byte(i)

var value byte

if v {
value = 1
} else {
value = 0
}

result |= value << i
}

assert.Equal(t, result, testData[0], "result of rpeated 8 times get bit didn't return expected byte")
Expand All @@ -50,6 +59,24 @@ func TestBitmuncherGetBits(t *testing.T) {
assert.Equal(t, byte(bm.GetBits(bitsPerByte)), testData[0], "get bits didn't return expected value")
}

func TestBitMuncherGetBitsWrongNumber(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

defer func() {
if r := recover(); r == nil {
t.Fatal("wrong bytes count read, but no error reproted")
}
}()

_ = bm.GetBits(33)
}

func TestBitmuncherGetBytes(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

assert.Equal(t, bm.GetBytes(4), testData[0:4], "get bytes didn't return expected value")
}

func TestBitmuncherGetNoBits(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

Expand Down Expand Up @@ -82,6 +109,30 @@ func TestBitmuncherSkipBits(t *testing.T) {
assert.Equal(t, bm.GetByte(), testData[1], "skipping 8 bits didn't moved bit muncher's position into next byte")
}

func TestBitmuncherGetInt16(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

var testInt int16

for i := 0; i < bytesPerint16; i++ {
testInt |= int16(testData[i]) << int16(bitsPerByte*i)
}

assert.Equal(t, bm.GetInt16(), testInt, "int16 value wasn't returned properly")
}

func TestBitmuncherGetUint16(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

var testUint uint16

for i := 0; i < bytesPerint16; i++ {
testUint |= uint16(testData[i]) << uint16(bitsPerByte*i)
}

assert.Equal(t, bm.GetUInt16(), testUint, "uint16 value wasn't returned properly")
}

func TestBitmuncherGetInt32(t *testing.T) {
bm := CreateBitMuncher(testData, 0)

Expand All @@ -105,3 +156,10 @@ func TestBitmuncherGetUint32(t *testing.T) {

assert.Equal(t, bm.GetUInt32(), testUint, "uint32 value wasn't returned properly")
}

func TestBitMuncherAlign(t *testing.T) {
bm := CreateBitMuncher(testData, 0)
_ = bm.GetBits(5)
bm.Align()
assert.Equal(t, 0, bm.Offset()%8, "offset after calling Align isn't 0")
}
10 changes: 5 additions & 5 deletions d2common/d2fileformats/d2dcc/dcc_direction.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type DCCDirection struct {
// nolint:funlen // no need to reduce
func CreateDCCDirection(bm *d2datautils.BitMuncher, file *DCC) *DCCDirection {
// nolint:gomnd // constant
var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}
crazyBitTable := []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}

result := &DCCDirection{
OutSizeCoded: int(bm.GetUInt32()),
Expand Down Expand Up @@ -96,7 +96,7 @@ func CreateDCCDirection(bm *d2datautils.BitMuncher, file *DCC) *DCCDirection {
}

for paletteEntryCount, i := 0, 0; i < 256; i++ {
valid := bm.GetBit() != 0
valid := bm.GetBit()
if valid {
result.PaletteEntries[paletteEntryCount] = byte(i)
paletteEntryCount++
Expand Down Expand Up @@ -278,7 +278,7 @@ func (v *DCCDirection) generateFrames(pcd *d2datautils.BitMuncher) {

//nolint:funlen,gocognit,gocyclo // can't reduce
func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp *d2datautils.BitMuncher) {
var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
pixelMaskLookup := []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}

lastPixel := uint32(0)
maxCellX := 0
Expand Down Expand Up @@ -322,7 +322,7 @@ func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp *d2datautils.BitMunch

if cellBuffer[currentCell] != nil {
if v.EqualCellsBitstreamSize > 0 {
tmp = int(ec.GetBit())
tmp = int(ec.GetBits(1))
} else {
tmp = 0
}
Expand All @@ -348,7 +348,7 @@ func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp *d2datautils.BitMunch
encodingType := 0

if (numberOfPixelBits != 0) && (v.EncodingTypeBitsreamSize > 0) {
encodingType = int(et.GetBit())
encodingType = int(et.GetBits(1))
} else {
encodingType = 0
}
Expand Down
4 changes: 2 additions & 2 deletions d2common/d2fileformats/d2dcc/dcc_direction_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func CreateDCCDirectionFrame(bits *d2datautils.BitMuncher, direction *DCCDirecti
result.YOffset = bits.GetSignedBits(direction.YOffsetBits)
result.NumberOfOptionalBytes = int(bits.GetBits(direction.OptionalDataBits))
result.NumberOfCodedBytes = int(bits.GetBits(direction.CodedBytesBits))
result.FrameIsBottomUp = bits.GetBit() == 1
result.FrameIsBottomUp = bits.GetBit()

if result.FrameIsBottomUp {
log.Panic("Bottom up frames are not implemented.")
Expand All @@ -56,7 +56,7 @@ func CreateDCCDirectionFrame(bits *d2datautils.BitMuncher, direction *DCCDirecti

func (v *DCCDirectionFrame) recalculateCells(direction *DCCDirection) {
// nolint:gomnd // constant
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
w := 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)

if (v.Width - w) <= 1 {
v.HorizontalCellCount = 1
Expand Down
3 changes: 1 addition & 2 deletions d2common/d2fileformats/d2ds1/ds1_layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,7 @@ func (l *ds1Layers) insert(t LayerGroupType, idx int, newLayer *Layer) {
// idx=1
// newLayer=c
// existing layerGroup is [a, b]
newGroup := append((*group)[:idx], append([]*Layer{newLayer}, (*group)[idx:]...)...)
*group = newGroup
*group = append((*group)[:idx], append([]*Layer{newLayer}, (*group)[idx:]...)...)
}

func (l *ds1Layers) delete(t LayerGroupType, idx int) {
Expand Down
2 changes: 2 additions & 0 deletions d2core/d2records/set_item_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ func setItemLoader(r *RecordManager, d *d2txt.DataDictionary) error {
}

record.Properties = props
record.SetPropertiesLevel1 = bonus1
record.SetPropertiesLevel2 = bonus2

records[record.SetItemKey] = record
}
Expand Down
3 changes: 2 additions & 1 deletion d2game/d2player/party_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func NewPartyPanel(asset *d2asset.AssetManager,
}

pp.partyIndexes = partyIndexes
pp.indexes = indexes

pp.Logger = d2util.NewLogger()
pp.Logger.SetLevel(l)
Expand Down Expand Up @@ -231,7 +232,7 @@ func (pi *partyIndex) setNameTooltipText() {

// setColor sets appropriate labels' colors
func (pi *partyIndex) setColor(relations d2enum.PlayersRelationships) {
var color = d2util.Color(white)
color := d2util.Color(white)

switch relations {
case d2enum.PlayerRelationEnemy:
Expand Down