diff --git a/d2common/d2data/d2compression/huffman.go b/d2common/d2data/d2compression/huffman.go index db845bc6..ecbcd839 100644 --- a/d2common/d2data/d2compression/huffman.go +++ b/d2common/d2data/d2compression/huffman.go @@ -401,10 +401,10 @@ Loop: case 257: newvalue := bitstream.ReadBits(8) - outputstream.PushByte(byte(newvalue)) + outputstream.PushBytes(byte(newvalue)) tail = insertNode(tail, newvalue) default: - outputstream.PushByte(byte(decoded)) + outputstream.PushBytes(byte(decoded)) } } diff --git a/d2common/d2datautils/stream_writer.go b/d2common/d2datautils/stream_writer.go index 849cbdb2..432fcdbb 100644 --- a/d2common/d2datautils/stream_writer.go +++ b/d2common/d2datautils/stream_writer.go @@ -21,9 +21,11 @@ func (v *StreamWriter) GetBytes() []byte { return v.data.Bytes() } -// PushByte writes a byte to the stream -func (v *StreamWriter) PushByte(val byte) { - v.data.WriteByte(val) +// PushBytes writes a bytes to the stream +func (v *StreamWriter) PushBytes(b ...byte) { + for _, i := range b { + v.data.WriteByte(i) + } } // PushInt16 writes a int16 word to the stream diff --git a/d2common/d2enum/composite_type_string.go b/d2common/d2enum/composite_type_string.go index e9b1a46e..5400d78a 100644 --- a/d2common/d2enum/composite_type_string.go +++ b/d2common/d2enum/composite_type_string.go @@ -37,3 +37,7 @@ func (i CompositeType) String() string { } return _CompositeType_name[_CompositeType_index[i]:_CompositeType_index[i+1]] } + +func (i CompositeType) Int() int { + return int(i) +} diff --git a/d2common/d2fileformats/d2cof/cof.go b/d2common/d2fileformats/d2cof/cof.go index 685a2cd3..53b23815 100644 --- a/d2common/d2fileformats/d2cof/cof.go +++ b/d2common/d2fileformats/d2cof/cof.go @@ -35,6 +35,10 @@ const ( // COF is a structure that represents a COF file. type COF struct { + // unknown bytes for header + unknownHeaderBytes []byte + // unknown bytes (first "body's" bytes) + unknown1 []byte NumberOfDirections int FramesPerDirection int NumberOfLayers int @@ -46,6 +50,7 @@ type COF struct { } // Load loads a COF file. +// nolint:funlen // no need to change func Load(fileData []byte) (*COF, error) { result := &COF{} streamReader := d2datautils.CreateStreamReader(fileData) @@ -62,9 +67,19 @@ func Load(fileData []byte) (*COF, error) { result.NumberOfLayers = int(b[headerNumLayers]) result.FramesPerDirection = int(b[headerFramesPerDir]) result.NumberOfDirections = int(b[headerNumDirs]) + result.unknownHeaderBytes = b[headerNumDirs+1 : headerSpeed] result.Speed = int(b[headerSpeed]) - streamReader.SkipBytes(3) //nolint:gomnd // Unknown data + // read unknown bytes + // previous streamReader.SkipBytes(3) + for i := 0; i < 3; i++ { + b, errSR := streamReader.ReadByte() + if errSR != nil { + return nil, errSR + } + + result.unknown1 = append(result.unknown1, b) + } result.CofLayers = make([]CofLayer, result.NumberOfLayers) result.CompositeLayers = make(map[d2enum.CompositeType]int) @@ -83,6 +98,7 @@ func Load(fileData []byte) (*COF, error) { layer.Transparent = b[layerTransparent] > 0 layer.DrawEffect = d2enum.DrawEffect(b[layerDrawEffect]) + layer.weaponClassByte = b[layerWeaponClass:] layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll( string(b[layerWeaponClass:]), badCharacter, ""))) @@ -124,3 +140,50 @@ func Load(fileData []byte) (*COF, error) { return result, nil } + +// Marshal encodes COF back into byte slince +func (c *COF) Marshal() []byte { + sw := d2datautils.CreateStreamWriter() + + sw.PushBytes(byte(c.NumberOfLayers)) + sw.PushBytes(byte(c.FramesPerDirection)) + sw.PushBytes(byte(c.NumberOfDirections)) + sw.PushBytes(c.unknownHeaderBytes...) + sw.PushBytes(byte(c.Speed)) + sw.PushBytes(c.unknown1...) + + for i := range c.CofLayers { + sw.PushBytes(byte(c.CofLayers[i].Type.Int())) + sw.PushBytes(c.CofLayers[i].Shadow) + + if c.CofLayers[i].Selectable { + sw.PushBytes(byte(1)) + } else { + sw.PushBytes(byte(0)) + } + + if c.CofLayers[i].Transparent { + sw.PushBytes(byte(1)) + } else { + sw.PushBytes(byte(0)) + } + + sw.PushBytes(byte(c.CofLayers[i].DrawEffect)) + + sw.PushBytes(c.CofLayers[i].weaponClassByte...) + } + + for _, i := range c.AnimationFrames { + sw.PushBytes(byte(i)) + } + + for direction := 0; direction < c.NumberOfDirections; direction++ { + for frame := 0; frame < c.FramesPerDirection; frame++ { + for i := 0; i < c.NumberOfLayers; i++ { + sw.PushBytes(byte(c.Priority[direction][frame][i])) + } + } + } + + return sw.GetBytes() +} diff --git a/d2common/d2fileformats/d2cof/cof_layer.go b/d2common/d2fileformats/d2cof/cof_layer.go index 8a09f9c1..2658e691 100644 --- a/d2common/d2fileformats/d2cof/cof_layer.go +++ b/d2common/d2fileformats/d2cof/cof_layer.go @@ -4,10 +4,11 @@ import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" // CofLayer is a structure that represents a single layer in a COF file. type CofLayer struct { - Type d2enum.CompositeType - Shadow byte - Selectable bool - Transparent bool - DrawEffect d2enum.DrawEffect - WeaponClass d2enum.WeaponClass + Type d2enum.CompositeType + Shadow byte + Selectable bool + Transparent bool + DrawEffect d2enum.DrawEffect + WeaponClass d2enum.WeaponClass + weaponClassByte []byte }