/
replay.go
178 lines (152 loc) · 3.4 KB
/
replay.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
// Package replay implements decoders for replays (Starcraft, Heroes
// of the Storm etc.).
package replay
import (
"bufio"
"fmt"
"io"
"log"
"blizzard/blizzval"
"blizzard/mpq"
)
type Player struct {
Raw blizzval.Value
Name string "0"
// bnet region "1"
// starcraft race "2"
// color "3"
Team int64 "5"
Handicap int64 "6"
// unknown "7"
IsWinner int64 "8"
// unknown "9"
Character string "10"
}
type Details struct {
Raw blizzval.Value
Players []*Player "0"
Map string "1"
// 2 unknown empty string
// 3 preview image(?)
// 4 always 1 ?
TimeStamp int64 "5"
UTCOffset int64 "6"
// And a bunch more mystery fields...
}
func readReplayDetails(r *mpq.Reader) {
fr := r.OpenFile("replay.details")
e := blizzval.Read(bufio.NewReader(fr))
// printTrackerEvent(e, 0)
// fmt.Printf("\n")
d := readDetails(e)
log.Printf("%#v", d)
// for _, p := range d.Players {
// log.Printf("%#v", p)
// }
}
type lr struct {
r io.Reader
}
func (r *lr) Read(buf []byte) (int, error) {
n, err := r.r.Read(buf)
log.Printf("read %d %x", n, buf[:n])
return n, err
}
type TrackerEventType int
//go:generate stringer -type=TrackerEventType
const (
EventPlayerStats TrackerEventType = 0
EventUnitBorn TrackerEventType = 1
EventUnitDied TrackerEventType = 2
EventOwnerChange TrackerEventType = 3
EventTypeChange TrackerEventType = 4
EventUnknown1 TrackerEventType = 5
EventUnitPositions TrackerEventType = 8
EventPlayerSetup TrackerEventType = 9
)
type TrackerEvent struct {
Frame int64
Type TrackerEventType
Val blizzval.Value
}
func readReplayTrackerEvents(mpqr *mpq.Reader) {
r := mpqr.OpenFile("replay.tracker.events")
for i := 0; i < 10000; i++ {
te := &TrackerEvent{}
var buf [3]byte
_, err := r.Read(buf[:3])
if err != nil {
if err == io.EOF {
return
}
panic(err)
}
if string(buf[:3]) != "\x03\x00\x09" &&
string(buf[:3]) != "\x03\x02\x09" {
panic(fmt.Errorf("unexpected event head %x", buf[:3]))
}
te.Frame = blizzval.ReadVarInt(r)
_, err = r.Read(buf[:1])
if err != nil {
panic(err)
}
if string(buf[:1]) != "\x09" {
panic("unexpected event trailer")
}
te.Type = TrackerEventType(blizzval.ReadVarInt(r))
te.Val = blizzval.Read(r)
log.Printf("%d %+v", i, te)
}
}
func readBits(r *bitReader, n int) uint64 {
bits, err := r.ReadBits(n)
if err != nil {
panic(err)
}
return bits
}
func decodeGameLoopDelta(r *bitReader) (int, error) {
tag, err := r.ReadBits(2)
if err != nil {
return 0, err
}
switch tag {
case 0: // m_uint6
return int(readBits(r, 6)), nil
case 1: // m_uint14
return int(readBits(r, 14)), nil
case 2: // m_uint22
return int(readBits(r, 22)), nil
case 3: // m_uint32
return int(readBits(r, 32)), nil
default:
panic(fmt.Errorf("unknown choice tag %d", tag))
}
}
type GameEventReader struct {
r *bitReader
gameLoop int
}
func NewGameEventReader(mpqr *mpq.Reader) *GameEventReader {
fr := mpqr.OpenFile("replay.game.events")
r := newBitReader(bufio.NewReader(fr))
return &GameEventReader{r: r}
}
func (r *GameEventReader) Read() Event {
delta, err := decodeGameLoopDelta(r.r)
if err != nil {
if err == io.EOF {
return nil
}
panic(err)
}
r.gameLoop += delta
userId := int(readBits(r.r, 5))
eventId := int(readBits(r.r, 7))
event := readGameEvent(r.r, eventId)
r.r.SyncToByte()
meta := event.Meta()
meta.GameLoop = r.gameLoop
meta.UserId = userId
return event
}