/
esp.go
191 lines (165 loc) · 4.89 KB
/
esp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package fileio
// .esp - Effect sprite data
import (
"encoding/binary"
"io"
"log"
"os"
)
type ESPHeader struct {
Ids [8]uint8 // If value doesn't equal 0xff, there is an animation
}
type AnimBlockHeader struct {
NumFrames uint16
NumSprites uint16
Width uint8
Height uint8
Unknown uint16
}
type AnimFrame struct {
SpriteId uint8
Count uint8
Time uint8
SquareSide uint8 // length and width are the same
X int16
Y int16
}
// x and y are top-left position of sprite in TIM sprite sheet
// Offset is used to shift sprite frame to align it with other frames with different dimensions
type AnimSprite struct {
ImageX uint8
ImageY uint8
OffsetX int8
OffsetY int8
}
type AnimMovement struct {
FunctionId0 uint8
FunctionId1 uint8
Unknown0 [2]uint8
TranslateX uint16
TranslateY uint16
Acceleration [3]uint8
Unknown1 uint8
Speed [3]int16
Unknown2 [3]uint16
}
type ESPOutput struct {
SpriteData []SpriteData
ValidSpriteCount int
}
type SpriteData struct {
Id int
FrameData []AnimFrame
FramePositions []AnimSprite
AnimMovements []AnimMovement
ImageData *TIMOutput
}
func LoadESPFile(filename string) *ESPOutput {
espFile, _ := os.Open(filename)
defer espFile.Close()
if espFile == nil {
log.Fatal("ESP file doesn't exist: ", filename)
return nil
}
fi, err := espFile.Stat()
if err != nil {
log.Fatal(err)
return nil
}
fileLength := fi.Size()
espOutput, err := LoadESPStream(espFile, fileLength, fileLength-4)
if err != nil {
log.Fatal("Failed to load ESP file: ", err)
return nil
}
return espOutput
}
func LoadESPStream(r io.ReaderAt, fileLength int64, eofOffset int64) (*ESPOutput, error) {
streamReader := io.NewSectionReader(r, int64(0), fileLength)
// Read the header
espHeader := ESPHeader{}
if err := binary.Read(streamReader, binary.LittleEndian, &espHeader); err != nil {
return nil, err
}
// Read the offset to each block, which is stored separately
blockOffsets := make([]uint32, 0)
validSpriteCount := 0
for i := 0; i < len(espHeader.Ids); i++ {
if espHeader.Ids[i] == 255 {
continue
}
validSpriteCount++
offsetReader := io.NewSectionReader(r, eofOffset-int64(i*4), 4)
blockOffset := uint32(0)
if err := binary.Read(offsetReader, binary.LittleEndian, &blockOffset); err != nil {
return nil, err
}
blockOffsets = append(blockOffsets, blockOffset)
}
spriteData := make([]SpriteData, len(blockOffsets))
for i, blockOffset := range blockOffsets {
animationDataOffset := int64(blockOffset)
animationDataReader := io.NewSectionReader(r, animationDataOffset, fileLength-int64(animationDataOffset))
animBlockHeader := AnimBlockHeader{}
if err := binary.Read(animationDataReader, binary.LittleEndian, &animBlockHeader); err != nil {
return nil, err
}
// Read animation frames
frames := make([]AnimFrame, int(animBlockHeader.NumFrames))
for j := 0; j < int(animBlockHeader.NumFrames); j++ {
animFrame := AnimFrame{}
if err := binary.Read(animationDataReader, binary.LittleEndian, &animFrame); err != nil {
return nil, err
}
frames[j] = animFrame
}
// Read animation sprites
positions := make([]AnimSprite, int(animBlockHeader.NumSprites))
for j := 0; j < int(animBlockHeader.NumSprites); j++ {
animSprite := AnimSprite{}
if err := binary.Read(animationDataReader, binary.LittleEndian, &animSprite); err != nil {
return nil, err
}
positions[j] = animSprite
}
// Read animation movement
movementDataOffsets := make([]uint16, 8)
if err := binary.Read(animationDataReader, binary.LittleEndian, &movementDataOffsets); err != nil {
return nil, err
}
animBlockHeaderSize := 8
frameBlockSize := 8 * int(animBlockHeader.NumFrames)
spriteBlockSize := 4 * int(animBlockHeader.NumSprites)
movementReaderOffset := animationDataOffset + int64(animBlockHeaderSize+frameBlockSize+spriteBlockSize)
animMovements := make([]AnimMovement, 0)
for j := 0; j < 8; j++ {
if movementDataOffsets[j] == 0 {
break
}
newOffset := movementReaderOffset + int64(movementDataOffsets[j]*4)
movementReader := io.NewSectionReader(r, newOffset, fileLength-newOffset)
movementCounts := make([]uint16, 2)
if err := binary.Read(movementReader, binary.LittleEndian, &movementCounts); err != nil {
return nil, err
}
movementData := AnimMovement{}
if err := binary.Read(movementReader, binary.LittleEndian, &movementData); err != nil {
return nil, err
}
animMovements = append(animMovements, movementData)
// TODO: Figure out the remaining offsets of the block
}
spriteData[i] = SpriteData{
Id: int(espHeader.Ids[i]),
FrameData: frames,
FramePositions: positions,
AnimMovements: animMovements,
ImageData: nil, // needs to be loaded separately
}
}
output := &ESPOutput{
SpriteData: spriteData,
ValidSpriteCount: validSpriteCount,
}
return output, nil
}