1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-07 18:57:18 -05:00
OpenDiablo2/d2common/d2fileformats/d2cof/cof.go
2021-01-31 12:11:54 +01:00

189 lines
4.7 KiB
Go

package d2cof
import (
"strings"
"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))
)
// 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
Speed int
CofLayers []CofLayer
CompositeLayers map[d2enum.CompositeType]int
AnimationFrames []d2enum.AnimationFrame
Priority [][][]d2enum.CompositeType
}
// Load loads a COF file.
func Load(fileData []byte) (*COF, error) {
result := &COF{}
streamReader := d2datautils.CreateStreamReader(fileData)
var b []byte
var err error
b, err = streamReader.ReadBytes(numHeaderBytes)
if err != nil {
return nil, err
}
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])
// 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)
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])
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
}
b, err = streamReader.ReadBytes(result.FramesPerDirection)
if err != nil {
return nil, err
}
result.AnimationFrames = make([]d2enum.AnimationFrame, result.FramesPerDirection)
for i := range b {
result.AnimationFrames[i] = d2enum.AnimationFrame(b[i])
}
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
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++
}
}
}
return result, nil
}
// Marshals encodes COF back into byte slince
func (c *COF) Marshal() []byte {
var result []byte
result = append(result, byte(c.NumberOfLayers))
result = append(result, byte(c.FramesPerDirection))
result = append(result, byte(c.NumberOfDirections))
result = append(result, c.unknownHeaderBytes...)
result = append(result, byte(c.Speed))
result = append(result, c.unknown1...)
for i := range c.CofLayers {
result = append(result, byte(c.CofLayers[i].Type.Int()))
result = append(result, c.CofLayers[i].Shadow)
if c.CofLayers[i].Selectable {
result = append(result, byte(1))
} else {
result = append(result, byte(0))
}
if c.CofLayers[i].Transparent {
result = append(result, byte(1))
} else {
result = append(result, byte(0))
}
result = append(result, byte(c.CofLayers[i].DrawEffect))
result = append(result, c.CofLayers[i].weaponClassByte...)
}
for _, i := range c.AnimationFrames {
result = append(result, byte(i))
}
for direction := 0; direction < c.NumberOfDirections; direction++ {
for frame := 0; frame < c.FramesPerDirection; frame++ {
for i := 0; i < c.NumberOfLayers; i++ {
result = append(result, byte(c.Priority[direction][frame][i]))
}
}
}
return result
}