2020-01-26 00:39:13 -05:00
|
|
|
package d2dc6
|
|
|
|
|
|
|
|
import (
|
2020-09-08 15:58:35 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
|
2020-01-26 00:39:13 -05:00
|
|
|
)
|
|
|
|
|
2020-07-18 00:02:45 -04:00
|
|
|
const (
|
|
|
|
endOfScanLine = 0x80
|
|
|
|
maxRunLength = 0x7f
|
|
|
|
)
|
|
|
|
|
2020-08-04 22:23:26 -04:00
|
|
|
type scanlineState int
|
|
|
|
|
|
|
|
const (
|
|
|
|
endOfLine scanlineState = iota
|
|
|
|
runOfTransparentPixels
|
|
|
|
runOfOpaquePixels
|
|
|
|
)
|
|
|
|
|
2020-06-28 22:32:34 -04:00
|
|
|
// DC6 represents a DC6 file.
|
|
|
|
type DC6 struct {
|
2020-07-10 23:02:45 -04:00
|
|
|
Version int32
|
|
|
|
Flags uint32
|
|
|
|
Encoding uint32
|
|
|
|
Termination []byte // 4 bytes
|
|
|
|
Directions uint32
|
|
|
|
FramesPerDirection uint32
|
|
|
|
FramePointers []uint32 // size is Directions*FramesPerDirection
|
|
|
|
Frames []*DC6Frame // size is Directions*FramesPerDirection
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2020-06-28 22:32:34 -04:00
|
|
|
// Load uses restruct to read the binary dc6 data into structs then parses image data from the frame data.
|
|
|
|
func Load(data []byte) (*DC6, error) {
|
2020-07-10 23:02:45 -04:00
|
|
|
const (
|
|
|
|
terminationSize = 4
|
|
|
|
terminatorSize = 3
|
|
|
|
)
|
|
|
|
|
2020-09-08 15:58:35 -04:00
|
|
|
r := d2datautils.CreateStreamReader(data)
|
2020-07-10 23:02:45 -04:00
|
|
|
|
|
|
|
var dc DC6
|
|
|
|
dc.Version = r.GetInt32()
|
|
|
|
dc.Flags = r.GetUInt32()
|
|
|
|
dc.Encoding = r.GetUInt32()
|
|
|
|
dc.Termination = r.ReadBytes(terminationSize)
|
|
|
|
dc.Directions = r.GetUInt32()
|
|
|
|
dc.FramesPerDirection = r.GetUInt32()
|
|
|
|
|
|
|
|
frameCount := int(dc.Directions * dc.FramesPerDirection)
|
|
|
|
|
|
|
|
dc.FramePointers = make([]uint32, frameCount)
|
|
|
|
for i := 0; i < frameCount; i++ {
|
|
|
|
dc.FramePointers[i] = r.GetUInt32()
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-07-10 23:02:45 -04:00
|
|
|
dc.Frames = make([]*DC6Frame, frameCount)
|
2020-07-18 00:02:45 -04:00
|
|
|
|
2020-07-10 23:02:45 -04:00
|
|
|
for i := 0; i < frameCount; i++ {
|
|
|
|
frame := &DC6Frame{
|
|
|
|
Flipped: r.GetUInt32(),
|
|
|
|
Width: r.GetUInt32(),
|
|
|
|
Height: r.GetUInt32(),
|
|
|
|
OffsetX: r.GetInt32(),
|
|
|
|
OffsetY: r.GetInt32(),
|
|
|
|
Unknown: r.GetUInt32(),
|
|
|
|
NextBlock: r.GetUInt32(),
|
|
|
|
Length: r.GetUInt32(),
|
|
|
|
}
|
|
|
|
frame.FrameData = r.ReadBytes(int(frame.Length))
|
|
|
|
frame.Terminator = r.ReadBytes(terminatorSize)
|
|
|
|
dc.Frames[i] = frame
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2020-07-10 23:02:45 -04:00
|
|
|
return &dc, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-07-08 09:14:09 -04:00
|
|
|
|
2020-07-10 23:02:45 -04:00
|
|
|
// DecodeFrame decodes the given frame to an indexed color texture
|
2020-07-08 09:14:09 -04:00
|
|
|
func (d *DC6) DecodeFrame(frameIndex int) []byte {
|
|
|
|
frame := d.Frames[frameIndex]
|
|
|
|
|
|
|
|
indexData := make([]byte, frame.Width*frame.Height)
|
|
|
|
x := 0
|
|
|
|
y := int(frame.Height) - 1
|
|
|
|
offset := 0
|
|
|
|
|
2020-08-04 22:23:26 -04:00
|
|
|
loop: // this is a label for the loop, so the switch can break the loop (and not the switch)
|
2020-07-08 09:14:09 -04:00
|
|
|
for {
|
|
|
|
b := int(frame.FrameData[offset])
|
|
|
|
offset++
|
|
|
|
|
2020-08-04 22:23:26 -04:00
|
|
|
switch scanlineType(b) {
|
|
|
|
case endOfLine:
|
2020-07-08 09:14:09 -04:00
|
|
|
if y == 0 {
|
2020-08-04 22:23:26 -04:00
|
|
|
break loop
|
2020-07-08 09:14:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
y--
|
|
|
|
|
|
|
|
x = 0
|
2020-08-04 22:23:26 -04:00
|
|
|
case runOfTransparentPixels:
|
2020-07-18 00:02:45 -04:00
|
|
|
transparentPixels := b & maxRunLength
|
2020-07-08 09:14:09 -04:00
|
|
|
x += transparentPixels
|
2020-08-04 22:23:26 -04:00
|
|
|
case runOfOpaquePixels:
|
2020-07-08 09:14:09 -04:00
|
|
|
for i := 0; i < b; i++ {
|
|
|
|
indexData[x+y*int(frame.Width)+i] = frame.FrameData[offset]
|
|
|
|
offset++
|
|
|
|
}
|
|
|
|
|
|
|
|
x += b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return indexData
|
|
|
|
}
|
2020-08-04 22:23:26 -04:00
|
|
|
|
|
|
|
func scanlineType(b int) scanlineState {
|
|
|
|
if b == endOfScanLine {
|
|
|
|
return endOfLine
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b & endOfScanLine) > 0 {
|
|
|
|
return runOfTransparentPixels
|
|
|
|
}
|
|
|
|
|
|
|
|
return runOfOpaquePixels
|
|
|
|
}
|
2020-10-26 09:13:08 -04:00
|
|
|
|
|
|
|
// Clone creates a copy of the DC6
|
|
|
|
func (d *DC6) Clone() *DC6 {
|
|
|
|
clone := *d
|
|
|
|
copy(clone.Termination, d.Termination)
|
|
|
|
copy(clone.FramePointers, d.FramePointers)
|
|
|
|
clone.Frames = make([]*DC6Frame, len(d.Frames))
|
|
|
|
|
|
|
|
for i := range d.Frames {
|
|
|
|
cloneFrame := *d.Frames[i]
|
|
|
|
clone.Frames = append(clone.Frames, &cloneFrame)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &clone
|
|
|
|
}
|