1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-24 08:05:24 +00:00
OpenDiablo2/d2common/d2fileformats/d2cof/cof.go

190 lines
4.6 KiB
Go
Raw Normal View History

package d2cof
import (
"strings"
2020-09-12 20:25:09 +00:00
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
const (
unknownByteCount = 21
numHeaderBytes = 4 + unknownByteCount
numLayerBytes = 9
)
const (
headerNumLayers = iota
headerFramesPerDir
headerNumDirs
headerSpeed = numHeaderBytes - 1
)
const (
layerType = iota
layerShadow
layerSelectable
layerTransparent
layerDrawEffect
layerWeaponClass
)
const (
badCharacter = string(byte(0))
)
2020-06-29 02:32:34 +00:00
// COF is a structure that represents a COF file.
type COF struct {
2021-01-30 16:09:37 +00:00
// unknown bytes for header
unknownHeaderBytes []byte
// unknown bytes (first "body's" bytes)
unknown1 []byte
NumberOfDirections int
FramesPerDirection int
NumberOfLayers int
Speed int
CofLayers []CofLayer
CompositeLayers map[d2enum.CompositeType]int
AnimationFrames []d2enum.AnimationFrame
Priority [][][]d2enum.CompositeType
}
2020-06-29 02:32:34 +00:00
// Load loads a COF file.
// nolint:funlen // no need to change
2020-06-29 02:32:34 +00:00
func Load(fileData []byte) (*COF, error) {
result := &COF{}
streamReader := d2datautils.CreateStreamReader(fileData)
2020-06-29 02:32:34 +00:00
var b []byte
var err error
b, err = streamReader.ReadBytes(numHeaderBytes)
if err != nil {
return nil, err
}
2020-06-29 02:32:34 +00:00
result.NumberOfLayers = int(b[headerNumLayers])
result.FramesPerDirection = int(b[headerFramesPerDir])
result.NumberOfDirections = int(b[headerNumDirs])
2021-01-30 16:09:37 +00:00
result.unknownHeaderBytes = b[headerNumDirs+1 : headerSpeed]
result.Speed = int(b[headerSpeed])
2020-06-29 02:32:34 +00:00
2021-01-30 16:09:37 +00:00
// 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)
}
2020-06-29 02:32:34 +00:00
result.CofLayers = make([]CofLayer, result.NumberOfLayers)
result.CompositeLayers = make(map[d2enum.CompositeType]int)
2020-06-29 02:32:34 +00:00
for i := 0; i < result.NumberOfLayers; i++ {
layer := CofLayer{}
b, err = streamReader.ReadBytes(numLayerBytes)
if err != nil {
return nil, err
}
layer.Type = d2enum.CompositeType(b[layerType])
layer.Shadow = b[layerShadow]
layer.Selectable = b[layerSelectable] > 0
layer.Transparent = b[layerTransparent] > 0
layer.DrawEffect = d2enum.DrawEffect(b[layerDrawEffect])
2021-01-30 16:09:37 +00:00
layer.weaponClassByte = b[layerWeaponClass:]
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(
string(b[layerWeaponClass:]), badCharacter, "")))
result.CofLayers[i] = layer
result.CompositeLayers[layer.Type] = i
}
2020-06-29 02:32:34 +00:00
b, err = streamReader.ReadBytes(result.FramesPerDirection)
if err != nil {
return nil, err
}
result.AnimationFrames = make([]d2enum.AnimationFrame, result.FramesPerDirection)
2020-06-29 02:32:34 +00:00
for i := range b {
result.AnimationFrames[i] = d2enum.AnimationFrame(b[i])
}
2020-06-29 02:32:34 +00:00
priorityLen := result.FramesPerDirection * result.NumberOfDirections * result.NumberOfLayers
result.Priority = make([][][]d2enum.CompositeType, result.NumberOfDirections)
priorityBytes, err := streamReader.ReadBytes(priorityLen)
if err != nil {
return nil, err
}
priorityIndex := 0
2020-06-29 02:32:34 +00:00
for direction := 0; direction < result.NumberOfDirections; direction++ {
result.Priority[direction] = make([][]d2enum.CompositeType, result.FramesPerDirection)
for frame := 0; frame < result.FramesPerDirection; frame++ {
result.Priority[direction][frame] = make([]d2enum.CompositeType, result.NumberOfLayers)
for i := 0; i < result.NumberOfLayers; i++ {
result.Priority[direction][frame][i] = d2enum.CompositeType(priorityBytes[priorityIndex])
priorityIndex++
}
}
}
2020-06-29 02:32:34 +00:00
return result, nil
}
2021-01-30 16:09:37 +00:00
// Marshal encodes COF back into byte slince
2021-01-31 11:11:54 +00:00
func (c *COF) Marshal() []byte {
sw := d2datautils.CreateStreamWriter()
2021-01-30 16:09:37 +00:00
sw.PushByte(byte(c.NumberOfLayers))
sw.PushByte(byte(c.FramesPerDirection))
sw.PushByte(byte(c.NumberOfDirections))
sw.PushBytes(c.unknownHeaderBytes...)
sw.PushByte(byte(c.Speed))
sw.PushBytes(c.unknown1...)
2021-01-30 16:09:37 +00:00
for i := range c.CofLayers {
sw.PushByte(byte(c.CofLayers[i].Type.Int()))
sw.PushByte(c.CofLayers[i].Shadow)
2021-01-30 16:09:37 +00:00
if c.CofLayers[i].Selectable {
sw.PushByte(byte(1))
2021-01-30 16:09:37 +00:00
} else {
sw.PushByte(byte(0))
2021-01-30 16:09:37 +00:00
}
if c.CofLayers[i].Transparent {
sw.PushByte(byte(1))
2021-01-30 16:09:37 +00:00
} else {
sw.PushByte(byte(0))
2021-01-30 16:09:37 +00:00
}
sw.PushByte(byte(c.CofLayers[i].DrawEffect))
2021-01-30 16:09:37 +00:00
sw.PushBytes(c.CofLayers[i].weaponClassByte...)
2021-01-30 16:09:37 +00:00
}
for _, i := range c.AnimationFrames {
sw.PushByte(byte(i))
2021-01-30 16:09:37 +00:00
}
for direction := 0; direction < c.NumberOfDirections; direction++ {
for frame := 0; frame < c.FramesPerDirection; frame++ {
for i := 0; i < c.NumberOfLayers; i++ {
sw.PushByte(byte(c.Priority[direction][frame][i]))
2021-01-30 16:09:37 +00:00
}
}
}
return sw.GetBytes()
2021-01-30 16:09:37 +00:00
}