diff --git a/d2common/d2fileformats/d2cof/cof.go b/d2common/d2fileformats/d2cof/cof.go index 48a42c7c..7053d904 100644 --- a/d2common/d2fileformats/d2cof/cof.go +++ b/d2common/d2fileformats/d2cof/cof.go @@ -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 } diff --git a/d2common/d2fileformats/d2cof/coflayer.go b/d2common/d2fileformats/d2cof/cof_layer.go similarity index 77% rename from d2common/d2fileformats/d2cof/coflayer.go rename to d2common/d2fileformats/d2cof/cof_layer.go index d453f0a5..8a09f9c1 100644 --- a/d2common/d2fileformats/d2cof/coflayer.go +++ b/d2common/d2fileformats/d2cof/cof_layer.go @@ -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 diff --git a/d2common/d2fileformats/d2dat/d2dat.go b/d2common/d2fileformats/d2dat/dat.go similarity index 53% rename from d2common/d2fileformats/d2dat/d2dat.go rename to d2common/d2fileformats/d2dat/dat.go index c217b3cc..514075d2 100644 --- a/d2common/d2fileformats/d2dat/d2dat.go +++ b/d2common/d2fileformats/d2dat/dat.go @@ -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++ { diff --git a/d2common/d2fileformats/d2dat/dat_color.go b/d2common/d2fileformats/d2dat/dat_color.go new file mode 100644 index 00000000..2af2b76a --- /dev/null +++ b/d2common/d2fileformats/d2dat/dat_color.go @@ -0,0 +1,8 @@ +package d2dat + +// DATColor represents a single color in a DAT file. +type DATColor struct { + R uint8 + G uint8 + B uint8 +} diff --git a/d2common/d2fileformats/d2dat/dat_palette.go b/d2common/d2fileformats/d2dat/dat_palette.go new file mode 100644 index 00000000..89535b5a --- /dev/null +++ b/d2common/d2fileformats/d2dat/dat_palette.go @@ -0,0 +1,6 @@ +package d2dat + +// DATPalette represents a 256 color palette. +type DATPalette struct { + Colors [256]DATColor +} diff --git a/d2common/d2fileformats/d2dc6/dc6.go b/d2common/d2fileformats/d2dc6/dc6.go index 2e59d7b3..886df74b 100644 --- a/d2common/d2fileformats/d2dc6/dc6.go +++ b/d2common/d2fileformats/d2dc6/dc6.go @@ -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 } diff --git a/d2common/d2fileformats/d2dc6/dc6_frame.go b/d2common/d2fileformats/d2dc6/dc6_frame.go new file mode 100644 index 00000000..4b7162be --- /dev/null +++ b/d2common/d2fileformats/d2dc6/dc6_frame.go @@ -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"` +} diff --git a/d2common/d2fileformats/d2dc6/dc6_frame_header.go b/d2common/d2fileformats/d2dc6/dc6_frame_header.go new file mode 100644 index 00000000..ec36501f --- /dev/null +++ b/d2common/d2fileformats/d2dc6/dc6_frame_header.go @@ -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"` +} diff --git a/d2common/d2fileformats/d2dc6/dc6_header.go b/d2common/d2fileformats/d2dc6/dc6_header.go new file mode 100644 index 00000000..e947979e --- /dev/null +++ b/d2common/d2fileformats/d2dc6/dc6_header.go @@ -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"` +} diff --git a/d2common/d2fileformats/d2dcc/common_data.go b/d2common/d2fileformats/d2dcc/common_data.go deleted file mode 100644 index c17a5c2e..00000000 --- a/d2common/d2fileformats/d2dcc/common_data.go +++ /dev/null @@ -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 - } -} diff --git a/d2common/d2fileformats/d2dcc/dcc.go b/d2common/d2fileformats/d2dcc/dcc.go index 0ae9837e..518257be 100644 --- a/d2common/d2fileformats/d2dcc/dcc.go +++ b/d2common/d2fileformats/d2dcc/dcc.go @@ -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 } diff --git a/d2common/d2fileformats/d2dcc/dcc_cell.go b/d2common/d2fileformats/d2dcc/dcc_cell.go index fba5aa6e..01114740 100644 --- a/d2common/d2fileformats/d2dcc/dcc_cell.go +++ b/d2common/d2fileformats/d2dcc/dcc_cell.go @@ -1,5 +1,6 @@ package d2dcc +// DCCCell represents a single cell in a DCC file. type DCCCell struct { Width int Height int diff --git a/d2common/d2fileformats/d2dcc/dcc_dir_lookup.go b/d2common/d2fileformats/d2dcc/dcc_dir_lookup.go new file mode 100644 index 00000000..f2a7d271 --- /dev/null +++ b/d2common/d2fileformats/d2dcc/dcc_dir_lookup.go @@ -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 + } +} diff --git a/d2common/d2fileformats/d2dcc/dcc_direction.go b/d2common/d2fileformats/d2dcc/dcc_direction.go index 1735f35d..4fde58e0 100644 --- a/d2common/d2fileformats/d2dcc/dcc_direction.go +++ b/d2common/d2fileformats/d2dcc/dcc_direction.go @@ -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 } } diff --git a/d2common/d2fileformats/d2dcc/dcc_direction_frame.go b/d2common/d2fileformats/d2dcc/dcc_direction_frame.go index b3ec5cc7..d5cb69e4 100644 --- a/d2common/d2fileformats/d2dcc/dcc_direction_frame.go +++ b/d2common/d2fileformats/d2dcc/dcc_direction_frame.go @@ -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] } } diff --git a/d2common/d2fileformats/d2dcc/dcc_pixel_buffer_entry.go b/d2common/d2fileformats/d2dcc/dcc_pixel_buffer_entry.go index 3c4a0498..0a2caf52 100644 --- a/d2common/d2fileformats/d2dcc/dcc_pixel_buffer_entry.go +++ b/d2common/d2fileformats/d2dcc/dcc_pixel_buffer_entry.go @@ -1,5 +1,6 @@ package d2dcc +// DCCPixelBufferEntry represents a single entry in the pixel buffer. type DCCPixelBufferEntry struct { Value [4]byte Frame int diff --git a/d2common/d2fileformats/d2ds1/wall_record.go b/d2common/d2fileformats/d2ds1/wall_record.go index 9b9b087b..5a6571fa 100644 --- a/d2common/d2fileformats/d2ds1/wall_record.go +++ b/d2common/d2fileformats/d2ds1/wall_record.go @@ -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 diff --git a/d2common/d2fileformats/d2dt1/block.go b/d2common/d2fileformats/d2dt1/block.go index 7dfd3502..f463aae2 100644 --- a/d2common/d2fileformats/d2dt1/block.go +++ b/d2common/d2fileformats/d2dt1/block.go @@ -1,5 +1,6 @@ package d2dt1 +// Block represents a DT1 block type Block struct { X int16 Y int16 diff --git a/d2common/d2fileformats/d2dt1/dt1.go b/d2common/d2fileformats/d2dt1/dt1.go index 276e7143..5330670b 100644 --- a/d2common/d2fileformats/d2dt1/dt1.go +++ b/d2common/d2fileformats/d2dt1/dt1.go @@ -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) diff --git a/d2common/d2fileformats/d2dt1/material.go b/d2common/d2fileformats/d2dt1/material.go index b1acd540..d5708b28 100644 --- a/d2common/d2fileformats/d2dt1/material.go +++ b/d2common/d2fileformats/d2dt1/material.go @@ -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, diff --git a/d2common/d2fileformats/d2dt1/subtile.go b/d2common/d2fileformats/d2dt1/subtile.go index 723064ad..6a98ddb1 100644 --- a/d2common/d2fileformats/d2dt1/subtile.go +++ b/d2common/d2fileformats/d2dt1/subtile.go @@ -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, diff --git a/d2common/d2fileformats/d2dt1/subtile_test.go b/d2common/d2fileformats/d2dt1/subtile_test.go index 4b252b5b..b7a11775 100644 --- a/d2common/d2fileformats/d2dt1/subtile_test.go +++ b/d2common/d2fileformats/d2dt1/subtile_test.go @@ -21,5 +21,4 @@ func TestNewSubTile(t *testing.T) { assert.Equal(i == 6, tile.Unknown2) assert.Equal(i == 7, tile.Unknown3) } - } diff --git a/d2common/d2fileformats/d2mpq/crypto_buff.go b/d2common/d2fileformats/d2mpq/crypto_buff.go index b14e4c9e..2c000bf2 100644 --- a/d2common/d2fileformats/d2mpq/crypto_buff.go +++ b/d2common/d2fileformats/d2mpq/crypto_buff.go @@ -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 diff --git a/d2common/d2fileformats/d2mpq/hash_entry_map.go b/d2common/d2fileformats/d2mpq/hash_entry_map.go index 95e2078f..cdd42d2a 100644 --- a/d2common/d2fileformats/d2mpq/hash_entry_map.go +++ b/d2common/d2fileformats/d2mpq/hash_entry_map.go @@ -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 diff --git a/d2common/d2fileformats/d2mpq/mpq.go b/d2common/d2fileformats/d2mpq/mpq.go index 67cb3e18..c30e23df 100644 --- a/d2common/d2fileformats/d2mpq/mpq.go +++ b/d2common/d2fileformats/d2mpq/mpq.go @@ -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 } diff --git a/d2common/d2fileformats/d2mpq/mpq_data_stream.go b/d2common/d2fileformats/d2mpq/mpq_data_stream.go index 7bb500e3..66d73f6d 100644 --- a/d2common/d2fileformats/d2mpq/mpq_data_stream.go +++ b/d2common/d2fileformats/d2mpq/mpq_data_stream.go @@ -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 diff --git a/d2common/d2fileformats/d2mpq/mpq_file_record.go b/d2common/d2fileformats/d2mpq/mpq_file_record.go index 95e4a9ee..bc5a07b6 100644 --- a/d2common/d2fileformats/d2mpq/mpq_file_record.go +++ b/d2common/d2fileformats/d2mpq/mpq_file_record.go @@ -1,5 +1,6 @@ package d2mpq +// MpqFileRecord represents a file record in an MPQ type MpqFileRecord struct { MpqFile string IsPatch bool diff --git a/d2common/d2fileformats/d2mpq/mpq_stream.go b/d2common/d2fileformats/d2mpq/mpq_stream.go index 2a121eae..3629260d 100644 --- a/d2common/d2fileformats/d2mpq/mpq_stream.go +++ b/d2common/d2fileformats/d2mpq/mpq_stream.go @@ -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() } diff --git a/d2common/d2fileformats/d2pl2/pl2.go b/d2common/d2fileformats/d2pl2/pl2.go index 071c6e03..e1585a81 100644 --- a/d2common/d2fileformats/d2pl2/pl2.go +++ b/d2common/d2fileformats/d2pl2/pl2.go @@ -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() diff --git a/d2common/d2fileformats/d2pl2/pl2_color.go b/d2common/d2fileformats/d2pl2/pl2_color.go new file mode 100644 index 00000000..de665794 --- /dev/null +++ b/d2common/d2fileformats/d2pl2/pl2_color.go @@ -0,0 +1,9 @@ +package d2pl2 + +// PL2Color represents an RGBA color +type PL2Color struct { + R uint8 + G uint8 + B uint8 + _ uint8 +} diff --git a/d2common/d2fileformats/d2pl2/pl2_color_24bits.go b/d2common/d2fileformats/d2pl2/pl2_color_24bits.go new file mode 100644 index 00000000..514a2496 --- /dev/null +++ b/d2common/d2fileformats/d2pl2/pl2_color_24bits.go @@ -0,0 +1,8 @@ +package d2pl2 + +// PL2Color24Bits represents an RGB color +type PL2Color24Bits struct { + R uint8 + G uint8 + B uint8 +} diff --git a/d2common/d2fileformats/d2pl2/pl2_palette.go b/d2common/d2fileformats/d2pl2/pl2_palette.go new file mode 100644 index 00000000..f5b8df7e --- /dev/null +++ b/d2common/d2fileformats/d2pl2/pl2_palette.go @@ -0,0 +1,6 @@ +package d2pl2 + +// PL2Palette represents a PL2 palette. +type PL2Palette struct { + Colors [256]PL2Color +} diff --git a/d2common/d2fileformats/d2pl2/pl2_palette_transform.go b/d2common/d2fileformats/d2pl2/pl2_palette_transform.go new file mode 100644 index 00000000..dc1846d2 --- /dev/null +++ b/d2common/d2fileformats/d2pl2/pl2_palette_transform.go @@ -0,0 +1,6 @@ +package d2pl2 + +// PL2PaletteTransform represents a PL2 palette transform. +type PL2PaletteTransform struct { + Indices [256]uint8 +} diff --git a/d2core/d2asset/animation.go b/d2core/d2asset/animation.go index 6315c6ca..0dcc44fa 100644 --- a/d2core/d2asset/animation.go +++ b/d2core/d2asset/animation.go @@ -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, diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index 406a3330..b688707f 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -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) } diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index 6a4192f7..ebfebde7 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -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) } diff --git a/d2core/d2asset/palette_manager.go b/d2core/d2asset/palette_manager.go index 29371527..0983c501 100644 --- a/d2core/d2asset/palette_manager.go +++ b/d2core/d2asset/palette_manager.go @@ -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 } diff --git a/d2core/d2asset/palette_transform_manager.go b/d2core/d2asset/palette_transform_manager.go index ad48f70e..c117168a 100644 --- a/d2core/d2asset/palette_transform_manager.go +++ b/d2core/d2asset/palette_transform_manager.go @@ -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 }