Fixed lint issues (#484)

This commit is contained in:
Tim Sarbin 2020-06-28 22:32:34 -04:00 committed by GitHub
parent 09a28c2822
commit 255ffc75da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 505 additions and 247 deletions

View File

@ -1,13 +1,14 @@
// Package d2cof contains the logic for loading and processing COF files.
package d2cof
import (
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// COF is a structure that represents a COF file.
type COF struct {
NumberOfDirections int
FramesPerDirection int
@ -19,17 +20,23 @@ type COF struct {
Priority [][][]d2enum.CompositeType
}
func LoadCOF(fileData []byte) (*COF, error) {
// Load loads a COF file.
func Load(fileData []byte) (*COF, error) {
result := &COF{}
streamReader := d2common.CreateStreamReader(fileData)
result.NumberOfLayers = int(streamReader.GetByte())
result.FramesPerDirection = int(streamReader.GetByte())
result.NumberOfDirections = int(streamReader.GetByte())
streamReader.SkipBytes(21) // Skip 21 unknown bytes...
streamReader.SkipBytes(21) //nolint:gomnd // Unknown data
result.Speed = int(streamReader.GetByte())
streamReader.SkipBytes(3)
streamReader.SkipBytes(3) //nolint:gomnd // Unknown data
result.CofLayers = make([]CofLayer, result.NumberOfLayers)
result.CompositeLayers = make(map[d2enum.CompositeType]int)
for i := 0; i < result.NumberOfLayers; i++ {
layer := CofLayer{}
layer.Type = d2enum.CompositeType(streamReader.GetByte())
@ -37,20 +44,24 @@ func LoadCOF(fileData []byte) (*COF, error) {
layer.Selectable = streamReader.GetByte() != 0
layer.Transparent = streamReader.GetByte() != 0
layer.DrawEffect = d2enum.DrawEffect(streamReader.GetByte())
weaponClassStr := streamReader.ReadBytes(4)
weaponClassStr := streamReader.ReadBytes(4) //nolint:gomnd // Binary data
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(string(weaponClassStr), string(0), "")))
result.CofLayers[i] = layer
result.CompositeLayers[layer.Type] = i
}
animationFrameBytes := streamReader.ReadBytes(result.FramesPerDirection)
result.AnimationFrames = make([]d2enum.AnimationFrame, result.FramesPerDirection)
for i := range animationFrameBytes {
result.AnimationFrames[i] = d2enum.AnimationFrame(animationFrameBytes[i])
}
priorityLen := result.FramesPerDirection * result.NumberOfDirections * result.NumberOfLayers
result.Priority = make([][][]d2enum.CompositeType, result.NumberOfDirections)
priorityBytes := streamReader.ReadBytes(priorityLen)
priorityIndex := 0
for direction := 0; direction < result.NumberOfDirections; direction++ {
result.Priority[direction] = make([][]d2enum.CompositeType, result.FramesPerDirection)
for frame := 0; frame < result.FramesPerDirection; frame++ {
@ -61,5 +72,6 @@ func LoadCOF(fileData []byte) (*COF, error) {
}
}
}
return result, nil
}

View File

@ -2,6 +2,7 @@ package d2cof
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

View File

@ -1,16 +1,8 @@
// Package d2dat contains the logic for loading and processing DAT files.
package d2dat
type DATColor struct {
R uint8
G uint8
B uint8
}
type DATPalette struct {
Colors [256]DATColor
}
func LoadDAT(data []byte) (*DATPalette, error) {
// Load loads a DAT file.
func Load(data []byte) (*DATPalette, error) {
palette := &DATPalette{}
for i := 0; i < 256; i++ {

View File

@ -0,0 +1,8 @@
package d2dat
// DATColor represents a single color in a DAT file.
type DATColor struct {
R uint8
G uint8
B uint8
}

View File

@ -0,0 +1,6 @@
package d2dat
// DATPalette represents a 256 color palette.
type DATPalette struct {
Colors [256]DATColor
}

View File

@ -1,3 +1,4 @@
// Package d2dc6 contains the logic for loading and processing DC6 files.
package d2dc6
import (
@ -6,7 +7,8 @@ import (
"github.com/go-restruct/restruct"
)
type DC6File struct {
// DC6 represents a DC6 file.
type DC6 struct {
// Header
Version int32 `struct:"int32"`
Flags uint32 `struct:"uint32"`
@ -19,45 +21,13 @@ type DC6File struct {
Frames []*DC6Frame `struct-size:"Directions*FramesPerDirection"`
}
type DC6Header struct {
Version int32 `struct:"int32"`
Flags uint32 `struct:"uint32"`
Encoding uint32 `struct:"uint32"`
Termination []byte `struct:"[4]byte"`
Directions int32 `struct:"int32"`
FramesPerDirection int32 `struct:"int32"`
}
type DC6FrameHeader struct {
Flipped int32 `struct:"int32"`
Width int32 `struct:"int32"`
Height int32 `struct:"int32"`
OffsetX int32 `struct:"int32"`
OffsetY int32 `struct:"int32"`
Unknown uint32 `struct:"uint32"`
NextBlock uint32 `struct:"uint32"`
Length uint32 `struct:"uint32"`
}
type DC6Frame struct {
Flipped uint32 `struct:"uint32"`
Width uint32 `struct:"uint32"`
Height uint32 `struct:"uint32"`
OffsetX int32 `struct:"int32"`
OffsetY int32 `struct:"int32"`
Unknown uint32 `struct:"uint32"`
NextBlock uint32 `struct:"uint32"`
Length uint32 `struct:"uint32,sizeof=FrameData"`
FrameData []byte
Terminator []byte `struct:"[3]byte"`
}
// LoadDC6 uses restruct to read the binary dc6 data into structs then parses image data from the frame data.
func LoadDC6(data []byte) (*DC6File, error) {
result := &DC6File{}
// 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) {
result := &DC6{}
restruct.EnableExprBeta()
err := restruct.Unpack(data, binary.LittleEndian, &result)
if err != nil {
return nil, err
}

View File

@ -0,0 +1,15 @@
package d2dc6
// DC6Frame represents a single frame in a DC6.
type DC6Frame struct {
Flipped uint32 `struct:"uint32"`
Width uint32 `struct:"uint32"`
Height uint32 `struct:"uint32"`
OffsetX int32 `struct:"int32"`
OffsetY int32 `struct:"int32"`
Unknown uint32 `struct:"uint32"`
NextBlock uint32 `struct:"uint32"`
Length uint32 `struct:"uint32,sizeof=FrameData"`
FrameData []byte
Terminator []byte `struct:"[3]byte"`
}

View File

@ -0,0 +1,13 @@
package d2dc6
// DC6FrameHeader represents the header of a frame in a DC6.
type DC6FrameHeader struct {
Flipped int32 `struct:"int32"`
Width int32 `struct:"int32"`
Height int32 `struct:"int32"`
OffsetX int32 `struct:"int32"`
OffsetY int32 `struct:"int32"`
Unknown uint32 `struct:"uint32"`
NextBlock uint32 `struct:"uint32"`
Length uint32 `struct:"uint32"`
}

View File

@ -0,0 +1,11 @@
package d2dc6
// DC6Header represents the file header of a DC6 file.
type DC6Header struct {
Version int32 `struct:"int32"`
Flags uint32 `struct:"uint32"`
Encoding uint32 `struct:"uint32"`
Termination []byte `struct:"[4]byte"`
Directions int32 `struct:"int32"`
FramesPerDirection int32 `struct:"int32"`
}

View File

@ -1,59 +0,0 @@
package d2dcc
var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}
var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
// Direction lookup tables courtesy of necrolis
var dir4 = []int{0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
var dir8 = []int{0x04, 0x04, 0x04, 0x04, 0x0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}
var dir16 = []int{0x04, 0x04, 0x08, 0x08, 0x8, 0x08, 0x00, 0x00, 0x00,
0x00, 0x09, 0x09, 0x09, 0x09, 0x05, 0x05, 0x05, 0x05, 0x0A, 0x0A,
0x0A, 0x0A, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x0B, 0x0B, 0x0B, 0x06,
0x06, 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x02, 0x02, 0x02, 0x02,
0x0D, 0x0D, 0x0D, 0x0D, 0x07, 0x07, 0x07, 0x07, 0x0E, 0x0E, 0x0E,
0x0E, 0x03, 0x03, 0x03, 0x03, 0x0F, 0x0F, 0x0F, 0x0F, 0x04, 0x04}
var dir32 = []int{0x04, 0x10, 0x10, 0x08, 0x8, 0x11, 0x11, 0x00, 0x00,
0x12, 0x12, 0x09, 0x09, 0x13, 0x13, 0x05, 0x05, 0x14, 0x14, 0x0A,
0x0A, 0x15, 0x15, 0x01, 0x01, 0x16, 0x16, 0x0B, 0x0B, 0x17, 0x17,
0x06, 0x06, 0x18, 0x18, 0x0C, 0x0C, 0x19, 0x19, 0x02, 0x02, 0x1A,
0x1A, 0x0D, 0x0D, 0x1B, 0x1B, 0x07, 0x07, 0x1C, 0x1C, 0x0E, 0x0E,
0x1D, 0x1D, 0x03, 0x03, 0x1E, 0x1E, 0x0F, 0x0F, 0x1F, 0x1F, 0x04}
// 64 direction assets don't actually exist? but here it is anyway.
var dir64 = []int{0x04, 0x20, 0x10, 0x21, 0x8, 0x22, 0x11, 0x23, 0x00,
0x24, 0x12, 0x25, 0x09, 0x26, 0x13, 0x27, 0x05, 0x28, 0x14, 0x29,
0x0A, 0x2A, 0x15, 0x2B, 0x01, 0x2C, 0x16, 0x2D, 0x0B, 0x2E, 0x17,
0x2F, 0x06, 0x30, 0x18, 0x31, 0x0C, 0x32, 0x19, 0x33, 0x02, 0x34,
0x1A, 0x35, 0x0D, 0x36, 0x1B, 0x37, 0x07, 0x38, 0x1C, 0x39, 0x0E,
0x3A, 0x1D, 0x3B, 0x03, 0x3C, 0x1E, 0x3D, 0x0F, 0x3E, 0x1F, 0x3F}
func Dir64ToDcc(direction, numDirections int) int {
switch numDirections {
case 4:
return dir4[direction]
case 8:
return dir8[direction]
case 16:
return dir16[direction]
case 32:
return dir32[direction]
case 64:
return dir64[direction]
default:
return 0
}
}

View File

@ -1,3 +1,4 @@
// Package d2dcc contains the logic for loading and processing DCC files.
package d2dcc
import (
@ -6,6 +7,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const dccFileSignature = 0x74
const directionOffsetMultiplier = 8
// DCC represents a DCC file.
type DCC struct {
Signature int
Version int
@ -14,27 +19,40 @@ type DCC struct {
Directions []*DCCDirection
}
func LoadDCC(fileData []byte) (*DCC, error) {
// Load loads a DCC file.
func Load(fileData []byte) (*DCC, error) {
result := &DCC{}
var bm = d2common.CreateBitMuncher(fileData, 0)
result.Signature = int(bm.GetByte())
if result.Signature != 0x74 {
if result.Signature != dccFileSignature {
return nil, errors.New("signature expected to be 0x74 but it is not")
}
result.Version = int(bm.GetByte())
result.NumberOfDirections = int(bm.GetByte())
result.FramesPerDirection = int(bm.GetInt32())
if bm.GetInt32() != 1 {
return nil, errors.New("this value isn't 1. It has to be 1")
}
bm.GetInt32() // TotalSizeCoded
directionOffsets := make([]int, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
directionOffsets[i] = int(bm.GetInt32())
}
result.Directions = make([]*DCCDirection, result.NumberOfDirections)
for i := 0; i < result.NumberOfDirections; i++ {
result.Directions[i] = CreateDCCDirection(d2common.CreateBitMuncher(fileData, directionOffsets[i]*8), *result)
result.Directions[i] = CreateDCCDirection(d2common.CreateBitMuncher(
fileData, directionOffsets[i]*directionOffsetMultiplier), result)
}
return result, nil
}

View File

@ -1,5 +1,6 @@
package d2dcc
// DCCCell represents a single cell in a DCC file.
type DCCCell struct {
Width int
Height int

View File

@ -0,0 +1,56 @@
package d2dcc
// Dir64ToDcc returns the DCC direction based on the actual direction.
// Special thanks for Necrolis for these tables!
func Dir64ToDcc(direction, numDirections int) int {
var dir4 = []int{0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
var dir8 = []int{0x04, 0x04, 0x04, 0x04, 0x0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04}
var dir16 = []int{0x04, 0x04, 0x08, 0x08, 0x8, 0x08, 0x00, 0x00, 0x00,
0x00, 0x09, 0x09, 0x09, 0x09, 0x05, 0x05, 0x05, 0x05, 0x0A, 0x0A,
0x0A, 0x0A, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x0B, 0x0B, 0x0B, 0x06,
0x06, 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x02, 0x02, 0x02, 0x02,
0x0D, 0x0D, 0x0D, 0x0D, 0x07, 0x07, 0x07, 0x07, 0x0E, 0x0E, 0x0E,
0x0E, 0x03, 0x03, 0x03, 0x03, 0x0F, 0x0F, 0x0F, 0x0F, 0x04, 0x04}
var dir32 = []int{0x04, 0x10, 0x10, 0x08, 0x8, 0x11, 0x11, 0x00, 0x00,
0x12, 0x12, 0x09, 0x09, 0x13, 0x13, 0x05, 0x05, 0x14, 0x14, 0x0A,
0x0A, 0x15, 0x15, 0x01, 0x01, 0x16, 0x16, 0x0B, 0x0B, 0x17, 0x17,
0x06, 0x06, 0x18, 0x18, 0x0C, 0x0C, 0x19, 0x19, 0x02, 0x02, 0x1A,
0x1A, 0x0D, 0x0D, 0x1B, 0x1B, 0x07, 0x07, 0x1C, 0x1C, 0x0E, 0x0E,
0x1D, 0x1D, 0x03, 0x03, 0x1E, 0x1E, 0x0F, 0x0F, 0x1F, 0x1F, 0x04}
// 64 direction assets don't actually exist? but here it is anyway.
var dir64 = []int{0x04, 0x20, 0x10, 0x21, 0x8, 0x22, 0x11, 0x23, 0x00,
0x24, 0x12, 0x25, 0x09, 0x26, 0x13, 0x27, 0x05, 0x28, 0x14, 0x29,
0x0A, 0x2A, 0x15, 0x2B, 0x01, 0x2C, 0x16, 0x2D, 0x0B, 0x2E, 0x17,
0x2F, 0x06, 0x30, 0x18, 0x31, 0x0C, 0x32, 0x19, 0x33, 0x02, 0x34,
0x1A, 0x35, 0x0D, 0x36, 0x1B, 0x37, 0x07, 0x38, 0x1C, 0x39, 0x0E,
0x3A, 0x1D, 0x3B, 0x03, 0x3C, 0x1E, 0x3D, 0x0F, 0x3E, 0x1F, 0x3F}
switch numDirections {
case 4:
return dir4[direction]
case 8:
return dir8[direction]
case 16:
return dir16[direction]
case 32:
return dir32[direction]
case 64:
return dir64[direction]
default:
return 0
}
}

View File

@ -6,6 +6,9 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const cellsPerRow = 4
// DCCDirection represents a DCCDirection file.
type DCCDirection struct {
OutSizeCoded int
CompressionFlags int
@ -30,22 +33,26 @@ type DCCDirection struct {
PixelBuffer []DCCPixelBufferEntry
}
func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) *DCCDirection {
// CreateDCCDirection creates an instance of a DCCDirection.
func CreateDCCDirection(bm *d2common.BitMuncher, file *DCC) *DCCDirection { //nolint:funlen // Can't reduce
var crazyBitTable = []byte{0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32}
result := &DCCDirection{}
result.OutSizeCoded = int(bm.GetUInt32())
result.CompressionFlags = int(bm.GetBits(2))
result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)])
result.WidthBits = int(crazyBitTable[bm.GetBits(4)])
result.HeightBits = int(crazyBitTable[bm.GetBits(4)])
result.XOffsetBits = int(crazyBitTable[bm.GetBits(4)])
result.YOffsetBits = int(crazyBitTable[bm.GetBits(4)])
result.OptionalDataBits = int(crazyBitTable[bm.GetBits(4)])
result.CodedBytesBits = int(crazyBitTable[bm.GetBits(4)])
result.CompressionFlags = int(bm.GetBits(2)) //nolint:gomnd // binary data
result.Variable0Bits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.WidthBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.HeightBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.XOffsetBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.YOffsetBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.OptionalDataBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.CodedBytesBits = int(crazyBitTable[bm.GetBits(4)]) //nolint:gomnd // binary data
result.Frames = make([]*DCCDirectionFrame, file.FramesPerDirection)
minx := 100000
miny := 100000
maxx := -100000
maxy := -100000
// Load the frame headers
for frameIdx := 0; frameIdx < file.FramesPerDirection; frameIdx++ {
result.Frames[frameIdx] = CreateDCCDirectionFrame(bm, result)
@ -54,20 +61,27 @@ func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) *DCCDirection {
maxx = int(d2common.MaxInt32(int32(result.Frames[frameIdx].Box.Right()), int32(maxx)))
maxy = int(d2common.MaxInt32(int32(result.Frames[frameIdx].Box.Bottom()), int32(maxy)))
}
result.Box = d2common.Rectangle{Left: minx, Top: miny, Width: maxx - minx, Height: maxy - miny}
if result.OptionalDataBits > 0 {
log.Panic("Optional bits in DCC data is not currently supported.")
}
if (result.CompressionFlags & 0x2) > 0 {
result.EqualCellsBitstreamSize = int(bm.GetBits(20))
result.EqualCellsBitstreamSize = int(bm.GetBits(20)) //nolint:gomnd // binary data
}
result.PixelMaskBitstreamSize = int(bm.GetBits(20))
result.PixelMaskBitstreamSize = int(bm.GetBits(20)) //nolint:gomnd // binary data
if (result.CompressionFlags & 0x1) > 0 {
result.EncodingTypeBitsreamSize = int(bm.GetBits(20))
result.RawPixelCodesBitstreamSize = int(bm.GetBits(20))
result.EncodingTypeBitsreamSize = int(bm.GetBits(20)) //nolint:gomnd // binary data
result.RawPixelCodesBitstreamSize = int(bm.GetBits(20)) //nolint:gomnd // binary data
}
// PixelValuesKey
paletteEntryCount := 0
for i := 0; i < 256; i++ {
valid := bm.GetBit() != 0
if valid {
@ -75,72 +89,99 @@ func CreateDCCDirection(bm *d2common.BitMuncher, file DCC) *DCCDirection {
paletteEntryCount++
}
}
// HERE BE GIANTS:
// Because of the way this thing mashes bits together, BIT offset matters
// here. For example, if you are on byte offset 3, bit offset 6, and
// the EqualCellsBitstreamSize is 20 bytes, then the next bit stream
// will be located at byte 23, bit offset 6!
equalCellsBitstream := d2common.CopyBitMuncher(bm)
bm.SkipBits(result.EqualCellsBitstreamSize)
pixelMaskBitstream := d2common.CopyBitMuncher(bm)
bm.SkipBits(result.PixelMaskBitstreamSize)
encodingTypeBitsream := d2common.CopyBitMuncher(bm)
bm.SkipBits(result.EncodingTypeBitsreamSize)
rawPixelCodesBitstream := d2common.CopyBitMuncher(bm)
bm.SkipBits(result.RawPixelCodesBitstreamSize)
pixelCodeandDisplacement := d2common.CopyBitMuncher(bm)
// Calculate the cells for the direction
result.CalculateCells()
result.calculateCells()
// Calculate the cells for each of the frames
for _, frame := range result.Frames {
frame.CalculateCells(result)
frame.recalculateCells(result)
}
// Fill in the pixel buffer
result.FillPixelBuffer(pixelCodeandDisplacement, equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream)
result.fillPixelBuffer(pixelCodeandDisplacement, equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream)
// Generate the actual frame pixel data
result.GenerateFrames(pixelCodeandDisplacement)
result.generateFrames(pixelCodeandDisplacement)
result.PixelBuffer = nil
// Verify that everything we expected to read was actually read (sanity check)...
if equalCellsBitstream.BitsRead != result.EqualCellsBitstreamSize {
log.Panic("Did not read the correct number of bits!")
}
if pixelMaskBitstream.BitsRead != result.PixelMaskBitstreamSize {
log.Panic("Did not read the correct number of bits!")
}
if encodingTypeBitsream.BitsRead != result.EncodingTypeBitsreamSize {
log.Panic("Did not read the correct number of bits!")
}
if rawPixelCodesBitstream.BitsRead != result.RawPixelCodesBitstreamSize {
log.Panic("Did not read the correct number of bits!")
}
bm.SkipBits(pixelCodeandDisplacement.BitsRead)
return result
}
func (v *DCCDirection) GenerateFrames(pcd *d2common.BitMuncher) {
//nolint:gocognit nolint:gocyclo // Can't reduce
func (v *DCCDirection) generateFrames(pcd *d2common.BitMuncher) {
pbIdx := 0
for _, cell := range v.Cells {
cell.LastWidth = -1
cell.LastHeight = -1
}
v.PixelData = make([]byte, v.Box.Width*v.Box.Height)
frameIndex := -1
for _, frame := range v.Frames {
frameIndex++
frame.PixelData = make([]byte, v.Box.Width*v.Box.Height)
c := -1
for _, cell := range frame.Cells {
c++
cellX := cell.XOffset / 4
cellY := cell.YOffset / 4
cellX := cell.XOffset / cellsPerRow
cellY := cell.YOffset / cellsPerRow
cellIndex := cellX + (cellY * v.HorizontalCellCount)
bufferCell := v.Cells[cellIndex]
pbe := v.PixelBuffer[pbIdx]
if (pbe.Frame != frameIndex) || (pbe.FrameCellIndex != c) {
// This buffer cell has an EqualCell bit set to 1, so copy the frame cell or clear it
if (cell.Width != bufferCell.LastWidth) || (cell.Height != bufferCell.LastHeight) {
// Different sizes
/// TODO: Clear the pixels of the frame cell
// Different sizes TODO: Clear the pixels of the frame cell
for y := 0; y < cell.Height; y++ {
for x := 0; x < cell.Width; x++ {
v.PixelData[x+cell.XOffset+((y+cell.YOffset)*v.Box.Width)] = 0
@ -154,7 +195,8 @@ func (v *DCCDirection) GenerateFrames(pcd *d2common.BitMuncher) {
// Frame (buff.lastx, buff.lasty) -> Frame (cell.offx, cell.offy)
// Cell (0, 0,) ->
// blit(dir->bmp, dir->bmp, buff_cell->last_x0, buff_cell->last_y0, cell->x0, cell->y0, cell->w, cell->h );
v.PixelData[fx+cell.XOffset+((fy+cell.YOffset)*v.Box.Width)] = v.PixelData[fx+bufferCell.LastXOffset+((fy+bufferCell.LastYOffset)*v.Box.Width)]
v.PixelData[fx+cell.XOffset+((fy+cell.YOffset)*v.Box.Width)] =
v.PixelData[fx+bufferCell.LastXOffset+((fy+bufferCell.LastYOffset)*v.Box.Width)]
}
}
// Copy it again into the final frame image
@ -168,7 +210,6 @@ func (v *DCCDirection) GenerateFrames(pcd *d2common.BitMuncher) {
} else {
if pbe.Value[0] == pbe.Value[1] {
// Clear the frame
//cell.PixelData = new byte[cell.Width * cell.Height];
for y := 0; y < cell.Height; y++ {
for x := 0; x < cell.Width; x++ {
v.PixelData[x+cell.XOffset+((y+cell.YOffset)*v.Box.Width)] = pbe.Value[0]
@ -187,98 +228,124 @@ func (v *DCCDirection) GenerateFrames(pcd *d2common.BitMuncher) {
}
}
}
// Copy the frame cell into the frame
for fy := 0; fy < cell.Height; fy++ {
for fx := 0; fx < cell.Width; fx++ {
//blit(cell->bmp, frm_bmp, 0, 0, cell->x0, cell->y0, cell->w, cell->h );
// blit(cell->bmp, frm_bmp, 0, 0, cell->x0, cell->y0, cell->w, cell->h );
frame.PixelData[fx+cell.XOffset+((fy+cell.YOffset)*v.Box.Width)] = v.PixelData[fx+cell.XOffset+((fy+cell.YOffset)*v.Box.Width)]
}
}
pbIdx++
}
bufferCell.LastWidth = cell.Width
bufferCell.LastHeight = cell.Height
bufferCell.LastXOffset = cell.XOffset
bufferCell.LastYOffset = cell.YOffset
}
// Free up the stuff we no longer need
frame.Cells = nil
}
v.Cells = nil
v.PixelData = nil
v.PixelBuffer = nil
}
func (v *DCCDirection) FillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher) {
//nolint:funlen nolint:gocognit // can't reduce
func (v *DCCDirection) fillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher) {
var pixelMaskLookup = []int{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
lastPixel := uint32(0)
maxCellX := 0
maxCellY := 0
for _, frame := range v.Frames {
if frame == nil {
continue
}
maxCellX += frame.HorizontalCellCount
maxCellY += frame.VerticalCellCount
}
v.PixelBuffer = make([]DCCPixelBufferEntry, maxCellX*maxCellY)
for i := 0; i < maxCellX*maxCellY; i++ {
v.PixelBuffer[i].Frame = -1
v.PixelBuffer[i].FrameCellIndex = -1
}
cellBuffer := make([]*DCCPixelBufferEntry, v.HorizontalCellCount*v.VerticalCellCount)
frameIndex := -1
pbIndex := -1
pixelMask := uint32(0x00)
var pixelMask uint32
for _, frame := range v.Frames {
frameIndex++
originCellX := (frame.Box.Left - v.Box.Left) / 4
originCellY := (frame.Box.Top - v.Box.Top) / 4
originCellX := (frame.Box.Left - v.Box.Left) / cellsPerRow
originCellY := (frame.Box.Top - v.Box.Top) / cellsPerRow
for cellY := 0; cellY < frame.VerticalCellCount; cellY++ {
currentCellY := cellY + originCellY
for cellX := 0; cellX < frame.HorizontalCellCount; cellX++ {
currentCell := originCellX + cellX + (currentCellY * v.HorizontalCellCount)
nextCell := false
tmp := 0
if cellBuffer[currentCell] != nil {
if v.EqualCellsBitstreamSize > 0 {
tmp = int(ec.GetBit())
} else {
tmp = 0
}
if tmp == 0 {
pixelMask = pm.GetBits(4)
pixelMask = pm.GetBits(4) //nolint:gomnd // binary data
} else {
nextCell = true
}
} else {
pixelMask = 0x0F
}
if nextCell {
continue
}
// Decode the pixels
var pixelStack [4]uint32
lastPixel = 0
numberOfPixelBits := pixelMaskLookup[pixelMask]
encodingType := 0
if (numberOfPixelBits != 0) && (v.EncodingTypeBitsreamSize > 0) {
encodingType = int(et.GetBit())
} else {
encodingType = 0
}
decodedPixel := 0
for i := 0; i < numberOfPixelBits; i++ {
if encodingType != 0 {
pixelStack[i] = rp.GetBits(8)
pixelStack[i] = rp.GetBits(8) //nolint:gomnd // binary data
} else {
pixelStack[i] = lastPixel
pixelDisplacement := pcd.GetBits(4)
pixelDisplacement := pcd.GetBits(4) //nolint:gomnd // binary data
pixelStack[i] += pixelDisplacement
for pixelDisplacement == 15 {
pixelDisplacement = pcd.GetBits(4)
pixelDisplacement = pcd.GetBits(4) //nolint:gomnd // binary data
pixelStack[i] += pixelDisplacement
}
}
if pixelStack[i] == lastPixel {
pixelStack[i] = 0
break
@ -287,9 +354,13 @@ func (v *DCCDirection) FillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher)
decodedPixel++
}
}
oldEntry := cellBuffer[currentCell]
pbIndex++
curIdx := decodedPixel - 1
for i := 0; i < 4; i++ {
if (pixelMask & (1 << uint(i))) != 0 {
if curIdx >= 0 {
@ -302,13 +373,14 @@ func (v *DCCDirection) FillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher)
v.PixelBuffer[pbIndex].Value[i] = oldEntry.Value[i]
}
}
cellBuffer[currentCell] = &v.PixelBuffer[pbIndex]
v.PixelBuffer[pbIndex].Frame = frameIndex
v.PixelBuffer[pbIndex].FrameCellIndex = cellX + (cellY * frame.HorizontalCellCount)
}
}
}
cellBuffer = nil
// Convert the palette entry index into actual palette entries
for i := 0; i <= pbIndex; i++ {
for x := 0; x < 4; x++ {
@ -317,10 +389,10 @@ func (v *DCCDirection) FillPixelBuffer(pcd, ec, pm, et, rp *d2common.BitMuncher)
}
}
func (v *DCCDirection) CalculateCells() {
func (v *DCCDirection) calculateCells() {
// Calculate the number of vertical and horizontal cells we need
v.HorizontalCellCount = 1 + (v.Box.Width-1)/4
v.VerticalCellCount = 1 + (v.Box.Height-1)/4
v.HorizontalCellCount = 1 + (v.Box.Width-1)/cellsPerRow
v.VerticalCellCount = 1 + (v.Box.Height-1)/cellsPerRow
// Calculate the cell widths
cellWidths := make([]int, v.HorizontalCellCount)
if v.HorizontalCellCount == 1 {
@ -344,8 +416,10 @@ func (v *DCCDirection) CalculateCells() {
// Set the cell widths and heights in the cell buffer
v.Cells = make([]*DCCCell, v.VerticalCellCount*v.HorizontalCellCount)
yOffset := 0
for y := 0; y < v.VerticalCellCount; y++ {
xOffset := 0
for x := 0; x < v.HorizontalCellCount; x++ {
v.Cells[x+(y*v.HorizontalCellCount)] = &DCCCell{
Width: cellWidths[x],
@ -353,8 +427,10 @@ func (v *DCCDirection) CalculateCells() {
XOffset: xOffset,
YOffset: yOffset,
}
xOffset += 4
}
yOffset += 4
}
}

View File

@ -6,25 +6,29 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// DCCDirectionFrame represents a direction frame for a DCC.
type DCCDirectionFrame struct {
Box d2common.Rectangle
Cells []DCCCell
PixelData []byte
Width int
Height int
XOffset int
YOffset int
NumberOfOptionalBytes int
NumberOfCodedBytes int
FrameIsBottomUp bool
Box d2common.Rectangle
Cells []DCCCell
PixelData []byte
HorizontalCellCount int
VerticalCellCount int
FrameIsBottomUp bool
valid bool
}
// CreateDCCDirectionFrame Creates a DCCDirectionFrame for a DCC.
func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection) *DCCDirectionFrame {
result := &DCCDirectionFrame{}
bits.GetBits(direction.Variable0Bits) // Variable0
result.Width = int(bits.GetBits(direction.WidthBits))
result.Height = int(bits.GetBits(direction.HeightBits))
result.XOffset = bits.GetSignedBits(direction.XOffsetBits)
@ -32,6 +36,7 @@ func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection)
result.NumberOfOptionalBytes = int(bits.GetBits(direction.OptionalDataBits))
result.NumberOfCodedBytes = int(bits.GetBits(direction.CodedBytesBits))
result.FrameIsBottomUp = bits.GetBit() == 1
if result.FrameIsBottomUp {
log.Panic("Bottom up frames are not implemented.")
} else {
@ -42,27 +47,33 @@ func CreateDCCDirectionFrame(bits *d2common.BitMuncher, direction *DCCDirection)
Height: result.Height,
}
}
result.valid = true
return result
}
func (v *DCCDirectionFrame) CalculateCells(direction *DCCDirection) {
func (v *DCCDirectionFrame) recalculateCells(direction *DCCDirection) {
var w = 4 - ((v.Box.Left - direction.Box.Left) % 4) // Width of the first column (in pixels)
if (v.Width - w) <= 1 {
v.HorizontalCellCount = 1
} else {
tmp := v.Width - w - 1
v.HorizontalCellCount = 2 + (tmp / 4)
v.HorizontalCellCount = 2 + (tmp / 4) //nolint:gomnd magic math
if (tmp % 4) == 0 {
v.HorizontalCellCount--
}
}
h := 4 - ((v.Box.Top - direction.Box.Top) % 4) // Height of the first column (in pixels)
// Height of the first column (in pixels)
h := 4 - ((v.Box.Top - direction.Box.Top) % 4) //nolint:gomnd data decode
if (v.Height - h) <= 1 {
v.VerticalCellCount = 1
} else {
tmp := v.Height - h - 1
v.VerticalCellCount = 2 + (tmp / 4)
v.VerticalCellCount = 2 + (tmp / 4) //nolint:gomnd data decode
if (tmp % 4) == 0 {
v.VerticalCellCount--
}
@ -92,8 +103,10 @@ func (v *DCCDirectionFrame) CalculateCells(direction *DCCDirection) {
v.Cells = make([]DCCCell, v.HorizontalCellCount*v.VerticalCellCount)
offsetY := v.Box.Top - direction.Box.Top
for y := 0; y < v.VerticalCellCount; y++ {
offsetX := v.Box.Left - direction.Box.Left
for x := 0; x < v.HorizontalCellCount; x++ {
v.Cells[x+(y*v.HorizontalCellCount)] = DCCCell{
XOffset: offsetX,
@ -101,8 +114,10 @@ func (v *DCCDirectionFrame) CalculateCells(direction *DCCDirection) {
Width: cellWidths[x],
Height: cellHeights[y],
}
offsetX += cellWidths[x]
}
offsetY += cellHeights[y]
}
}

View File

@ -1,5 +1,6 @@
package d2dcc
// DCCPixelBufferEntry represents a single entry in the pixel buffer.
type DCCPixelBufferEntry struct {
Value [4]byte
Frame int

View File

@ -2,6 +2,7 @@ package d2ds1
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
// WallRecord represents a wall record.
type WallRecord struct {
Type d2enum.TileType
Zero byte

View File

@ -1,5 +1,6 @@
package d2dt1
// Block represents a DT1 block
type Block struct {
X int16
Y int16

View File

@ -8,9 +8,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// DT1
// DT1 represents a DT1 file.
type DT1 struct {
Tiles []Tile
}
@ -19,11 +17,15 @@ type DT1 struct {
type BlockDataFormat int16
const (
BlockFormatRLE BlockDataFormat = 0 // Specifies the block format is RLE encoded
BlockFormatIsometric BlockDataFormat = 1 // Specifies the block format isometrically encoded
// BlockFormatRLE specifies the block format is RLE encoded
BlockFormatRLE BlockDataFormat = 0
// BlockFormatIsometric specifies the block format isometrically encoded
BlockFormatIsometric BlockDataFormat = 1
)
// LoadDT1 loads a DT1 record
//nolint:funlen Can't reduce
func LoadDT1(fileData []byte) (*DT1, error) {
result := &DT1{}
br := d2common.CreateStreamReader(fileData)

View File

@ -1,6 +1,6 @@
package d2dt1
// Lots of unknowns for now
// MaterialFlags represents the material flags. Lots of unknowns for now...
type MaterialFlags struct {
Other bool
Water bool
@ -14,6 +14,8 @@ type MaterialFlags struct {
Snow bool
}
// NewMaterialFlags represents the material flags
// nolint:gomnd Binary values
func NewMaterialFlags(data uint16) MaterialFlags {
return MaterialFlags{
Other: data&0x0001 == 0x0001,

View File

@ -1,5 +1,6 @@
package d2dt1
// SubTileFlags represent the sub-tile flags for a DT1
type SubTileFlags struct {
BlockWalk bool
BlockLOS bool
@ -11,35 +12,47 @@ type SubTileFlags struct {
Unknown3 bool
}
// DebugString returns the debug string
func (s *SubTileFlags) DebugString() string {
result := ""
if s.BlockWalk {
result += "BlockWalk "
}
if s.BlockLOS {
result += "BlockLOS "
}
if s.BlockJump {
result += "BlockJump "
}
if s.BlockPlayerWalk {
result += "BlockPlayerWalk "
}
if s.Unknown1 {
result += "Unknown1 "
}
if s.BlockLight {
result += "BlockLight "
}
if s.Unknown2 {
result += "Unknown2 "
}
if s.Unknown3 {
result += "Unknown3 "
}
return result
}
// NewSubTileFlags returns a list of new subtile flags
//nolint:gomnd binary flags
func NewSubTileFlags(data byte) SubTileFlags {
return SubTileFlags{
BlockWalk: data&1 == 1,

View File

@ -21,5 +21,4 @@ func TestNewSubTile(t *testing.T) {
assert.Equal(i == 6, tile.Unknown2)
assert.Equal(i == 7, tile.Unknown3)
}
}

View File

@ -1,21 +1,25 @@
package d2mpq
var cryptoBuffer [0x500]uint32
var cryptoBufferReady bool
var cryptoBuffer [0x500]uint32 //nolint:gochecknoglobals will fix later..
var cryptoBufferReady bool //nolint:gochecknoglobals will fix later..
func cryptoLookup(index uint32) uint32 {
if !cryptoBufferReady {
cryptoInitialize()
cryptoBufferReady = true
}
return cryptoBuffer[index]
}
//nolint:gomnd magic cryptographic stuff here...
func cryptoInitialize() {
seed := uint32(0x00100001)
for index1 := 0; index1 < 0x100; index1++ {
index2 := index1
for i := 0; i < 5; i++ {
seed = (seed*125 + 3) % 0x2AAAAB
temp1 := (seed & 0xFFFF) << 0x10

View File

@ -1,9 +1,11 @@
package d2mpq
// HashEntryMap represents a hash entry map
type HashEntryMap struct {
entries map[uint64]*HashTableEntry
}
// Insert inserts a hash entry into the table
func (hem *HashEntryMap) Insert(entry *HashTableEntry) {
if hem.entries == nil {
hem.entries = make(map[uint64]*HashTableEntry)
@ -12,6 +14,7 @@ func (hem *HashEntryMap) Insert(entry *HashTableEntry) {
hem.entries[uint64(entry.NamePartA)<<32|uint64(entry.NamePartB)] = entry
}
// Find finds a hash entry
func (hem *HashEntryMap) Find(fileName string) (*HashTableEntry, bool) {
if hem.entries == nil {
return nil, false
@ -21,9 +24,11 @@ func (hem *HashEntryMap) Find(fileName string) (*HashTableEntry, bool) {
hashB := hashString(fileName, 2)
entry, found := hem.entries[uint64(hashA)<<32|uint64(hashB)]
return entry, found
}
// Contains returns true if the hash entry contains the values
func (hem *HashEntryMap) Contains(fileName string) bool {
_, found := hem.Find(fileName)
return found

View File

@ -1,3 +1,4 @@
// Package d2mpq contains the functions for handling MPQ files.
package d2mpq
import (
@ -44,6 +45,7 @@ type HashTableEntry struct { // 16 bytes
BlockIndex uint32
}
// PatchInfo represents patch info for the MPQ.
type PatchInfo struct {
Length uint32 // Length of patch info header, in bytes
Flags uint32 // Flags. 0x80000000 = MD5 (?)
@ -101,7 +103,7 @@ func Load(fileName string) (*MPQ, error) {
if runtime.GOOS == "linux" {
result.File, err = openIgnoreCase(fileName)
} else {
result.File, err = os.Open(fileName)
result.File, err = os.Open(fileName) //nolint:gosec Will fix later
}
if err != nil {
@ -117,7 +119,7 @@ func Load(fileName string) (*MPQ, error) {
func openIgnoreCase(mpqPath string) (*os.File, error) {
// First see if file exists with specified case
mpqFile, err := os.Open(mpqPath)
mpqFile, err := os.Open(mpqPath) //nolint:gosec Will fix later
if err == nil {
return mpqFile, err
}
@ -137,20 +139,25 @@ func openIgnoreCase(mpqPath string) (*os.File, error) {
}
}
file, err := os.Open(path.Join(mpqDir, mpqName))
file, err := os.Open(path.Join(mpqDir, mpqName)) //nolint:gosec Will fix later
return file, err
}
func (v *MPQ) readHeader() error {
err := binary.Read(v.File, binary.LittleEndian, &v.Data)
if err != nil {
return err
}
if string(v.Data.Magic[:]) != "MPQ\x1A" {
return errors.New("invalid mpq header")
}
v.loadHashTable()
v.loadBlockTable()
return nil
}
@ -159,21 +166,24 @@ func (v *MPQ) loadHashTable() {
if err != nil {
log.Panic(err)
}
hashData := make([]uint32, v.Data.HashTableEntries*4)
hashData := make([]uint32, v.Data.HashTableEntries*4) //nolint:gomnd Decryption magic
hash := make([]byte, 4)
for i := range hashData {
v.File.Read(hash[:])
_, _ = v.File.Read(hash)
hashData[i] = binary.LittleEndian.Uint32(hash)
}
decrypt(hashData, hashString("(hash table)", 3))
for i := uint32(0); i < v.Data.HashTableEntries; i++ {
v.HashEntryMap.Insert(&HashTableEntry{
NamePartA: hashData[i*4],
NamePartB: hashData[(i*4)+1],
// TODO: Verify that we're grabbing the right high/lo word for the vars below
Locale: uint16(hashData[(i*4)+2] >> 16),
Platform: uint16(hashData[(i*4)+2] & 0xFFFF),
//nolint:godox // TODO: Verify that we're grabbing the right high/lo word for the vars below
Locale: uint16(hashData[(i*4)+2] >> 16), //nolint:gomnd binary data
Platform: uint16(hashData[(i*4)+2] & 0xFFFF), //nolint:gomnd binary data
BlockIndex: hashData[(i*4)+3],
})
}
@ -185,12 +195,14 @@ func (v *MPQ) loadBlockTable() {
log.Panic(err)
}
blockData := make([]uint32, v.Data.BlockTableEntries*4)
blockData := make([]uint32, v.Data.BlockTableEntries*4) //nolint:gomnd binary data
hash := make([]byte, 4)
for i := range blockData {
v.File.Read(hash[:])
_, _ = v.File.Read(hash[:]) //nolint:errcheck Will fix later
blockData[i] = binary.LittleEndian.Uint32(hash)
}
decrypt(blockData, hashString("(block table)", 3))
for i := uint32(0); i < v.Data.BlockTableEntries; i++ {
@ -204,54 +216,56 @@ func (v *MPQ) loadBlockTable() {
}
func decrypt(data []uint32, seed uint32) {
seed2 := uint32(0xeeeeeeee)
seed2 := uint32(0xeeeeeeee) //nolint:gomnd Decryption magic
for i := 0; i < len(data); i++ {
seed2 += cryptoLookup(0x400 + (seed & 0xff))
seed2 += cryptoLookup(0x400 + (seed & 0xff)) //nolint:gomnd Decryption magic
result := data[i]
result ^= seed + seed2
seed = ((^seed << 21) + 0x11111111) | (seed >> 11)
seed2 = result + seed2 + (seed2 << 5) + 3
seed2 = result + seed2 + (seed2 << 5) + 3 //nolint:gomnd Decryption magic
data[i] = result
}
}
func decryptBytes(data []byte, seed uint32) {
seed2 := uint32(0xEEEEEEEE)
seed2 := uint32(0xEEEEEEEE) //nolint:gomnd Decryption magic
for i := 0; i < len(data)-3; i += 4 {
seed2 += cryptoLookup(0x400 + (seed & 0xFF))
seed2 += cryptoLookup(0x400 + (seed & 0xFF)) //nolint:gomnd Decryption magic
result := binary.LittleEndian.Uint32(data[i : i+4])
result ^= seed + seed2
seed = ((^seed << 21) + 0x11111111) | (seed >> 11)
seed2 = result + seed2 + (seed2 << 5) + 3
seed2 = result + seed2 + (seed2 << 5) + 3 //nolint:gomnd Decryption magic
data[i+0] = uint8(result & 0xff)
data[i+1] = uint8((result >> 8) & 0xff)
data[i+2] = uint8((result >> 16) & 0xff)
data[i+3] = uint8((result >> 24) & 0xff)
data[i+0] = uint8(result & 0xff) //nolint:gomnd Decryption magic
data[i+1] = uint8((result >> 8) & 0xff) //nolint:gomnd Decryption magic
data[i+2] = uint8((result >> 16) & 0xff) //nolint:gomnd Decryption magic
data[i+3] = uint8((result >> 24) & 0xff) //nolint:gomnd Decryption magic
}
}
func hashString(key string, hashType uint32) uint32 {
seed1 := uint32(0x7FED7FED)
seed2 := uint32(0xEEEEEEEE)
seed1 := uint32(0x7FED7FED) //nolint:gomnd Decryption magic
seed2 := uint32(0xEEEEEEEE) //nolint:gomnd Decryption magic
/* prepare seeds. */
for _, char := range strings.ToUpper(key) {
seed1 = cryptoLookup((hashType*0x100)+uint32(char)) ^ (seed1 + seed2)
seed2 = uint32(char) + seed1 + seed2 + (seed2 << 5) + 3
seed2 = uint32(char) + seed1 + seed2 + (seed2 << 5) + 3 //nolint:gomnd Decryption magic
}
return seed1
}
// GetFileBlockData gets a block table entry
func (v *MPQ) getFileBlockData(fileName string) (BlockTableEntry, error) {
fileEntry, found := v.HashEntryMap.Find(fileName)
if !found || fileEntry.BlockIndex >= uint32(len(v.BlockTableEntries)) {
return BlockTableEntry{}, errors.New("file not found")
}
return v.BlockTableEntries[fileEntry.BlockIndex], nil
}
@ -263,6 +277,7 @@ func (v *MPQ) Close() {
}
}
// FileExists checks the mpq to see if the file exists
func (v *MPQ) FileExists(fileName string) bool {
return v.HashEntryMap.Contains(fileName)
}
@ -285,14 +300,18 @@ func (v *MPQ) ReadFile(fileName string) ([]byte, error) {
buffer := make([]byte, fileBlockData.UncompressedFileSize)
mpqStream.Read(buffer, 0, fileBlockData.UncompressedFileSize)
return buffer, nil
}
// ReadFileStream reads the mpq file data and returns a stream
func (v *MPQ) ReadFileStream(fileName string) (*MpqDataStream, error) {
fileBlockData, err := v.getFileBlockData(fileName)
if err != nil {
return nil, err
}
fileBlockData.FileName = strings.ToLower(fileName)
fileBlockData.calculateEncryptionSeed()
@ -307,33 +326,42 @@ func (v *MPQ) ReadFileStream(fileName string) (*MpqDataStream, error) {
// ReadTextFile reads a file and returns it as a string
func (v *MPQ) ReadTextFile(fileName string) (string, error) {
data, err := v.ReadFile(fileName)
if err != nil {
return "", err
}
return string(data), nil
}
func (v *BlockTableEntry) calculateEncryptionSeed() {
fileName := path.Base(v.FileName)
v.EncryptionSeed = hashString(fileName, 3)
if !v.HasFlag(FileFixKey) {
return
}
v.EncryptionSeed = (v.EncryptionSeed + v.FilePosition) ^ v.UncompressedFileSize
}
// GetFileList returns the list of files in this MPQ
func (v *MPQ) GetFileList() ([]string, error) {
data, err := v.ReadFile("(listfile)")
if err != nil {
return nil, err
}
raw := strings.TrimRight(string(data), "\x00")
s := bufio.NewScanner(strings.NewReader(raw))
var filePaths []string
for s.Scan() {
filePath := s.Text()
filePaths = append(filePaths, filePath)
}
return filePaths, nil
}

View File

@ -1,19 +1,23 @@
package d2mpq
// MpqDataStream represents a stream for MPQ data.
type MpqDataStream struct {
stream *Stream
}
// Read reads data from the data stream
func (m *MpqDataStream) Read(p []byte) (n int, err error) {
totalRead := m.stream.Read(p, 0, uint32(len(p)))
return int(totalRead), nil
}
// Seek sets the position of the data stream
func (m *MpqDataStream) Seek(offset int64, whence int) (int64, error) {
m.stream.CurrentPosition = uint32(offset + int64(whence))
return int64(m.stream.CurrentPosition), nil
}
// Close closes the data stream
func (m *MpqDataStream) Close() error {
m.stream = nil
return nil

View File

@ -1,5 +1,6 @@
package d2mpq
// MpqFileRecord represents a file record in an MPQ
type MpqFileRecord struct {
MpqFile string
IsPatch bool

View File

@ -33,49 +33,62 @@ func CreateStream(mpq *MPQ, blockTableEntry BlockTableEntry, fileName string) (*
result := &Stream{
MPQData: mpq,
BlockTableEntry: blockTableEntry,
CurrentBlockIndex: 0xFFFFFFFF,
CurrentBlockIndex: 0xFFFFFFFF, //nolint:gomnd MPQ magic
}
fileSegs := strings.Split(fileName, `\`)
result.EncryptionSeed = hashString(fileSegs[len(fileSegs)-1], 3)
if result.BlockTableEntry.HasFlag(FileFixKey) {
result.EncryptionSeed = (result.EncryptionSeed + result.BlockTableEntry.FilePosition) ^ result.BlockTableEntry.UncompressedFileSize
}
result.BlockSize = 0x200 << result.MPQData.Data.BlockSize
result.BlockSize = 0x200 << result.MPQData.Data.BlockSize //nolint:gomnd MPQ magic
if result.BlockTableEntry.HasFlag(FilePatchFile) {
log.Fatal("Patching is not supported")
}
var err error
if (result.BlockTableEntry.HasFlag(FileCompress) || result.BlockTableEntry.HasFlag(FileImplode)) && !result.BlockTableEntry.HasFlag(FileSingleUnit) {
if (result.BlockTableEntry.HasFlag(FileCompress) || result.BlockTableEntry.HasFlag(FileImplode)) &&
!result.BlockTableEntry.HasFlag(FileSingleUnit) {
err = result.loadBlockOffsets()
}
return result, err
}
func (v *Stream) loadBlockOffsets() error {
blockPositionCount := ((v.BlockTableEntry.UncompressedFileSize + v.BlockSize - 1) / v.BlockSize) + 1
v.BlockPositions = make([]uint32, blockPositionCount)
v.MPQData.File.Seek(int64(v.BlockTableEntry.FilePosition), 0)
mpqBytes := make([]byte, blockPositionCount*4)
v.MPQData.File.Read(mpqBytes)
_, _ = v.MPQData.File.Seek(int64(v.BlockTableEntry.FilePosition), 0)
mpqBytes := make([]byte, blockPositionCount*4) //nolint:gomnd MPQ magic
_, _ = v.MPQData.File.Read(mpqBytes)
for i := range v.BlockPositions {
idx := i * 4
idx := i * 4 //nolint:gomnd MPQ magic
v.BlockPositions[i] = binary.LittleEndian.Uint32(mpqBytes[idx : idx+4])
}
//binary.Read(v.MPQData.File, binary.LittleEndian, &v.BlockPositions)
blockPosSize := blockPositionCount << 2
blockPosSize := blockPositionCount << 2 //nolint:gomnd MPQ magic
if v.BlockTableEntry.HasFlag(FileEncrypted) {
decrypt(v.BlockPositions, v.EncryptionSeed-1)
if v.BlockPositions[0] != blockPosSize {
log.Println("Decryption of MPQ failed!")
return errors.New("decryption of MPQ failed")
}
if v.BlockPositions[1] > v.BlockSize+blockPosSize {
log.Println("Decryption of MPQ failed!")
return errors.New("decryption of MPQ failed")
}
}
return nil
}
@ -83,17 +96,22 @@ func (v *Stream) Read(buffer []byte, offset, count uint32) uint32 {
if v.BlockTableEntry.HasFlag(FileSingleUnit) {
return v.readInternalSingleUnit(buffer, offset, count)
}
toRead := count
readTotal := uint32(0)
for toRead > 0 {
read := v.readInternal(buffer, offset, toRead)
if read == 0 {
break
}
readTotal += read
offset += read
toRead -= read
}
return readTotal
}
@ -103,28 +121,38 @@ func (v *Stream) readInternalSingleUnit(buffer []byte, offset, count uint32) uin
}
bytesToCopy := d2common.Min(uint32(len(v.CurrentData))-v.CurrentPosition, count)
copy(buffer[offset:offset+bytesToCopy], v.CurrentData[v.CurrentPosition:v.CurrentPosition+bytesToCopy])
v.CurrentPosition += bytesToCopy
return bytesToCopy
}
func (v *Stream) readInternal(buffer []byte, offset, count uint32) uint32 {
v.bufferData()
localPosition := v.CurrentPosition % v.BlockSize
bytesToCopy := d2common.MinInt32(int32(len(v.CurrentData))-int32(localPosition), int32(count))
if bytesToCopy <= 0 {
return 0
}
copy(buffer[offset:offset+uint32(bytesToCopy)], v.CurrentData[localPosition:localPosition+uint32(bytesToCopy)])
v.CurrentPosition += uint32(bytesToCopy)
return uint32(bytesToCopy)
}
func (v *Stream) bufferData() {
requiredBlock := v.CurrentPosition / v.BlockSize
if requiredBlock == v.CurrentBlockIndex {
return
}
expectedLength := d2common.Min(v.BlockTableEntry.UncompressedFileSize-(requiredBlock*v.BlockSize), v.BlockSize)
v.CurrentData = v.loadBlock(requiredBlock, expectedLength)
v.CurrentBlockIndex = requiredBlock
@ -132,12 +160,14 @@ func (v *Stream) bufferData() {
func (v *Stream) loadSingleUnit() {
fileData := make([]byte, v.BlockSize)
v.MPQData.File.Seek(int64(v.MPQData.Data.HeaderSize), 0)
v.MPQData.File.Read(fileData)
_, _ = v.MPQData.File.Seek(int64(v.MPQData.Data.HeaderSize), 0)
_, _ = v.MPQData.File.Read(fileData)
if v.BlockSize == v.BlockTableEntry.UncompressedFileSize {
v.CurrentData = fileData
return
}
v.CurrentData = decompressMulti(fileData, v.BlockTableEntry.UncompressedFileSize)
}
@ -146,6 +176,7 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
offset uint32
toRead uint32
)
if v.BlockTableEntry.HasFlag(FileCompress) || v.BlockTableEntry.HasFlag(FileImplode) {
offset = v.BlockPositions[blockIndex]
toRead = v.BlockPositions[blockIndex+1] - offset
@ -153,10 +184,13 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
offset = blockIndex * v.BlockSize
toRead = expectedLength
}
offset += v.BlockTableEntry.FilePosition
data := make([]byte, toRead)
v.MPQData.File.Seek(int64(offset), 0)
v.MPQData.File.Read(data)
_, _ = v.MPQData.File.Seek(int64(offset), 0)
_, _ = v.MPQData.File.Read(data)
if v.BlockTableEntry.HasFlag(FileEncrypted) && v.BlockTableEntry.UncompressedFileSize > 3 {
if v.EncryptionSeed == 0 {
panic("Unable to determine encryption key")
@ -164,6 +198,7 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
decryptBytes(data, blockIndex+v.EncryptionSeed)
}
if v.BlockTableEntry.HasFlag(FileCompress) && (toRead != expectedLength) {
if !v.BlockTableEntry.HasFlag(FileSingleUnit) {
data = decompressMulti(data, expectedLength)
@ -171,6 +206,7 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
data = pkDecompress(data)
}
}
if v.BlockTableEntry.HasFlag(FileImplode) && (toRead != expectedLength) {
data = pkDecompress(data)
}
@ -178,9 +214,11 @@ func (v *Stream) loadBlock(blockIndex, expectedLength uint32) []byte {
return data
}
func decompressMulti(data []byte, expectedLength uint32) []byte {
copmressionType := data[0]
switch copmressionType {
//nolint:gomnd Will fix enum values later
func decompressMulti(data []byte /*expectedLength*/, _ uint32) []byte {
compressionType := data[0]
switch compressionType {
case 1: // Huffman
panic("huffman decompression not supported")
case 2: // ZLib/Deflate
@ -197,16 +235,18 @@ func decompressMulti(data []byte, expectedLength uint32) []byte {
panic("lzma decompression not supported")
// Combos
case 0x22:
// TODO: sparse then zlib
// sparse then zlib
panic("sparse decompression + deflate decompression not supported")
case 0x30:
// TODO: sparse then bzip2
// sparse then bzip2
panic("sparse decompression + bzip2 decompression not supported")
case 0x41:
sinput := d2compression.HuffmanDecompress(data[1:])
sinput = d2compression.WavDecompress(sinput, 1)
tmp := make([]byte, len(sinput))
copy(tmp, sinput)
return tmp
case 0x48:
//byte[] result = PKDecompress(sinput, outputLength);
@ -223,42 +263,54 @@ func decompressMulti(data []byte, expectedLength uint32) []byte {
//return MpqWavCompression.Decompress(new MemoryStream(result), 2);
panic("pk + wav decompression not supported")
default:
panic(fmt.Sprintf("decompression not supported for unknown compression type %X", copmressionType))
panic(fmt.Sprintf("decompression not supported for unknown compression type %X", compressionType))
}
}
func deflate(data []byte) []byte {
b := bytes.NewReader(data)
r, err := zlib.NewReader(b)
if err != nil {
panic(err)
}
buffer := new(bytes.Buffer)
_, err = buffer.ReadFrom(r)
if err != nil {
log.Panic(err)
}
err = r.Close()
if err != nil {
log.Panic(err)
}
return buffer.Bytes()
}
func pkDecompress(data []byte) []byte {
b := bytes.NewReader(data)
r, err := blast.NewReader(b)
if err != nil {
panic(err)
}
buffer := new(bytes.Buffer)
_, err = buffer.ReadFrom(r)
if err != nil {
panic(err)
}
err = r.Close()
if err != nil {
panic(err)
}
return buffer.Bytes()
}

View File

@ -1,3 +1,4 @@
// Package d2pl2 handles processing of PL2 palette files.
package d2pl2
import (
@ -6,7 +7,8 @@ import (
"github.com/go-restruct/restruct"
)
type PL2File struct {
// PL2 represents a palette file.
type PL2 struct {
BasePalette PL2Palette
LightLevelVariations [32]PL2PaletteTransform
@ -27,30 +29,9 @@ type PL2File struct {
TextColorShifts [13]PL2PaletteTransform
}
type PL2Color struct {
R uint8
G uint8
B uint8
_ uint8
}
type PL2Color24Bits struct {
R uint8
G uint8
B uint8
}
type PL2Palette struct {
Colors [256]PL2Color
}
type PL2PaletteTransform struct {
Indices [256]uint8
}
// uses restruct to read the binary dc6 data into structs
func LoadPL2(data []byte) (*PL2File, error) {
result := &PL2File{}
// Load uses restruct to read the binary pl2 data into structs
func Load(data []byte) (*PL2, error) {
result := &PL2{}
restruct.EnableExprBeta()

View File

@ -0,0 +1,9 @@
package d2pl2
// PL2Color represents an RGBA color
type PL2Color struct {
R uint8
G uint8
B uint8
_ uint8
}

View File

@ -0,0 +1,8 @@
package d2pl2
// PL2Color24Bits represents an RGB color
type PL2Color24Bits struct {
R uint8
G uint8
B uint8
}

View File

@ -0,0 +1,6 @@
package d2pl2
// PL2Palette represents a PL2 palette.
type PL2Palette struct {
Colors [256]PL2Color
}

View File

@ -0,0 +1,6 @@
package d2pl2
// PL2PaletteTransform represents a PL2 palette transform.
type PL2PaletteTransform struct {
Indices [256]uint8
}

View File

@ -114,7 +114,7 @@ func CreateAnimationFromDCC(dcc *d2dcc.DCC, palette *d2dat.DATPalette, transpare
return animation, nil
}
func CreateAnimationFromDC6(dc6 *d2dc6.DC6File, palette *d2dat.DATPalette) (*Animation, error) {
func CreateAnimationFromDC6(dc6 *d2dc6.DC6, palette *d2dat.DATPalette) (*Animation, error) {
animation := &Animation{
playLength: 1.0,
playLoop: true,

View File

@ -22,13 +22,13 @@ type assetManager struct {
fontManager *fontManager
}
func loadDC6(dc6Path string) (*d2dc6.DC6File, error) {
func loadDC6(dc6Path string) (*d2dc6.DC6, error) {
dc6Data, err := LoadFile(dc6Path)
if err != nil {
return nil, err
}
dc6, err := d2dc6.LoadDC6(dc6Data)
dc6, err := d2dc6.Load(dc6Data)
if err != nil {
return nil, err
}
@ -42,7 +42,7 @@ func loadDCC(dccPath string) (*d2dcc.DCC, error) {
return nil, err
}
return d2dcc.LoadDCC(dccData)
return d2dcc.Load(dccData)
}
func loadCOF(cofPath string) (*d2cof.COF, error) {
@ -51,5 +51,5 @@ func loadCOF(cofPath string) (*d2cof.COF, error) {
return nil, err
}
return d2cof.LoadCOF(cofData)
return d2cof.Load(cofData)
}

View File

@ -112,7 +112,7 @@ func LoadAnimation(animationPath, palettePath string) (*Animation, error) {
return LoadAnimationWithTransparency(animationPath, palettePath, 255)
}
func LoadPaletteTransform(pl2Path string) (*d2pl2.PL2File, error) {
func LoadPaletteTransform(pl2Path string) (*d2pl2.PL2, error) {
verifyWasInit()
return singleton.paletteTransformManager.loadPaletteTransform(pl2Path)
}

View File

@ -27,7 +27,7 @@ func (pm *paletteManager) loadPalette(palettePath string) (*d2dat.DATPalette, er
return nil, err
}
palette, err := d2dat.LoadDAT(paletteData)
palette, err := d2dat.Load(paletteData)
if err != nil {
return nil, err
}

View File

@ -17,9 +17,9 @@ func createPaletteTransformManager() *paletteTransformManager {
return &paletteTransformManager{d2common.CreateCache(paletteTransformBudget)}
}
func (pm *paletteTransformManager) loadPaletteTransform(path string) (*d2pl2.PL2File, error) {
func (pm *paletteTransformManager) loadPaletteTransform(path string) (*d2pl2.PL2, error) {
if pl2, found := pm.cache.Retrieve(path); found {
return pl2.(*d2pl2.PL2File), nil
return pl2.(*d2pl2.PL2), nil
}
data, err := LoadFile(path)
@ -27,7 +27,7 @@ func (pm *paletteTransformManager) loadPaletteTransform(path string) (*d2pl2.PL2
return nil, err
}
pl2, err := d2pl2.LoadPL2(data)
pl2, err := d2pl2.Load(data)
if err != nil {
return nil, err
}