mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 23:47:16 -05:00
Fixed lint issues (#484)
This commit is contained in:
parent
09a28c2822
commit
255ffc75da
@ -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
|
||||
}
|
||||
|
@ -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
|
@ -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++ {
|
8
d2common/d2fileformats/d2dat/dat_color.go
Normal file
8
d2common/d2fileformats/d2dat/dat_color.go
Normal 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
|
||||
}
|
6
d2common/d2fileformats/d2dat/dat_palette.go
Normal file
6
d2common/d2fileformats/d2dat/dat_palette.go
Normal file
@ -0,0 +1,6 @@
|
||||
package d2dat
|
||||
|
||||
// DATPalette represents a 256 color palette.
|
||||
type DATPalette struct {
|
||||
Colors [256]DATColor
|
||||
}
|
@ -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
|
||||
}
|
||||
|
15
d2common/d2fileformats/d2dc6/dc6_frame.go
Normal file
15
d2common/d2fileformats/d2dc6/dc6_frame.go
Normal 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"`
|
||||
}
|
13
d2common/d2fileformats/d2dc6/dc6_frame_header.go
Normal file
13
d2common/d2fileformats/d2dc6/dc6_frame_header.go
Normal 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"`
|
||||
}
|
11
d2common/d2fileformats/d2dc6/dc6_header.go
Normal file
11
d2common/d2fileformats/d2dc6/dc6_header.go
Normal 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"`
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package d2dcc
|
||||
|
||||
// DCCCell represents a single cell in a DCC file.
|
||||
type DCCCell struct {
|
||||
Width int
|
||||
Height int
|
||||
|
56
d2common/d2fileformats/d2dcc/dcc_dir_lookup.go
Normal file
56
d2common/d2fileformats/d2dcc/dcc_dir_lookup.go
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package d2dcc
|
||||
|
||||
// DCCPixelBufferEntry represents a single entry in the pixel buffer.
|
||||
type DCCPixelBufferEntry struct {
|
||||
Value [4]byte
|
||||
Frame int
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
package d2dt1
|
||||
|
||||
// Block represents a DT1 block
|
||||
type Block struct {
|
||||
X int16
|
||||
Y int16
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -21,5 +21,4 @@ func TestNewSubTile(t *testing.T) {
|
||||
assert.Equal(i == 6, tile.Unknown2)
|
||||
assert.Equal(i == 7, tile.Unknown3)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
package d2mpq
|
||||
|
||||
// MpqFileRecord represents a file record in an MPQ
|
||||
type MpqFileRecord struct {
|
||||
MpqFile string
|
||||
IsPatch bool
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
9
d2common/d2fileformats/d2pl2/pl2_color.go
Normal file
9
d2common/d2fileformats/d2pl2/pl2_color.go
Normal file
@ -0,0 +1,9 @@
|
||||
package d2pl2
|
||||
|
||||
// PL2Color represents an RGBA color
|
||||
type PL2Color struct {
|
||||
R uint8
|
||||
G uint8
|
||||
B uint8
|
||||
_ uint8
|
||||
}
|
8
d2common/d2fileformats/d2pl2/pl2_color_24bits.go
Normal file
8
d2common/d2fileformats/d2pl2/pl2_color_24bits.go
Normal file
@ -0,0 +1,8 @@
|
||||
package d2pl2
|
||||
|
||||
// PL2Color24Bits represents an RGB color
|
||||
type PL2Color24Bits struct {
|
||||
R uint8
|
||||
G uint8
|
||||
B uint8
|
||||
}
|
6
d2common/d2fileformats/d2pl2/pl2_palette.go
Normal file
6
d2common/d2fileformats/d2pl2/pl2_palette.go
Normal file
@ -0,0 +1,6 @@
|
||||
package d2pl2
|
||||
|
||||
// PL2Palette represents a PL2 palette.
|
||||
type PL2Palette struct {
|
||||
Colors [256]PL2Color
|
||||
}
|
6
d2common/d2fileformats/d2pl2/pl2_palette_transform.go
Normal file
6
d2common/d2fileformats/d2pl2/pl2_palette_transform.go
Normal file
@ -0,0 +1,6 @@
|
||||
package d2pl2
|
||||
|
||||
// PL2PaletteTransform represents a PL2 palette transform.
|
||||
type PL2PaletteTransform struct {
|
||||
Indices [256]uint8
|
||||
}
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user