@astefanski/storm-parser 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +246 -14
- package/dist/index.d.ts +246 -14
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -36,39 +36,271 @@ declare class ReplayParser {
|
|
|
36
36
|
getTrackerEvents(): ReplayEvent[];
|
|
37
37
|
getGameEvents(): ReplayEvent[];
|
|
38
38
|
extractFile(filename: string): Buffer | null;
|
|
39
|
+
getHeader(): Record<string, unknown> | undefined;
|
|
40
|
+
getBuild(): number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
interface
|
|
43
|
+
interface ReplayVersion {
|
|
44
|
+
m_flags: number;
|
|
45
|
+
m_major: number;
|
|
46
|
+
m_minor: number;
|
|
47
|
+
m_revision: number;
|
|
48
|
+
m_build: number;
|
|
49
|
+
m_baseBuild: number;
|
|
50
|
+
}
|
|
51
|
+
interface KillParticipant {
|
|
52
|
+
player: string;
|
|
42
53
|
hero: string;
|
|
54
|
+
}
|
|
55
|
+
interface TakedownEvent {
|
|
56
|
+
loop: number;
|
|
57
|
+
time: number;
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
killers: KillParticipant[];
|
|
61
|
+
victim: KillParticipant;
|
|
62
|
+
}
|
|
63
|
+
interface LevelTime {
|
|
64
|
+
loop: number;
|
|
65
|
+
level: number;
|
|
66
|
+
team: string;
|
|
67
|
+
time: number;
|
|
68
|
+
}
|
|
69
|
+
interface BanEntry {
|
|
70
|
+
hero: string;
|
|
71
|
+
order: number;
|
|
72
|
+
absolute: number;
|
|
73
|
+
}
|
|
74
|
+
interface PickData {
|
|
75
|
+
0: string[];
|
|
76
|
+
1: string[];
|
|
77
|
+
first: number;
|
|
78
|
+
}
|
|
79
|
+
interface XPValues {
|
|
80
|
+
GameTime: number;
|
|
81
|
+
PreviousGameTime: number;
|
|
82
|
+
MinionXP: number;
|
|
83
|
+
CreepXP: number;
|
|
84
|
+
StructureXP: number;
|
|
85
|
+
HeroXP: number;
|
|
86
|
+
TrickleXP: number;
|
|
87
|
+
}
|
|
88
|
+
interface XPBreakdownEntry {
|
|
89
|
+
loop: number;
|
|
90
|
+
time: number;
|
|
91
|
+
team: number;
|
|
92
|
+
teamLevel?: number;
|
|
93
|
+
breakdown: XPValues;
|
|
94
|
+
theoreticalMinionXP: number;
|
|
95
|
+
}
|
|
96
|
+
interface MercCapture {
|
|
97
|
+
loop: number;
|
|
98
|
+
type: string;
|
|
99
|
+
team: number;
|
|
100
|
+
time: number;
|
|
101
|
+
}
|
|
102
|
+
interface MercUnitLocation {
|
|
103
|
+
x: number;
|
|
104
|
+
y: number;
|
|
105
|
+
}
|
|
106
|
+
interface MercUnit {
|
|
107
|
+
loop: number;
|
|
108
|
+
team: number;
|
|
109
|
+
type: string;
|
|
110
|
+
locations: MercUnitLocation[];
|
|
111
|
+
time: number;
|
|
112
|
+
duration: number;
|
|
113
|
+
}
|
|
114
|
+
interface MercsData {
|
|
115
|
+
captures: MercCapture[];
|
|
116
|
+
units: Record<string, MercUnit>;
|
|
117
|
+
}
|
|
118
|
+
interface StructureInfo {
|
|
119
|
+
type: string;
|
|
43
120
|
name: string;
|
|
44
|
-
tag:
|
|
121
|
+
tag: number;
|
|
122
|
+
rtag: number;
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
45
125
|
team: number;
|
|
46
|
-
|
|
47
|
-
|
|
126
|
+
destroyedLoop?: number;
|
|
127
|
+
destroyed?: number;
|
|
128
|
+
}
|
|
129
|
+
interface ObjectiveEvent {
|
|
130
|
+
team: number;
|
|
131
|
+
score?: number;
|
|
132
|
+
loop: number;
|
|
133
|
+
time: number;
|
|
134
|
+
duration?: number;
|
|
135
|
+
endLoop?: number;
|
|
136
|
+
end?: number;
|
|
137
|
+
}
|
|
138
|
+
interface TeamObjective {
|
|
139
|
+
count: number;
|
|
140
|
+
events: ObjectiveEvent[];
|
|
141
|
+
}
|
|
142
|
+
interface ObjectiveData {
|
|
143
|
+
0: TeamObjective;
|
|
144
|
+
1: TeamObjective;
|
|
145
|
+
type: string;
|
|
146
|
+
}
|
|
147
|
+
interface StructureStats {
|
|
148
|
+
lost: number;
|
|
149
|
+
destroyed: number;
|
|
150
|
+
first: number;
|
|
151
|
+
}
|
|
152
|
+
interface UptimeEntry {
|
|
153
|
+
time: number;
|
|
154
|
+
heroes: number;
|
|
155
|
+
}
|
|
156
|
+
interface TeamTotals {
|
|
157
|
+
DamageTaken: number;
|
|
158
|
+
CreepDamage: number;
|
|
159
|
+
Healing: number;
|
|
160
|
+
HeroDamage: number;
|
|
161
|
+
MinionDamage: number;
|
|
162
|
+
SelfHealing: number;
|
|
163
|
+
SiegeDamage: number;
|
|
164
|
+
ProtectionGivenToAllies: number;
|
|
165
|
+
TeamfightDamageTaken: number;
|
|
166
|
+
TeamfightHealingDone: number;
|
|
167
|
+
TeamfightHeroDamage: number;
|
|
168
|
+
TimeCCdEnemyHeroes: number;
|
|
169
|
+
TimeRootingEnemyHeroes: number;
|
|
170
|
+
TimeSpentDead: number;
|
|
171
|
+
TimeStunningEnemyHeroes: number;
|
|
172
|
+
TimeSilencingEnemyHeroes: number;
|
|
173
|
+
avgTimeSpentDead: number;
|
|
174
|
+
timeDeadPct: number;
|
|
175
|
+
}
|
|
176
|
+
interface TeamStats {
|
|
177
|
+
mercCaptures: number;
|
|
178
|
+
mercUptime: number;
|
|
179
|
+
mercUptimePercent: number;
|
|
180
|
+
structures: Record<string, StructureStats>;
|
|
181
|
+
KDA: number;
|
|
182
|
+
PPK: number;
|
|
183
|
+
timeTo10: number;
|
|
184
|
+
totals: TeamTotals;
|
|
185
|
+
levelAdvTime: number;
|
|
186
|
+
maxLevelAdv: number;
|
|
187
|
+
avgLevelAdv: number;
|
|
188
|
+
levelAdvPct: number;
|
|
189
|
+
uptime: UptimeEntry[];
|
|
190
|
+
uptimeHistogram: Record<string, number>;
|
|
191
|
+
wipes: number;
|
|
192
|
+
avgHeroesAlive: number;
|
|
193
|
+
aces: number;
|
|
194
|
+
timeWithHeroAdv: number;
|
|
195
|
+
pctWithHeroAdv: number;
|
|
196
|
+
passiveXPRate: number;
|
|
197
|
+
passiveXPDiff: number;
|
|
198
|
+
passiveXPGain: number;
|
|
48
199
|
}
|
|
49
200
|
interface TeamStat {
|
|
50
201
|
level: number;
|
|
51
202
|
takedowns: number;
|
|
52
203
|
ids: string[];
|
|
204
|
+
names: string[];
|
|
205
|
+
heroes: string[];
|
|
206
|
+
tags: number[];
|
|
207
|
+
stats: TeamStats;
|
|
208
|
+
}
|
|
209
|
+
interface LevelAdvSegment {
|
|
210
|
+
start: number;
|
|
211
|
+
end: number;
|
|
212
|
+
levelDiff: number;
|
|
213
|
+
length: number;
|
|
214
|
+
}
|
|
215
|
+
interface TalentChoices {
|
|
216
|
+
Tier1Choice?: string;
|
|
217
|
+
Tier2Choice?: string;
|
|
218
|
+
Tier3Choice?: string;
|
|
219
|
+
Tier4Choice?: string;
|
|
220
|
+
Tier5Choice?: string;
|
|
221
|
+
Tier6Choice?: string;
|
|
222
|
+
Tier7Choice?: string;
|
|
223
|
+
}
|
|
224
|
+
interface UnitPosition {
|
|
225
|
+
x: number;
|
|
226
|
+
y: number;
|
|
227
|
+
time: number;
|
|
228
|
+
}
|
|
229
|
+
interface UnitLife {
|
|
230
|
+
born: number;
|
|
231
|
+
locations: UnitPosition[];
|
|
232
|
+
died?: number;
|
|
233
|
+
duration: number;
|
|
234
|
+
}
|
|
235
|
+
interface PlayerUnit {
|
|
236
|
+
lives: UnitLife[];
|
|
237
|
+
}
|
|
238
|
+
interface PlayerStat {
|
|
239
|
+
hero: string;
|
|
240
|
+
name: string;
|
|
241
|
+
uuid: number;
|
|
242
|
+
region: number;
|
|
243
|
+
realm: number;
|
|
244
|
+
ToonHandle: string;
|
|
245
|
+
tag: number;
|
|
246
|
+
team: number;
|
|
247
|
+
win: boolean;
|
|
248
|
+
gameStats: Record<string, number>;
|
|
249
|
+
awards: string[];
|
|
250
|
+
talents: TalentChoices;
|
|
251
|
+
takedowns: TakedownEvent[];
|
|
252
|
+
deaths: TakedownEvent[];
|
|
253
|
+
units: Record<string, PlayerUnit>;
|
|
53
254
|
}
|
|
54
255
|
interface MatchStat {
|
|
256
|
+
version: ReplayVersion;
|
|
257
|
+
type?: number;
|
|
258
|
+
mode?: number;
|
|
55
259
|
map?: string;
|
|
56
260
|
date: string;
|
|
261
|
+
rawDate?: number;
|
|
57
262
|
length: number;
|
|
58
263
|
winner: number;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
264
|
+
region?: number;
|
|
265
|
+
loopLength?: number;
|
|
266
|
+
loopGameStart?: number;
|
|
267
|
+
playerIDs: string[];
|
|
268
|
+
heroes: string[];
|
|
269
|
+
levelTimes: Record<string, Record<string, LevelTime>>;
|
|
270
|
+
bans: Record<string, BanEntry[]>;
|
|
271
|
+
picks: PickData;
|
|
272
|
+
XPBreakdown: XPBreakdownEntry[];
|
|
273
|
+
takedowns: TakedownEvent[];
|
|
274
|
+
mercs: MercsData;
|
|
275
|
+
team0Takedowns: number;
|
|
276
|
+
team1Takedowns: number;
|
|
277
|
+
structures: Record<string, StructureInfo>;
|
|
278
|
+
objective: ObjectiveData;
|
|
62
279
|
teams: Record<string, TeamStat>;
|
|
280
|
+
winningPlayers: string[];
|
|
281
|
+
levelAdvTimeline: LevelAdvSegment[];
|
|
282
|
+
firstPickWin: boolean;
|
|
283
|
+
firstObjective?: number;
|
|
284
|
+
firstObjectiveWin?: boolean;
|
|
285
|
+
firstFort?: number;
|
|
286
|
+
firstKeep?: number;
|
|
287
|
+
firstFortWin?: boolean;
|
|
288
|
+
firstKeepWin?: boolean;
|
|
63
289
|
}
|
|
290
|
+
interface AnalysisResult {
|
|
291
|
+
status: number;
|
|
292
|
+
match?: MatchStat;
|
|
293
|
+
players?: Record<string, PlayerStat>;
|
|
294
|
+
error?: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
64
297
|
declare class ReplayAnalyzer {
|
|
65
|
-
static analyze(filePath: string): Promise<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}>;
|
|
298
|
+
static analyze(filePath: string): Promise<AnalysisResult>;
|
|
299
|
+
private static emptyTeam;
|
|
300
|
+
private static extractBattleTags;
|
|
301
|
+
private static extractDraft;
|
|
302
|
+
private static processScoreEvents;
|
|
71
303
|
private static fileTimeToDate;
|
|
72
304
|
}
|
|
73
305
|
|
|
74
|
-
export { type Protocol, ReplayAnalyzer, type ReplayEvent, ReplayParser };
|
|
306
|
+
export { type AnalysisResult, type BanEntry, type KillParticipant, type LevelAdvSegment, type LevelTime, type MatchStat, type MercCapture, type MercUnit, type MercUnitLocation, type MercsData, type ObjectiveData, type ObjectiveEvent, type PickData, type PlayerStat, type PlayerUnit, type Protocol, ReplayAnalyzer, type ReplayEvent, ReplayParser, type ReplayVersion, type StructureInfo, type StructureStats, type TakedownEvent, type TalentChoices, type TeamObjective, type TeamStat, type TeamStats, type TeamTotals, type UnitLife, type UnitPosition, type UptimeEntry, type XPBreakdownEntry, type XPValues };
|
package/dist/index.d.ts
CHANGED
|
@@ -36,39 +36,271 @@ declare class ReplayParser {
|
|
|
36
36
|
getTrackerEvents(): ReplayEvent[];
|
|
37
37
|
getGameEvents(): ReplayEvent[];
|
|
38
38
|
extractFile(filename: string): Buffer | null;
|
|
39
|
+
getHeader(): Record<string, unknown> | undefined;
|
|
40
|
+
getBuild(): number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
interface
|
|
43
|
+
interface ReplayVersion {
|
|
44
|
+
m_flags: number;
|
|
45
|
+
m_major: number;
|
|
46
|
+
m_minor: number;
|
|
47
|
+
m_revision: number;
|
|
48
|
+
m_build: number;
|
|
49
|
+
m_baseBuild: number;
|
|
50
|
+
}
|
|
51
|
+
interface KillParticipant {
|
|
52
|
+
player: string;
|
|
42
53
|
hero: string;
|
|
54
|
+
}
|
|
55
|
+
interface TakedownEvent {
|
|
56
|
+
loop: number;
|
|
57
|
+
time: number;
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
killers: KillParticipant[];
|
|
61
|
+
victim: KillParticipant;
|
|
62
|
+
}
|
|
63
|
+
interface LevelTime {
|
|
64
|
+
loop: number;
|
|
65
|
+
level: number;
|
|
66
|
+
team: string;
|
|
67
|
+
time: number;
|
|
68
|
+
}
|
|
69
|
+
interface BanEntry {
|
|
70
|
+
hero: string;
|
|
71
|
+
order: number;
|
|
72
|
+
absolute: number;
|
|
73
|
+
}
|
|
74
|
+
interface PickData {
|
|
75
|
+
0: string[];
|
|
76
|
+
1: string[];
|
|
77
|
+
first: number;
|
|
78
|
+
}
|
|
79
|
+
interface XPValues {
|
|
80
|
+
GameTime: number;
|
|
81
|
+
PreviousGameTime: number;
|
|
82
|
+
MinionXP: number;
|
|
83
|
+
CreepXP: number;
|
|
84
|
+
StructureXP: number;
|
|
85
|
+
HeroXP: number;
|
|
86
|
+
TrickleXP: number;
|
|
87
|
+
}
|
|
88
|
+
interface XPBreakdownEntry {
|
|
89
|
+
loop: number;
|
|
90
|
+
time: number;
|
|
91
|
+
team: number;
|
|
92
|
+
teamLevel?: number;
|
|
93
|
+
breakdown: XPValues;
|
|
94
|
+
theoreticalMinionXP: number;
|
|
95
|
+
}
|
|
96
|
+
interface MercCapture {
|
|
97
|
+
loop: number;
|
|
98
|
+
type: string;
|
|
99
|
+
team: number;
|
|
100
|
+
time: number;
|
|
101
|
+
}
|
|
102
|
+
interface MercUnitLocation {
|
|
103
|
+
x: number;
|
|
104
|
+
y: number;
|
|
105
|
+
}
|
|
106
|
+
interface MercUnit {
|
|
107
|
+
loop: number;
|
|
108
|
+
team: number;
|
|
109
|
+
type: string;
|
|
110
|
+
locations: MercUnitLocation[];
|
|
111
|
+
time: number;
|
|
112
|
+
duration: number;
|
|
113
|
+
}
|
|
114
|
+
interface MercsData {
|
|
115
|
+
captures: MercCapture[];
|
|
116
|
+
units: Record<string, MercUnit>;
|
|
117
|
+
}
|
|
118
|
+
interface StructureInfo {
|
|
119
|
+
type: string;
|
|
43
120
|
name: string;
|
|
44
|
-
tag:
|
|
121
|
+
tag: number;
|
|
122
|
+
rtag: number;
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
45
125
|
team: number;
|
|
46
|
-
|
|
47
|
-
|
|
126
|
+
destroyedLoop?: number;
|
|
127
|
+
destroyed?: number;
|
|
128
|
+
}
|
|
129
|
+
interface ObjectiveEvent {
|
|
130
|
+
team: number;
|
|
131
|
+
score?: number;
|
|
132
|
+
loop: number;
|
|
133
|
+
time: number;
|
|
134
|
+
duration?: number;
|
|
135
|
+
endLoop?: number;
|
|
136
|
+
end?: number;
|
|
137
|
+
}
|
|
138
|
+
interface TeamObjective {
|
|
139
|
+
count: number;
|
|
140
|
+
events: ObjectiveEvent[];
|
|
141
|
+
}
|
|
142
|
+
interface ObjectiveData {
|
|
143
|
+
0: TeamObjective;
|
|
144
|
+
1: TeamObjective;
|
|
145
|
+
type: string;
|
|
146
|
+
}
|
|
147
|
+
interface StructureStats {
|
|
148
|
+
lost: number;
|
|
149
|
+
destroyed: number;
|
|
150
|
+
first: number;
|
|
151
|
+
}
|
|
152
|
+
interface UptimeEntry {
|
|
153
|
+
time: number;
|
|
154
|
+
heroes: number;
|
|
155
|
+
}
|
|
156
|
+
interface TeamTotals {
|
|
157
|
+
DamageTaken: number;
|
|
158
|
+
CreepDamage: number;
|
|
159
|
+
Healing: number;
|
|
160
|
+
HeroDamage: number;
|
|
161
|
+
MinionDamage: number;
|
|
162
|
+
SelfHealing: number;
|
|
163
|
+
SiegeDamage: number;
|
|
164
|
+
ProtectionGivenToAllies: number;
|
|
165
|
+
TeamfightDamageTaken: number;
|
|
166
|
+
TeamfightHealingDone: number;
|
|
167
|
+
TeamfightHeroDamage: number;
|
|
168
|
+
TimeCCdEnemyHeroes: number;
|
|
169
|
+
TimeRootingEnemyHeroes: number;
|
|
170
|
+
TimeSpentDead: number;
|
|
171
|
+
TimeStunningEnemyHeroes: number;
|
|
172
|
+
TimeSilencingEnemyHeroes: number;
|
|
173
|
+
avgTimeSpentDead: number;
|
|
174
|
+
timeDeadPct: number;
|
|
175
|
+
}
|
|
176
|
+
interface TeamStats {
|
|
177
|
+
mercCaptures: number;
|
|
178
|
+
mercUptime: number;
|
|
179
|
+
mercUptimePercent: number;
|
|
180
|
+
structures: Record<string, StructureStats>;
|
|
181
|
+
KDA: number;
|
|
182
|
+
PPK: number;
|
|
183
|
+
timeTo10: number;
|
|
184
|
+
totals: TeamTotals;
|
|
185
|
+
levelAdvTime: number;
|
|
186
|
+
maxLevelAdv: number;
|
|
187
|
+
avgLevelAdv: number;
|
|
188
|
+
levelAdvPct: number;
|
|
189
|
+
uptime: UptimeEntry[];
|
|
190
|
+
uptimeHistogram: Record<string, number>;
|
|
191
|
+
wipes: number;
|
|
192
|
+
avgHeroesAlive: number;
|
|
193
|
+
aces: number;
|
|
194
|
+
timeWithHeroAdv: number;
|
|
195
|
+
pctWithHeroAdv: number;
|
|
196
|
+
passiveXPRate: number;
|
|
197
|
+
passiveXPDiff: number;
|
|
198
|
+
passiveXPGain: number;
|
|
48
199
|
}
|
|
49
200
|
interface TeamStat {
|
|
50
201
|
level: number;
|
|
51
202
|
takedowns: number;
|
|
52
203
|
ids: string[];
|
|
204
|
+
names: string[];
|
|
205
|
+
heroes: string[];
|
|
206
|
+
tags: number[];
|
|
207
|
+
stats: TeamStats;
|
|
208
|
+
}
|
|
209
|
+
interface LevelAdvSegment {
|
|
210
|
+
start: number;
|
|
211
|
+
end: number;
|
|
212
|
+
levelDiff: number;
|
|
213
|
+
length: number;
|
|
214
|
+
}
|
|
215
|
+
interface TalentChoices {
|
|
216
|
+
Tier1Choice?: string;
|
|
217
|
+
Tier2Choice?: string;
|
|
218
|
+
Tier3Choice?: string;
|
|
219
|
+
Tier4Choice?: string;
|
|
220
|
+
Tier5Choice?: string;
|
|
221
|
+
Tier6Choice?: string;
|
|
222
|
+
Tier7Choice?: string;
|
|
223
|
+
}
|
|
224
|
+
interface UnitPosition {
|
|
225
|
+
x: number;
|
|
226
|
+
y: number;
|
|
227
|
+
time: number;
|
|
228
|
+
}
|
|
229
|
+
interface UnitLife {
|
|
230
|
+
born: number;
|
|
231
|
+
locations: UnitPosition[];
|
|
232
|
+
died?: number;
|
|
233
|
+
duration: number;
|
|
234
|
+
}
|
|
235
|
+
interface PlayerUnit {
|
|
236
|
+
lives: UnitLife[];
|
|
237
|
+
}
|
|
238
|
+
interface PlayerStat {
|
|
239
|
+
hero: string;
|
|
240
|
+
name: string;
|
|
241
|
+
uuid: number;
|
|
242
|
+
region: number;
|
|
243
|
+
realm: number;
|
|
244
|
+
ToonHandle: string;
|
|
245
|
+
tag: number;
|
|
246
|
+
team: number;
|
|
247
|
+
win: boolean;
|
|
248
|
+
gameStats: Record<string, number>;
|
|
249
|
+
awards: string[];
|
|
250
|
+
talents: TalentChoices;
|
|
251
|
+
takedowns: TakedownEvent[];
|
|
252
|
+
deaths: TakedownEvent[];
|
|
253
|
+
units: Record<string, PlayerUnit>;
|
|
53
254
|
}
|
|
54
255
|
interface MatchStat {
|
|
256
|
+
version: ReplayVersion;
|
|
257
|
+
type?: number;
|
|
258
|
+
mode?: number;
|
|
55
259
|
map?: string;
|
|
56
260
|
date: string;
|
|
261
|
+
rawDate?: number;
|
|
57
262
|
length: number;
|
|
58
263
|
winner: number;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
264
|
+
region?: number;
|
|
265
|
+
loopLength?: number;
|
|
266
|
+
loopGameStart?: number;
|
|
267
|
+
playerIDs: string[];
|
|
268
|
+
heroes: string[];
|
|
269
|
+
levelTimes: Record<string, Record<string, LevelTime>>;
|
|
270
|
+
bans: Record<string, BanEntry[]>;
|
|
271
|
+
picks: PickData;
|
|
272
|
+
XPBreakdown: XPBreakdownEntry[];
|
|
273
|
+
takedowns: TakedownEvent[];
|
|
274
|
+
mercs: MercsData;
|
|
275
|
+
team0Takedowns: number;
|
|
276
|
+
team1Takedowns: number;
|
|
277
|
+
structures: Record<string, StructureInfo>;
|
|
278
|
+
objective: ObjectiveData;
|
|
62
279
|
teams: Record<string, TeamStat>;
|
|
280
|
+
winningPlayers: string[];
|
|
281
|
+
levelAdvTimeline: LevelAdvSegment[];
|
|
282
|
+
firstPickWin: boolean;
|
|
283
|
+
firstObjective?: number;
|
|
284
|
+
firstObjectiveWin?: boolean;
|
|
285
|
+
firstFort?: number;
|
|
286
|
+
firstKeep?: number;
|
|
287
|
+
firstFortWin?: boolean;
|
|
288
|
+
firstKeepWin?: boolean;
|
|
63
289
|
}
|
|
290
|
+
interface AnalysisResult {
|
|
291
|
+
status: number;
|
|
292
|
+
match?: MatchStat;
|
|
293
|
+
players?: Record<string, PlayerStat>;
|
|
294
|
+
error?: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
64
297
|
declare class ReplayAnalyzer {
|
|
65
|
-
static analyze(filePath: string): Promise<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}>;
|
|
298
|
+
static analyze(filePath: string): Promise<AnalysisResult>;
|
|
299
|
+
private static emptyTeam;
|
|
300
|
+
private static extractBattleTags;
|
|
301
|
+
private static extractDraft;
|
|
302
|
+
private static processScoreEvents;
|
|
71
303
|
private static fileTimeToDate;
|
|
72
304
|
}
|
|
73
305
|
|
|
74
|
-
export { type Protocol, ReplayAnalyzer, type ReplayEvent, ReplayParser };
|
|
306
|
+
export { type AnalysisResult, type BanEntry, type KillParticipant, type LevelAdvSegment, type LevelTime, type MatchStat, type MercCapture, type MercUnit, type MercUnitLocation, type MercsData, type ObjectiveData, type ObjectiveEvent, type PickData, type PlayerStat, type PlayerUnit, type Protocol, ReplayAnalyzer, type ReplayEvent, ReplayParser, type ReplayVersion, type StructureInfo, type StructureStats, type TakedownEvent, type TalentChoices, type TeamObjective, type TeamStat, type TeamStats, type TeamTotals, type UnitLife, type UnitPosition, type UptimeEntry, type XPBreakdownEntry, type XPValues };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`):this.files=null}else this.files=null}readHeader(){let e=this.file.toString("utf8",0,4),t;if(e==="MPQ")t=this.readMPQHeader(),t.offset=0;else if(e==="MPQ\x1B"){let r=this.readMPQUserDataHeader();t=this.readMPQHeader(r.mpqHeaderOffset),t.offset=r.mpqHeaderOffset,t.userDataHeader=r}else throw new Error("Invalid MPQ file header");return t}readMPQHeader(e=0){let t=this.file.subarray(e,e+32),r={magic:t.toString("utf8",0,4),headerSize:t.readUInt32LE(4),archiveSize:t.readUInt32LE(8),formatVersion:t.readUInt16LE(12),sectorSizeShift:t.readUInt16LE(14),hashTableOffset:t.readUInt32LE(16),blockTableOffset:t.readUInt32LE(20),hashTableEntries:t.readUInt32LE(24),blockTableEntries:t.readUInt32LE(28)};if(r.formatVersion===1){let n=this.file.subarray(e+32,e+32+12);r.extendedBlockTableOffset=n.readUInt32LE(0)+n.readUInt32LE(4)*4294967296,r.hashTableOffsetHigh=n.readInt8(8),r.blockTableOffsetHigh=n.readInt8(10)}return r}readMPQUserDataHeader(){let e=this.file.subarray(0,16),t={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return t.content=this.file.subarray(16,16+t.userDataHeaderSize),t}readTable(e){let t=e==="hash"?"hashTableOffset":"blockTableOffset",r=e==="hash"?"hashTableEntries":"blockTableEntries",n=this.header[t],s=this.header[r];if(n==null||s==null)throw new Error("Missing "+e+" offset or entries");let l=this.hash("("+e+" table)","TABLE"),a=this.file.subarray(n+(this.header.offset||0),n+(this.header.offset||0)+s*16);a=this.decrypt(a,l);let c=[];for(let _=0;_<s;_++){let h=a.subarray(_*16,_*16+16);e==="hash"?c.push({hashA:h.readUInt32LE(0),hashB:h.readUInt32LE(4),locale:h.readUInt16LE(8),platform:h.readUInt16LE(10),blockTableIndex:h.readUInt32LE(12)}):c.push({offset:h.readUInt32LE(0),archivedSize:h.readUInt32LE(4),size:h.readUInt32LE(8),flags:h.readUInt32LE(12)})}return c}getHashTableEntry(e){let t=this.hash(e,"HASH_A"),r=this.hash(e,"HASH_B");for(let n of this.hashTable)if(n.hashA===t&&n.hashB===r)return n}readFile(e,t=!1){function r(c){let _=c[0];if(_===0)return c;if(_===2)return S.inflateSync(c.subarray(1));if(_===16)return J.decode(c.subarray(1));try{return S.inflateSync(c.subarray(1))}catch{return S.inflateRawSync(c.subarray(1))}}let n=this.getHashTableEntry(e);if(!n)return null;let s=this.blockTable[n.blockTableIndex];if(!s||!(s.flags&Z))return null;if(s.archivedSize===0)return Buffer.alloc(0);let l=s.offset+(this.header.offset||0),a=this.file.subarray(l,l+s.archivedSize);if(s.flags&K)throw new Error("Encryption is not supported");if(s.flags&W)s.flags&Q&&(t||s.size>s.archivedSize)&&(a=r(a));else{let c=512<<this.header.sectorSizeShift,_=Math.trunc(s.size/c)+1,h=!1;s.flags&Y&&(h=!0,_+=1);let o=[];for(let d=0;d<_+1;d++)o.push(a.readUInt32LE(4*d));let u=o.length-(h?2:1),f=[],b=s.size;for(let d=0;d<u;d++){let m=a.subarray(o[d],o[d+1]);s.flags&Q&&(t||b>m.length)&&(m=r(m)),b-=m.length,f.push(m)}a=Buffer.concat(f)}return a}hash(e,t){let r=2146271213,n=4008636142;for(let s=0;s<e.length;s++){let l=e.toUpperCase().charCodeAt(s);r=(z[(ee[t]<<8)+l]^r+n)>>>0,n=l+r+n+(n<<5)+3>>>0}return r}decrypt(e,t){let r=t>>>0,n=4008636142,s=Buffer.alloc(e.length),l=e.length/4;for(let a=0;a<l;a++){n=n+z[1024+(r&255)]>>>0;let c=e.readUInt32LE(a*4);c=(c^r+n)>>>0,r=((~r<<21)+286331153|r>>>11)>>>0,n=c+n+(n<<5)+3>>>0,s.writeUInt32LE(c,a*4)}return s}};var B=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},g=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},T=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,t="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=t==="big"}done(){return this._nextbits===0&&this._used>=this._data.length}used_bits(){return this._used*8-this._nextbits}byte_align(){this._nextbits=0}read_aligned_bytes(e){if(this.byte_align(),this._used+e>this._data.length)throw new B;let t=this._data.subarray(this._used,this._used+e);return this._used+=e,t}read_bits(e){let t=0,r=0;for(;r!==e;){if(this._nextbits===0){if(this.done())throw new B;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let n=Math.min(e-r,this._nextbits),s=this._next&(1<<n)-1;this._bigendian?t+=s*Math.pow(2,e-r-n):t+=s*Math.pow(2,r),this._next>>=n,this._nextbits-=n,r+=n}return t}read_bits_bigint(e){let t=0n,r=0;for(;r!==e;){if(this._nextbits===0){if(this.done())throw new B;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let n=Math.min(e-r,this._nextbits),s=BigInt(this._next&(1<<n)-1);this._bigendian?t|=s<<BigInt(e-r-n):t|=s<<BigInt(r),this._next>>=n,this._nextbits-=n,r+=n}return t}read_unaligned_bytes(e){let t=Buffer.alloc(e);for(let r=0;r<e;r++)t[r]=this.read_bits(8);return t}};var I=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new T(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new g(`Invalid typeid ${e}`);let t=this._typeinfos[e],r=t[0],n=t[1]||[],s=this[r];if(typeof s!="function")throw new Error(`Decoder method ${r} not implemented`);return s.apply(this,n)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_array(e,t){let r=this._int(e),n=new Array(r);for(let s=0;s<r;s++)n[s]=this.instance(t);return n}_bitarray(e){let t=this._int(e);return[t,this._buffer.read_bits(t)]}_blob(e){let t=this._int(e);return this._buffer.read_aligned_bytes(t)}_bool(){return this._int([0,1])!==0}_choice(e,t){let r=this._int(e);if(!(r in t))throw new g(`Choice tag ${r} not found`);let n=t[r];return{[n[0]]:this.instance(n[1])}}_fourcc(){let e=this._buffer.read_bits(32),t=Buffer.alloc(4);return t.writeUInt32BE(e,0),t.toString("ascii")}_int(e){return e[0]+this._buffer.read_bits(e[1])}_null(){return null}_optional(e){return this._bool()?this.instance(e):null}_real32(){return this._buffer.read_unaligned_bytes(4).readFloatBE(0)}_real64(){return this._buffer.read_unaligned_bytes(8).readDoubleBE(0)}_struct(e){let t={};for(let r of e)if(r[0]==="__parent"){let n=this.instance(r[1]);if(typeof n=="object"&&n!==null)t={...t,...n};else{if(e.length===1)return n;t[r[0]]=n}}else t[r[0]]=this.instance(r[1]);return t}},v=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new T(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new g(`Invalid typeid ${e}`);let t=this._typeinfos[e],r=t[0],n=t[1]||[],s=this[r];if(typeof s!="function")throw new Error(`Decoder method ${r} not implemented`);return s.apply(this,n)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_expect_skip(e){if(this._buffer.read_bits(8)!==e)throw new g(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),t=(e&1)!==0,r=BigInt(e>>1&63),n=6n;for(;(e&128)!==0;)e=this._buffer.read_bits(8),r|=BigInt(e&127)<<n,n+=7n;let s=t?-r:r;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,t){this._expect_skip(0);let r=Number(this._vint()),n=new Array(r);for(let s=0;s<r;s++)n[s]=this.instance(t);return n}_bitarray(e){this._expect_skip(1);let t=Number(this._vint());return[t,this._buffer.read_aligned_bytes(Math.floor((t+7)/8))]}_blob(e){this._expect_skip(2);let t=Number(this._vint());return this._buffer.read_aligned_bytes(t)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,t){this._expect_skip(3);let r=Number(this._vint());if(!(r in t))return this._skip_instance(),{};let n=t[r];return{[n[0]]:this.instance(n[1])}}_fourcc(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4)}_int(e){return this._expect_skip(9),Number(this._vint())}_null(){return null}_optional(e){return this._expect_skip(4),this._buffer.read_bits(8)!==0?this.instance(e):null}_real32(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4).readFloatBE(0)}_real64(){return this._expect_skip(8),this._buffer.read_aligned_bytes(8).readDoubleBE(0)}_struct(e){this._expect_skip(5);let t={},r=Number(this._vint());for(let n=0;n<r;n++){let s=Number(this._vint()),l=e.find(a=>a[2]===s);if(l)if(l[0]==="__parent"){let a=this.instance(l[1]);typeof a=="object"&&a!==null?t={...t,...a}:e.length===1?t=a:t[l[0]]=a}else t[l[0]]=this.instance(l[1]);else this._skip_instance()}return t}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let t=Number(this._vint());for(let r=0;r<t;r++)this._skip_instance()}else if(e===1){let t=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((t+7)/8))}else if(e===2){let t=Number(this._vint());this._buffer.read_aligned_bytes(t)}else if(e===3)this._vint(),this._skip_instance();else if(e===4)this._buffer.read_bits(8)!==0&&this._skip_instance();else if(e===5){let t=Number(this._vint());for(let r=0;r<t;r++)this._vint(),this._skip_instance()}else e===6?this._buffer.read_aligned_bytes(1):e===7?this._buffer.read_aligned_bytes(4):e===8?this._buffer.read_aligned_bytes(8):e===9&&this._vint()}};var E=M(require("fs")),w=M(require("path"));function re(i){let e=i;for(;e!==w.default.dirname(e);){if(E.default.existsSync(w.default.join(e,"package.json")))return e;e=w.default.dirname(e)}throw new Error("@astefanski/storm-parser: Could not find package root (no package.json found)")}function A(){let i;try{i=__dirname}catch{i=process.cwd()}let e=re(i),t=w.default.join(e,"protocols");if(E.default.existsSync(t))return t;throw new Error("@astefanski/storm-parser: Protocols directory not found at "+t+". Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts")}var U=new Map;function H(i){if(U.has(i))return U.get(i);let e=A(),t=w.default.join(e,`protocol${i}.json`);if(!E.default.existsSync(t))return null;let r=JSON.parse(E.default.readFileSync(t,"utf-8"));return U.set(i,r),r}function O(){let i=A();return E.default.readdirSync(i).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,t)=>e-t)}var k=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new R(e,!1)}init(){let e=H(29406);if(!e)throw new Error("Base protocol29406 not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts");this.baseProtocol=e;let t=this.mpq.header.userDataHeader;if(!t||!t.content)throw new Error("Replay does not have a user data header");let r=new v(t.content,this.baseProtocol.typeinfos);this.header=r.instance(this.baseProtocol.replay_header_typeid),this.build=this.header.m_version.m_baseBuild,this.protocol=this.loadProtocolForBuild(this.build)}loadProtocolForBuild(e){let t=H(e);if(!t){let r=O(),n=r[0];for(let s of r)s<=e&&s>n&&(n=s);t=H(n)}if(!t)throw new Error(`No protocol found for build ${e}`);return t}*decodeEventStream(e,t,r,n){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let l=e.used_bits(),a=e.instance(this.protocol.svaruint32_typeid),c=Object.keys(a)[0],_=a[c];s+=_;let h=n?e.instance(this.protocol.replay_userid_typeid):void 0,o=Number(e.instance(t)),u=r[o];if(!u)throw new Error(`Unknown eventid(${o})`);let f=u[0],b=u[1],d=e.instance(f);d._event=b,d._eventid=o,d._gameloop=s,n&&(d._userid=h),e.byte_align(),d._bits=e.used_bits()-l,yield d}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new v(e,this.protocol.typeinfos).instance(this.protocol.game_details_typeid):null}getInitData(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.initData");return e?new I(e,this.protocol.typeinfos).instance(this.protocol.replay_initdata_typeid):null}getTrackerEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.tracker.events");if(!e)return[];let t=new v(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.tracker_eventid_typeid,this.protocol.tracker_event_types,!1))}getGameEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.game.events");if(!e)return[];let t=new I(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.game_eventid_typeid,this.protocol.game_event_types,!0))}extractFile(e){return this.mpq.readFile(e)}};var L=class{static async analyze(e){try{let t=new k(e);await t.init();let r=t.getDetails(),n=t.getTrackerEvents();if(!r)throw new Error("Missing replay.details from parsed MPQ archive");console.log("Raw TimeUTC:",r?.m_timeUTC,"Type:",typeof r?.m_timeUTC);let s={map:r?.m_title?.toString("utf8"),date:r?this.fileTimeToDate(r.m_timeUTC).toISOString():new Date().toISOString(),length:0,winner:-1,version:{m_build:t.build},teams:{0:{level:0,takedowns:0,ids:[]},1:{level:0,takedowns:0,ids:[]}}},l={};for(let o of r.m_playerList){if(!o||!o.m_toon)continue;let u=o.m_toon,f=`${u.m_region}-${u.m_programId}-${u.m_realm}-${u.m_id}`;l[f]={hero:o.m_hero?.toString("utf8")||"",name:o.m_name?.toString("utf8")||"",tag:"",team:o.m_teamId,win:o.m_result===1,gameStats:{}}}let a=t.extractFile("replay.server.battlelobby");if(a)try{let o=new RegExp("([\\p{L}\\d]{3,24}#\\d{4,10})[z\xD8]?","gu"),u=a.toString("utf8").match(o);if(u){let f=0;for(let b of r.m_playerList){if(!b||!b.m_toon)continue;let d=b.m_name?.toString("utf8");for(;f<u.length;){let p=u[f].split("#"),D=p[0],x=p[1].replace(/[zØ]/g,"");if(f++,D===d){let y=`${b.m_toon.m_region}-${b.m_toon.m_programId}-${b.m_toon.m_realm}-${b.m_toon.m_id}`;l[y]&&(l[y].tag=x);break}}}}}catch(o){console.error("BattleTag regex error:",o)}let c={},_=0,h=0;for(let o of n){if(o._event==="NNet.Replay.Tracker.SStatGameEvent"){let u=o.m_eventName?.toString("utf8");if(u==="PlayerInit"){let f=o.m_intData,b=o.m_stringData,d=f[0].m_value,m=b[1].m_value?.toString("utf8");m&&l[m]&&(c[d]=m)}else u==="GatesOpen"&&(_=o._gameloop)}h=Math.max(h,o._gameloop)}s.length=Math.floor((h-_)/16);for(let o of n)if(o._event==="NNet.Replay.Tracker.SScoreResultEvent"){let u=o.m_instanceList;for(let f of u){let b=f.m_name?.toString("utf8"),d=f.m_values,m=0;if(!b.startsWith("EndOfMatchAward"))for(let p of d)if(p&&p.length>0&&p[0]!==void 0){let D=m+1,x=c[D];if(x&&l[x]){let y=typeof p[0]=="object"&&p[0]!==null&&"m_value"in p[0]?p[0].m_value:p[0];y!=null&&(l[x].gameStats[b]=y)}m++}else p&&p.length===0&&m++}}for(let[o,u]of Object.entries(l)){u.win&&(s.winner=u.team);let f=s.teams[u.team.toString()];f&&(f.level=Math.max(f.level,u.gameStats.Level||0),f.takedowns+=u.gameStats.Takedowns||0,f.ids.push(o))}return{status:1,match:s,players:l}}catch(t){return console.error("ReplayAnalyzer Error:",t),{status:-2,error:String(t)}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};0&&(module.exports={ReplayAnalyzer,ReplayParser});
|
|
1
|
+
"use strict";var Z=Object.create;var L=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var ne=Object.getPrototypeOf,re=Object.prototype.hasOwnProperty;var ie=(r,e)=>{for(var t in e)L(r,t,{get:e[t],enumerable:!0})},G=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of te(e))!re.call(r,i)&&i!==t&&L(r,i,{get:()=>e[i],enumerable:!(n=ee(e,i))||n.enumerable});return r};var A=(r,e,t)=>(t=r!=null?Z(ne(r)):{},G(e||!r||!r.__esModule?L(t,"default",{value:r,enumerable:!0}):t,r)),se=r=>G(L({},"__esModule",{value:!0}),r);var Re={};ie(Re,{ReplayAnalyzer:()=>N,ReplayParser:()=>D});module.exports=se(Re);var z=A(require("fs")),M=A(require("zlib")),oe=require("seek-bzip"),F=512,ae=65536,le=16777216,ce=67108864,ue=2147483648,de={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function fe(){let r=1048577,e=new Uint32Array(256*5);for(let t=0;t<256;t++){let n=t;for(let i=0;i<5;i++){r=(r*125+3)%2796203;let s=(r&65535)<<16;r=(r*125+3)%2796203;let a=r&65535;e[n]=(s|a)>>>0,n+=256}}return e}var Q=fe(),C=class{file;header;hashTable;blockTable;files;constructor(e,t=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=z.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),t){let n=this.readFile("(listfile)");n?this.files=n.toString("utf8").trim().split(`\r
|
|
2
|
+
`):this.files=null}else this.files=null}readHeader(){let e=this.file.toString("utf8",0,4),t;if(e==="MPQ")t=this.readMPQHeader(),t.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();t=this.readMPQHeader(n.mpqHeaderOffset),t.offset=n.mpqHeaderOffset,t.userDataHeader=n}else throw new Error("Invalid MPQ file header");return t}readMPQHeader(e=0){let t=this.file.subarray(e,e+32),n={magic:t.toString("utf8",0,4),headerSize:t.readUInt32LE(4),archiveSize:t.readUInt32LE(8),formatVersion:t.readUInt16LE(12),sectorSizeShift:t.readUInt16LE(14),hashTableOffset:t.readUInt32LE(16),blockTableOffset:t.readUInt32LE(20),hashTableEntries:t.readUInt32LE(24),blockTableEntries:t.readUInt32LE(28)};if(n.formatVersion===1){let i=this.file.subarray(e+32,e+32+12);n.extendedBlockTableOffset=i.readUInt32LE(0)+i.readUInt32LE(4)*4294967296,n.hashTableOffsetHigh=i.readInt8(8),n.blockTableOffsetHigh=i.readInt8(10)}return n}readMPQUserDataHeader(){let e=this.file.subarray(0,16),t={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return t.content=this.file.subarray(16,16+t.userDataHeaderSize),t}readTable(e){let t=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[t],s=this.header[n];if(i==null||s==null)throw new Error("Missing "+e+" offset or entries");let a=this.hash("("+e+" table)","TABLE"),o=this.file.subarray(i+(this.header.offset||0),i+(this.header.offset||0)+s*16);o=this.decrypt(o,a);let l=[];for(let u=0;u<s;u++){let c=o.subarray(u*16,u*16+16);e==="hash"?l.push({hashA:c.readUInt32LE(0),hashB:c.readUInt32LE(4),locale:c.readUInt16LE(8),platform:c.readUInt16LE(10),blockTableIndex:c.readUInt32LE(12)}):l.push({offset:c.readUInt32LE(0),archivedSize:c.readUInt32LE(4),size:c.readUInt32LE(8),flags:c.readUInt32LE(12)})}return l}getHashTableEntry(e){let t=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===t&&i.hashB===n)return i}readFile(e,t=!1){function n(l){let u=l[0];if(u===0)return l;if(u===2)return M.inflateSync(l.subarray(1));if(u===16)return oe.decode(l.subarray(1));try{return M.inflateSync(l.subarray(1))}catch{return M.inflateRawSync(l.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&ue))return null;if(s.archivedSize===0)return Buffer.alloc(0);let a=s.offset+(this.header.offset||0),o=this.file.subarray(a,a+s.archivedSize);if(s.flags&ae)throw new Error("Encryption is not supported");if(s.flags&le)s.flags&F&&(t||s.size>s.archivedSize)&&(o=n(o));else{let l=512<<this.header.sectorSizeShift,u=Math.trunc(s.size/l)+1,c=!1;s.flags&ce&&(c=!0,u+=1);let d=[];for(let h=0;h<u+1;h++)d.push(o.readUInt32LE(4*h));let f=d.length-(c?2:1),b=[],m=s.size;for(let h=0;h<f;h++){let p=o.subarray(d[h],d[h+1]);s.flags&F&&(t||m>p.length)&&(p=n(p)),m-=p.length,b.push(p)}o=Buffer.concat(b)}return o}hash(e,t){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let a=e.toUpperCase().charCodeAt(s);n=(Q[(de[t]<<8)+a]^n+i)>>>0,i=a+n+i+(i<<5)+3>>>0}return n}decrypt(e,t){let n=t>>>0,i=4008636142,s=Buffer.alloc(e.length),a=e.length/4;for(let o=0;o<a;o++){i=i+Q[1024+(n&255)]>>>0;let l=e.readUInt32LE(o*4);l=(l^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=l+i+(i<<5)+3>>>0,s.writeUInt32LE(l,o*4)}return s}};var H=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},w=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},B=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,t="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=t==="big"}done(){return this._nextbits===0&&this._used>=this._data.length}used_bits(){return this._used*8-this._nextbits}byte_align(){this._nextbits=0}read_aligned_bytes(e){if(this.byte_align(),this._used+e>this._data.length)throw new H;let t=this._data.subarray(this._used,this._used+e);return this._used+=e,t}read_bits(e){let t=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new H;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let i=Math.min(e-n,this._nextbits),s=this._next&(1<<i)-1;this._bigendian?t+=s*Math.pow(2,e-n-i):t+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return t}read_bits_bigint(e){let t=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new H;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let i=Math.min(e-n,this._nextbits),s=BigInt(this._next&(1<<i)-1);this._bigendian?t|=s<<BigInt(e-n-i):t|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return t}read_unaligned_bytes(e){let t=Buffer.alloc(e);for(let n=0;n<e;n++)t[n]=this.read_bits(8);return t}};var R=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new B(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new w(`Invalid typeid ${e}`);let t=this._typeinfos[e],n=t[0],i=t[1]||[],s=this[n];if(typeof s!="function")throw new Error(`Decoder method ${n} not implemented`);return s.apply(this,i)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_array(e,t){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(t);return i}_bitarray(e){let t=this._int(e);return[t,this._buffer.read_bits(t)]}_blob(e){let t=this._int(e);return this._buffer.read_aligned_bytes(t)}_bool(){return this._int([0,1])!==0}_choice(e,t){let n=this._int(e);if(!(n in t))throw new w(`Choice tag ${n} not found`);let i=t[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),t=Buffer.alloc(4);return t.writeUInt32BE(e,0),t.toString("ascii")}_int(e){return e[0]+this._buffer.read_bits(e[1])}_null(){return null}_optional(e){return this._bool()?this.instance(e):null}_real32(){return this._buffer.read_unaligned_bytes(4).readFloatBE(0)}_real64(){return this._buffer.read_unaligned_bytes(8).readDoubleBE(0)}_struct(e){let t={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null)t={...t,...i};else{if(e.length===1)return i;t[n[0]]=i}}else t[n[0]]=this.instance(n[1]);return t}},x=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new B(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new w(`Invalid typeid ${e}`);let t=this._typeinfos[e],n=t[0],i=t[1]||[],s=this[n];if(typeof s!="function")throw new Error(`Decoder method ${n} not implemented`);return s.apply(this,i)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_expect_skip(e){if(this._buffer.read_bits(8)!==e)throw new w(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),t=(e&1)!==0,n=BigInt(e>>1&63),i=6n;for(;(e&128)!==0;)e=this._buffer.read_bits(8),n|=BigInt(e&127)<<i,i+=7n;let s=t?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,t){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(t);return i}_bitarray(e){this._expect_skip(1);let t=Number(this._vint());return[t,this._buffer.read_aligned_bytes(Math.floor((t+7)/8))]}_blob(e){this._expect_skip(2);let t=Number(this._vint());return this._buffer.read_aligned_bytes(t)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,t){this._expect_skip(3);let n=Number(this._vint());if(!(n in t))return this._skip_instance(),{};let i=t[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4)}_int(e){return this._expect_skip(9),Number(this._vint())}_null(){return null}_optional(e){return this._expect_skip(4),this._buffer.read_bits(8)!==0?this.instance(e):null}_real32(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4).readFloatBE(0)}_real64(){return this._expect_skip(8),this._buffer.read_aligned_bytes(8).readDoubleBE(0)}_struct(e){this._expect_skip(5);let t={},n=Number(this._vint());for(let i=0;i<n;i++){let s=Number(this._vint()),a=e.find(o=>o[2]===s);if(a)if(a[0]==="__parent"){let o=this.instance(a[1]);typeof o=="object"&&o!==null?t={...t,...o}:e.length===1?t=o:t[a[0]]=o}else t[a[0]]=this.instance(a[1]);else this._skip_instance()}return t}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let t=Number(this._vint());for(let n=0;n<t;n++)this._skip_instance()}else if(e===1){let t=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((t+7)/8))}else if(e===2){let t=Number(this._vint());this._buffer.read_aligned_bytes(t)}else if(e===3)this._vint(),this._skip_instance();else if(e===4)this._buffer.read_bits(8)!==0&&this._skip_instance();else if(e===5){let t=Number(this._vint());for(let n=0;n<t;n++)this._vint(),this._skip_instance()}else e===6?this._buffer.read_aligned_bytes(1):e===7?this._buffer.read_aligned_bytes(4):e===8?this._buffer.read_aligned_bytes(8):e===9&&this._vint()}};var I=A(require("fs")),S=A(require("path"));function me(){let r=[];try{r.push(S.default.resolve(__dirname,"..","protocols")),r.push(S.default.resolve(__dirname,"..","..","protocols"))}catch{}r.push(S.default.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==S.default.dirname(e);){let t=S.default.join(e,"node_modules","@astefanski","storm-parser","protocols");r.push(t),e=S.default.dirname(e)}for(let t of r)if(I.default.existsSync(t)&&I.default.readdirSync(t).some(n=>n.endsWith(".json")))return t;throw new Error(`@astefanski/storm-parser: Protocols directory not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts
|
|
3
|
+
Searched: `+r.join(", "))}var O=new Map,j=null;function $(){return j||(j=me()),j}function U(r){if(O.has(r))return O.get(r);let e=$(),t=S.default.join(e,`protocol${r}.json`);if(!I.default.existsSync(t))return null;let n=JSON.parse(I.default.readFileSync(t,"utf-8"));return O.set(r,n),n}function V(){let r=$();return I.default.readdirSync(r).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,t)=>e-t)}var D=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new C(e,!1)}init(){let e=U(29406);if(!e)throw new Error("Base protocol29406 not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts");this.baseProtocol=e;let t=this.mpq.header.userDataHeader;if(!t||!t.content)throw new Error("Replay does not have a user data header");let n=new x(t.content,this.baseProtocol.typeinfos);this.header=n.instance(this.baseProtocol.replay_header_typeid),this.build=this.header.m_version.m_baseBuild,this.protocol=this.loadProtocolForBuild(this.build)}loadProtocolForBuild(e){let t=U(e);if(!t){let n=V(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);t=U(i)}if(!t)throw new Error(`No protocol found for build ${e}`);return t}*decodeEventStream(e,t,n,i){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let a=e.used_bits(),o=e.instance(this.protocol.svaruint32_typeid),l=Object.keys(o)[0],u=o[l];s+=u;let c=i?e.instance(this.protocol.replay_userid_typeid):void 0,d=Number(e.instance(t)),f=n[d];if(!f)throw new Error(`Unknown eventid(${d})`);let b=f[0],m=f[1],h=e.instance(b);h._event=m,h._eventid=d,h._gameloop=s,i&&(h._userid=c),e.byte_align(),h._bits=e.used_bits()-a,yield h}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new x(e,this.protocol.typeinfos).instance(this.protocol.game_details_typeid):null}getInitData(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.initData");return e?new R(e,this.protocol.typeinfos).instance(this.protocol.replay_initdata_typeid):null}getTrackerEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.tracker.events");if(!e)return[];let t=new x(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.tracker_eventid_typeid,this.protocol.tracker_event_types,!1))}getGameEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.game.events");if(!e)return[];let t=new R(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.game_eventid_typeid,this.protocol.game_event_types,!0))}extractFile(e){return this.mpq.readFile(e)}getHeader(){return this.header}getBuild(){return this.build}};var W={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},pe={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function E(r){return Buffer.isBuffer(r)?r.toString("utf8"):typeof r=="string"?r:String(r??"")}function _(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?t.m_value:void 0}function X(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?E(t.m_value):void 0}function v(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?t.m_value:void 0}function K(r,e){return`${r}-${e}`}function k(r,e){return(r-e)/16}function q(r,e,t){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};t.levelTimes={0:{},1:{}},t.takedowns=[],t.structures={},t.XPBreakdown=[],t.mercs={captures:[],units:{}},t.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:t.map||""};for(let i of r)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":he(i,n,e,t);break;case"NNet.Replay.Tracker.SUnitBornEvent":_e(i,n,t);break;case"NNet.Replay.Tracker.SUnitDiedEvent":ye(i,n,t);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":ve(i,n);break}return t.loopGameStart=n.loopGameStart,t.loopLength=n.loopGameEnd,t.length=(n.loopGameEnd-n.loopGameStart)/16,Te(n,e),{playerIDMap:n.playerIDMap}}function he(r,e,t,n){let i=E(r.m_eventName),s=r.m_intData,a=r.m_stringData,o=r.m_fixedData;switch(i){case"PlayerInit":{let l=_(s,"PlayerID"),u=X(a,"ToonHandle");l!==void 0&&u&&t[u]&&(e.playerIDMap[l]=u);break}case"GatesOpen":e.loopGameStart=r._gameloop;break;case"LevelUp":{let l=_(s,"PlayerID"),u=_(s,"Level");if(l===void 0||u===void 0)break;let c;if(l>=1&&l<=5)c="0";else if(l>=6&&l<=10)c="1";else break;n.levelTimes[c][String(u)]||(n.levelTimes[c][String(u)]={loop:r._gameloop,level:u,team:c,time:k(r._gameloop,e.loopGameStart)});break}case"TalentChosen":{let l=_(s,"PlayerID"),u=X(a,"PurchaseName");if(l===void 0||!u)break;let c=e.playerIDMap[l];if(!c||!t[c])break;let d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let f of d)if(!t[c].talents[f]){t[c].talents[f]=u;break}break}case"PlayerDeath":{let l=_(s,"PlayerID"),u=_(s,"KillingPlayer"),c=v(o,"PositionX")??0,d=v(o,"PositionY")??0;if(l===void 0)break;let f=e.playerIDMap[l];if(!f)break;let b={player:f,hero:t[f]?.hero||""},m=[],h=t[f]?.team;if(u&&u>0){let g=e.playerIDMap[u];g&&t[g]&&m.push({player:g,hero:t[g].hero})}for(let[g,y]of Object.entries(e.playerIDMap)){let T=t[y];!T||T.team===h||parseInt(g)===u||m.push({player:y,hero:T.hero})}let p={loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),x:c,y:d,killers:m,victim:b};n.takedowns.push(p),t[f]&&t[f].deaths.push(p);for(let g of m)t[g.player]&&t[g.player].takedowns.push(p);break}case"PeriodicXPBreakdown":{let l=_(s,"Team");if(l===void 0)break;let u={GameTime:_(s,"GameTime")??0,PreviousGameTime:_(s,"PreviousGameTime")??0,MinionXP:v(o,"MinionXP")??0,CreepXP:v(o,"CreepXP")??0,StructureXP:v(o,"StructureXP")??0,HeroXP:v(o,"HeroXP")??0,TrickleXP:v(o,"TrickleXP")??0};n.XPBreakdown.push({loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),team:l,teamLevel:_(s,"TeamLevel")??0,breakdown:u,theoreticalMinionXP:_(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let l=_(s,"Team");if(l===void 0)break;n.XPBreakdown.push({loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),team:l,theoreticalMinionXP:_(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:v(o,"MinionXP")??0,CreepXP:v(o,"CreepXP")??0,StructureXP:v(o,"StructureXP")??0,HeroXP:v(o,"HeroXP")??0,TrickleXP:v(o,"TrickleXP")??0}});break}case"JungleCampCapture":{let l=_(s,"CampTeam")??_(s,"Team")??0;n.mercs.captures.push({loop:r._gameloop,type:X(a,"CampType")??X(a,"Result")??"Unknown Camp",team:l,time:k(r._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let l=_(s,"PlayerID");if(l===void 0)break;let u=e.playerIDMap[l];if(!u||!t[u])break;let c={},d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(a)for(let f=0;f<a.length&&f<d.length;f++){let b=E(a[f].m_value);b&&(c[d[f]]=b)}t[u].talents=c;break}default:ge(i,r,e,n,s,o);break}}var be=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function ge(r,e,t,n,i,s){if(!be.has(r)||!i)return;let a=_(i,"Team")??_(i,"Event")??0,o=a===0||a===1?a:0,l={team:a,loop:e._gameloop,time:k(e._gameloop,t.loopGameStart),score:_(i,"Score"),duration:v(s,"Duration")};n.objective[o].events.push(l),n.objective[o].count=n.objective[o].events.length}function _e(r,e,t){let n=E(r.m_unitTypeName),i=r.m_unitTagIndex,s=r.m_unitTagRecycle,a=K(i,s),o=r.m_controlPlayerId??r.m_upkeepPlayerId,l=r.m_x??0,u=r.m_y??0,c;if(o>=1&&o<=5?c=0:o>=6&&o<=10?c=1:o===11?c=0:o===12?c=1:c=o<=5?0:1,e.unitIndex[a]={type:n,playerId:o,team:c,x:l,y:u,bornLoop:r._gameloop},n.startsWith("Town")&&W[n]&&(t.structures[a]={type:n,name:W[n],tag:i,rtag:s,x:l,y:u,team:c}),n.startsWith("Hero")&&o>=1&&o<=10){e.heroUnits[a]=o,e.heroLives[o]||(e.heroLives[o]=[]);let d=k(r._gameloop,e.loopGameStart);e.heroLives[o].push({born:d,locations:[{x:l,y:u,time:d}],duration:0})}if(pe[n]){let f=t.mercs.captures[t.mercs.captures.length-1]?.loop??r._gameloop;t.mercs.units[a]={loop:f,team:c,type:n,locations:[{x:l,y:u}],time:k(f,e.loopGameStart),duration:0}}}function ye(r,e,t){let n=K(r.m_unitTagIndex,r.m_unitTagRecycle),i=k(r._gameloop,e.loopGameStart);t.structures[n]&&(t.structures[n].destroyedLoop=r._gameloop,t.structures[n].destroyed=i),t.mercs.units[n]&&(t.mercs.units[n].duration=i-t.mercs.units[n].time);let s=e.heroUnits[n];if(s!==void 0&&e.heroLives[s]){let a=e.heroLives[s],o=a[a.length-1];o&&o.died===void 0&&(o.died=i,o.duration=i-o.born)}}function ve(r,e){let t=K(r.m_unitTagIndex,r.m_unitTagRecycle),n=r.m_controlPlayerId??r.m_upkeepPlayerId;e.unitIndex[t]&&(e.unitIndex[t].playerId=n,n>=1&&n<=5?e.unitIndex[t].team=0:n>=6&&n<=10&&(e.unitIndex[t].team=1))}function Te(r,e){for(let[t,n]of Object.entries(r.heroLives)){let i=parseInt(t,10),s=r.playerIDMap[i];if(!s||!e[s])continue;for(let o of n)if(o.died===void 0){let l=o.locations[o.locations.length-1];o.duration=l?l.time-o.born:0}let a="";for(let[o,l]of Object.entries(r.heroUnits))if(l===i){a=o;break}a&&(e[s].units[a]={lives:n})}}var ke=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function Se(){return{DamageTaken:0,CreepDamage:0,Healing:0,HeroDamage:0,MinionDamage:0,SelfHealing:0,SiegeDamage:0,ProtectionGivenToAllies:0,TeamfightDamageTaken:0,TeamfightHealingDone:0,TeamfightHeroDamage:0,TimeCCdEnemyHeroes:0,TimeRootingEnemyHeroes:0,TimeSpentDead:0,TimeStunningEnemyHeroes:0,TimeSilencingEnemyHeroes:0,avgTimeSpentDead:0,timeDeadPct:0}}function Pe(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:Se(),levelAdvTime:0,maxLevelAdv:0,avgLevelAdv:0,levelAdvPct:0,uptime:[],uptimeHistogram:{},wipes:0,avgHeroesAlive:0,aces:0,timeWithHeroAdv:0,pctWithHeroAdv:0,passiveXPRate:0,passiveXPDiff:0,passiveXPGain:0}}function J(r,e){we(r,e),xe(r,e),Ie(r),De(r),Me(r,e),Be(r)}function we(r,e){for(let t of["0","1"]){let n=r.teams[t];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=Pe();for(let d of n.ids){let f=e[d];f&&(n.names.push(f.name),n.heroes.push(f.hero),n.tags.push(f.tag))}for(let d of n.ids){let f=e[d];if(f)for(let b of ke){let m=f.gameStats[b];typeof m=="number"&&(n.stats.totals[b]+=m)}}let i=n.ids.reduce((d,f)=>d+(e[f]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),r.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(r.length*n.ids.length));let s=n.takedowns,a=i;n.stats.KDA=a>0?s/a:s,n.stats.PPK=s>0?r.takedowns.filter(d=>n.ids.includes(d.killers[0]?.player)).reduce((d,f)=>d+f.killers.length,0)/s:0;let o=r.levelTimes[t];o?.["10"]&&(n.stats.timeTo10=o[10].time);let l=parseInt(t,10),u=r.mercs.captures.filter(d=>d.team===l);n.stats.mercCaptures=u.length;let c=0;for(let d of Object.values(r.mercs.units))d.team===l&&d.duration>0&&(c+=d.duration);n.stats.mercUptime=c,n.stats.mercUptimePercent=r.length>0?c/r.length:0,Ee(r,n,t)}}function Ee(r,e,t){let n=parseInt(t,10),i=n===0?1:0,s={};for(let a of Object.values(r.structures)){let o=a.name;s[o]||(s[o]={lost:0,destroyed:0,first:r.length}),a.team===i&&a.destroyed!==void 0&&(s[o].destroyed++,s[o].first=Math.min(s[o].first,a.destroyed)),a.team===n&&a.destroyed!==void 0&&s[o].lost++}e.stats.structures=s}function xe(r,e){let t=r.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,a=i.TeamTakedowns??0;t>0&&(i.DPM=(i.HeroDamage??0)/t,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/t,i.XPM=(i.ExperienceContribution??0)/t),i.KDA=s>0?(i.Takedowns??0)/s:i.Takedowns??0,i.KillParticipation=a>0?(i.Takedowns??0)/a:0,i.damageDonePerDeath=s>0?(i.HeroDamage??0)/s:i.HeroDamage??0,i.damageTakenPerDeath=s>0?(i.DamageTaken??0)/s:i.DamageTaken??0,i.healingDonePerDeath=s>0?((i.Healing??0)+(i.SelfHealing??0))/s:(i.Healing??0)+(i.SelfHealing??0),i.length=r.length}}function Ie(r){let e=0,t=0,n=new Set(r.teams[0]?.ids||[]);for(let i of r.takedowns)n.has(i.victim.player)?t++:e++;r.team0Takedowns=e,r.team1Takedowns=t}function De(r){let e=r.levelTimes[0]||{},t=r.levelTimes[1]||{},n=[];for(let u of Object.values(e))n.push({time:u.time,team:0,level:u.level});for(let u of Object.values(t))n.push({time:u.time,team:1,level:u.level});if(n.sort((u,c)=>u.time-c.time),n.length===0){r.levelAdvTimeline=[];return}let i=[],s=0,a=0,o=n[0].time;for(let u of n){let c=s-a;u.time>o&&i.push({start:o,end:u.time,levelDiff:c,length:u.time-o}),u.team===0?s=u.level:a=u.level,o=u.time}let l=s-a;r.length>o&&i.push({start:o,end:r.length,levelDiff:l,length:r.length-o}),r.levelAdvTimeline=i;for(let u of["0","1"]){let c=r.teams[u];if(!c)continue;let d=u==="0"?1:-1,f=0,b=0,m=0,h=0;for(let p of i){let g=p.levelDiff*d;g>0&&(f+=p.length),b=Math.max(b,g),m+=g*p.length,h+=p.length}c.stats.levelAdvTime=f,c.stats.maxLevelAdv=b,c.stats.avgLevelAdv=h>0?m/h:0,c.stats.levelAdvPct=r.length>0?f/r.length:0}}function Me(r,e){for(let s of["0","1"]){let a=r.teams[s];if(!a)continue;let o=[];for(let p of a.ids){let g=e[p];if(g)for(let y of g.deaths){o.push({time:y.time,delta:-1});let T=y.time+He(g.gameStats.Level??1,y.time,r.length);T<r.length&&o.push({time:T,delta:1})}}o.sort((p,g)=>p.time-g.time);let l=[{time:0,heroes:a.ids.length}],u=a.ids.length;for(let p of o)u+=p.delta,u=Math.max(0,Math.min(a.ids.length,u)),l.push({time:p.time,heroes:u});a.stats.uptime=l;let c={};for(let p=0;p<l.length;p++){let y=(p<l.length-1?l[p+1].time:r.length)-l[p].time,T=String(l[p].heroes);c[T]=(c[T]||0)+y}a.stats.uptimeHistogram=c;let d=0,f=0;for(let p=0;p<l.length;p++){let y=(p<l.length-1?l[p+1].time:r.length)-l[p].time;d+=l[p].heroes*y,f+=y}a.stats.avgHeroesAlive=f>0?d/f:a.ids.length,a.stats.wipes=l.filter(p=>p.heroes===0).length,a.stats.aces=0;let b=s==="0"?"1":"0",m=r.teams[b];m?.stats?.uptime&&(a.stats.aces=m.stats.uptime.filter(p=>p.heroes===0).length);let h=r.XPBreakdown.filter(p=>p.team===parseInt(s,10));if(h.length>0){let g=h[h.length-1].breakdown.TrickleXP;a.stats.passiveXPGain=g,a.stats.passiveXPRate=r.length>0?g/(r.length/60):0}}for(let s of["0","1"]){let a=r.teams[s],o=s==="0"?"1":"0",l=r.teams[o];if(!a||!l)continue;let u=a.stats.uptime,c=l.stats.uptime;if(!u.length||!c.length)continue;let d=new Set;for(let m of u)d.add(m.time);for(let m of c)d.add(m.time);let f=Array.from(d).sort((m,h)=>m-h),b=0;for(let m=0;m<f.length;m++){let h=f[m],g=(m<f.length-1?f[m+1]:r.length)-h,y=Y(u,h),T=Y(c,h);y>T&&(b+=g)}a.stats.timeWithHeroAdv=b,a.stats.pctWithHeroAdv=r.length>0?b/r.length:0}let t=r.teams[0]?.stats.passiveXPRate??0,n=r.teams[1]?.stats.passiveXPRate??0,i=(t+n)/2;r.teams[0]&&(r.teams[0].stats.passiveXPDiff=i>0?t/i:0),r.teams[1]&&(r.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function Y(r,e){let t=0;for(let n of r)if(n.time<=e)t=n.heroes;else break;return t}function He(r,e,t){return r<=1?15:r<=5?15+(r-1)*2:r<=10?23+(r-5)*3:r<=15?38+(r-10)*4:58+(r-15)*5}function Be(r){let e=1/0,t=-1,n=1/0,i=-1;for(let a of Object.values(r.structures))a.destroyed!==void 0&&(a.name==="Fort"&&a.destroyed<e&&(e=a.destroyed,t=a.team===0?1:0),a.name==="Keep"&&a.destroyed<n&&(n=a.destroyed,i=a.team===0?1:0));t>=0&&(r.firstFort=t,r.firstFortWin=t===r.winner),i>=0&&(r.firstKeep=i,r.firstKeepWin=i===r.winner);let s=[...r.objective[0].events.map(a=>({...a,assignedTeam:0})),...r.objective[1].events.map(a=>({...a,assignedTeam:1}))].sort((a,o)=>a.loop-o.loop);s.length>0&&(r.firstObjective=s[0].assignedTeam,r.firstObjectiveWin=s[0].assignedTeam===r.winner),r.firstPickWin=r.picks.first===r.winner}function P(r){return Buffer.isBuffer(r)?r.toString("utf8"):typeof r=="string"?r:String(r??"")}var N=class{static async analyze(e){try{let t=new D(e);await t.init();let n=t.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=t.getTrackerEvents(),s=t.getInitData(),o=t.getHeader()?.m_version??{},l={m_flags:o.m_flags??0,m_major:o.m_major??0,m_minor:o.m_minor??0,m_revision:o.m_revision??0,m_build:o.m_baseBuild??t.getBuild(),m_baseBuild:o.m_baseBuild??t.getBuild()},u=n.m_playerList.find(b=>b?.m_toon)?.m_toon,c={version:l,map:P(n.m_title),date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:u?.m_region,playerIDs:[],heroes:[],levelTimes:{0:{},1:{}},bans:{0:[],1:[]},picks:{0:[],1:[],first:0},XPBreakdown:[],takedowns:[],mercs:{captures:[],units:{}},team0Takedowns:0,team1Takedowns:0,structures:{},objective:{0:{count:0,events:[]},1:{count:0,events:[]},type:""},teams:{0:this.emptyTeam(),1:this.emptyTeam()},winningPlayers:[],levelAdvTimeline:[],firstPickWin:!1};c.objective.type=c.map||"";let d={};for(let b of n.m_playerList){if(!b?.m_toon)continue;let m=b.m_toon,h=P(m.m_programId),p=`${m.m_region}-${h}-${m.m_realm}-${m.m_id}`,g=P(b.m_hero);d[p]={hero:g,name:P(b.m_name),uuid:m.m_id,region:m.m_region,realm:m.m_realm,ToonHandle:p,tag:0,team:b.m_teamId,win:b.m_result===1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},c.playerIDs.push(p),c.heroes.push(g)}this.extractBattleTags(t,n,d),this.extractDraft(s,c,n);let{playerIDMap:f}=q(i,d,c);this.processScoreEvents(i,f,d);for(let[b,m]of Object.entries(d)){m.win&&(c.winner=m.team,c.winningPlayers.push(b));let h=c.teams[m.team.toString()];h&&(h.level=Math.max(h.level,m.gameStats.Level||0),h.takedowns+=m.gameStats.Takedowns||0,h.ids.push(b))}return J(c,d),{status:1,match:c,players:d}}catch(t){return console.error("ReplayAnalyzer Error:",t),{status:-2,error:String(t)}}}static emptyTeam(){return{level:0,takedowns:0,ids:[],names:[],heroes:[],tags:[],stats:{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:{DamageTaken:0,CreepDamage:0,Healing:0,HeroDamage:0,MinionDamage:0,SelfHealing:0,SiegeDamage:0,ProtectionGivenToAllies:0,TeamfightDamageTaken:0,TeamfightHealingDone:0,TeamfightHeroDamage:0,TimeCCdEnemyHeroes:0,TimeRootingEnemyHeroes:0,TimeSpentDead:0,TimeStunningEnemyHeroes:0,TimeSilencingEnemyHeroes:0,avgTimeSpentDead:0,timeDeadPct:0},levelAdvTime:0,maxLevelAdv:0,avgLevelAdv:0,levelAdvPct:0,uptime:[],uptimeHistogram:{},wipes:0,avgHeroesAlive:0,aces:0,timeWithHeroAdv:0,pctWithHeroAdv:0,passiveXPRate:0,passiveXPDiff:0,passiveXPGain:0}}}static extractBattleTags(e,t,n){let i=e.extractFile("replay.server.battlelobby");if(i)try{let s=new RegExp("([\\p{L}\\d]{3,24}#\\d{4,10})[z\xD8]?","gu"),a=i.toString("utf8").match(s);if(!a)return;let o=0;for(let l of t.m_playerList){if(!l?.m_toon)continue;let u=P(l.m_name);for(;o<a.length;){let d=a[o].split("#"),f=d[0],b=d[1].replace(/[zØ]/g,"");if(o++,f===u){let m=l.m_toon,h=`${m.m_region}-${P(m.m_programId)}-${m.m_realm}-${m.m_id}`;n[h]&&(n[h].tag=parseInt(b,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,t,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let a=[],o=[];for(let c of n.m_playerList){if(!c?.m_toon)continue;let d=P(c.m_hero);c.m_teamId===0?a.push(d):c.m_teamId===1&&o.push(d)}if(t.picks={0:a,1:o,first:0},typeof s.m_gameMode=="number"&&(t.mode=s.m_gameMode),typeof s.m_gameType=="number"&&(t.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let c=s.m_firstPickTeam??0;t.picks.first=c}}catch{}}static processScoreEvents(e,t,n){for(let i of e){if(i._event!=="NNet.Replay.Tracker.SScoreResultEvent")continue;let s=i.m_instanceList;for(let a of s){let o=P(a.m_name),l=a.m_values,u=o.startsWith("EndOfMatchAward"),c=0;for(let d of l)if(d&&d.length>0&&d[0]!==void 0){let f=c+1,b=t[f];if(b&&n[b]){let m=typeof d[0]=="object"&&d[0]!==null&&"m_value"in d[0]?d[0].m_value:d[0];m!=null&&(u?m===1&&n[b].awards.push(o):n[b].gameStats[o]=m)}c++}else d&&d.length===0&&c++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};0&&(module.exports={ReplayAnalyzer,ReplayParser});
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`):this.files=null}else this.files=null}readHeader(){let e=this.file.toString("utf8",0,4),t;if(e==="MPQ")t=this.readMPQHeader(),t.offset=0;else if(e==="MPQ\x1B"){let r=this.readMPQUserDataHeader();t=this.readMPQHeader(r.mpqHeaderOffset),t.offset=r.mpqHeaderOffset,t.userDataHeader=r}else throw new Error("Invalid MPQ file header");return t}readMPQHeader(e=0){let t=this.file.subarray(e,e+32),r={magic:t.toString("utf8",0,4),headerSize:t.readUInt32LE(4),archiveSize:t.readUInt32LE(8),formatVersion:t.readUInt16LE(12),sectorSizeShift:t.readUInt16LE(14),hashTableOffset:t.readUInt32LE(16),blockTableOffset:t.readUInt32LE(20),hashTableEntries:t.readUInt32LE(24),blockTableEntries:t.readUInt32LE(28)};if(r.formatVersion===1){let n=this.file.subarray(e+32,e+32+12);r.extendedBlockTableOffset=n.readUInt32LE(0)+n.readUInt32LE(4)*4294967296,r.hashTableOffsetHigh=n.readInt8(8),r.blockTableOffsetHigh=n.readInt8(10)}return r}readMPQUserDataHeader(){let e=this.file.subarray(0,16),t={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return t.content=this.file.subarray(16,16+t.userDataHeaderSize),t}readTable(e){let t=e==="hash"?"hashTableOffset":"blockTableOffset",r=e==="hash"?"hashTableEntries":"blockTableEntries",n=this.header[t],s=this.header[r];if(n==null||s==null)throw new Error("Missing "+e+" offset or entries");let l=this.hash("("+e+" table)","TABLE"),a=this.file.subarray(n+(this.header.offset||0),n+(this.header.offset||0)+s*16);a=this.decrypt(a,l);let c=[];for(let _=0;_<s;_++){let h=a.subarray(_*16,_*16+16);e==="hash"?c.push({hashA:h.readUInt32LE(0),hashB:h.readUInt32LE(4),locale:h.readUInt16LE(8),platform:h.readUInt16LE(10),blockTableIndex:h.readUInt32LE(12)}):c.push({offset:h.readUInt32LE(0),archivedSize:h.readUInt32LE(4),size:h.readUInt32LE(8),flags:h.readUInt32LE(12)})}return c}getHashTableEntry(e){let t=this.hash(e,"HASH_A"),r=this.hash(e,"HASH_B");for(let n of this.hashTable)if(n.hashA===t&&n.hashB===r)return n}readFile(e,t=!1){function r(c){let _=c[0];if(_===0)return c;if(_===2)return E.inflateSync(c.subarray(1));if(_===16)return A.decode(c.subarray(1));try{return E.inflateSync(c.subarray(1))}catch{return E.inflateRawSync(c.subarray(1))}}let n=this.getHashTableEntry(e);if(!n)return null;let s=this.blockTable[n.blockTableIndex];if(!s||!(s.flags&j))return null;if(s.archivedSize===0)return Buffer.alloc(0);let l=s.offset+(this.header.offset||0),a=this.file.subarray(l,l+s.archivedSize);if(s.flags&O)throw new Error("Encryption is not supported");if(s.flags&$)s.flags&D&&(t||s.size>s.archivedSize)&&(a=r(a));else{let c=512<<this.header.sectorSizeShift,_=Math.trunc(s.size/c)+1,h=!1;s.flags&C&&(h=!0,_+=1);let o=[];for(let d=0;d<_+1;d++)o.push(a.readUInt32LE(4*d));let u=o.length-(h?2:1),f=[],b=s.size;for(let d=0;d<u;d++){let m=a.subarray(o[d],o[d+1]);s.flags&D&&(t||b>m.length)&&(m=r(m)),b-=m.length,f.push(m)}a=Buffer.concat(f)}return a}hash(e,t){let r=2146271213,n=4008636142;for(let s=0;s<e.length;s++){let l=e.toUpperCase().charCodeAt(s);r=(U[(q[t]<<8)+l]^r+n)>>>0,n=l+r+n+(n<<5)+3>>>0}return r}decrypt(e,t){let r=t>>>0,n=4008636142,s=Buffer.alloc(e.length),l=e.length/4;for(let a=0;a<l;a++){n=n+U[1024+(r&255)]>>>0;let c=e.readUInt32LE(a*4);c=(c^r+n)>>>0,r=((~r<<21)+286331153|r>>>11)>>>0,n=c+n+(n<<5)+3>>>0,s.writeUInt32LE(c,a*4)}return s}};var k=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},g=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},x=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,t="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=t==="big"}done(){return this._nextbits===0&&this._used>=this._data.length}used_bits(){return this._used*8-this._nextbits}byte_align(){this._nextbits=0}read_aligned_bytes(e){if(this.byte_align(),this._used+e>this._data.length)throw new k;let t=this._data.subarray(this._used,this._used+e);return this._used+=e,t}read_bits(e){let t=0,r=0;for(;r!==e;){if(this._nextbits===0){if(this.done())throw new k;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let n=Math.min(e-r,this._nextbits),s=this._next&(1<<n)-1;this._bigendian?t+=s*Math.pow(2,e-r-n):t+=s*Math.pow(2,r),this._next>>=n,this._nextbits-=n,r+=n}return t}read_bits_bigint(e){let t=0n,r=0;for(;r!==e;){if(this._nextbits===0){if(this.done())throw new k;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let n=Math.min(e-r,this._nextbits),s=BigInt(this._next&(1<<n)-1);this._bigendian?t|=s<<BigInt(e-r-n):t|=s<<BigInt(r),this._next>>=n,this._nextbits-=n,r+=n}return t}read_unaligned_bytes(e){let t=Buffer.alloc(e);for(let r=0;r<e;r++)t[r]=this.read_bits(8);return t}};var S=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new x(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new g(`Invalid typeid ${e}`);let t=this._typeinfos[e],r=t[0],n=t[1]||[],s=this[r];if(typeof s!="function")throw new Error(`Decoder method ${r} not implemented`);return s.apply(this,n)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_array(e,t){let r=this._int(e),n=new Array(r);for(let s=0;s<r;s++)n[s]=this.instance(t);return n}_bitarray(e){let t=this._int(e);return[t,this._buffer.read_bits(t)]}_blob(e){let t=this._int(e);return this._buffer.read_aligned_bytes(t)}_bool(){return this._int([0,1])!==0}_choice(e,t){let r=this._int(e);if(!(r in t))throw new g(`Choice tag ${r} not found`);let n=t[r];return{[n[0]]:this.instance(n[1])}}_fourcc(){let e=this._buffer.read_bits(32),t=Buffer.alloc(4);return t.writeUInt32BE(e,0),t.toString("ascii")}_int(e){return e[0]+this._buffer.read_bits(e[1])}_null(){return null}_optional(e){return this._bool()?this.instance(e):null}_real32(){return this._buffer.read_unaligned_bytes(4).readFloatBE(0)}_real64(){return this._buffer.read_unaligned_bytes(8).readDoubleBE(0)}_struct(e){let t={};for(let r of e)if(r[0]==="__parent"){let n=this.instance(r[1]);if(typeof n=="object"&&n!==null)t={...t,...n};else{if(e.length===1)return n;t[r[0]]=n}}else t[r[0]]=this.instance(r[1]);return t}},v=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new x(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new g(`Invalid typeid ${e}`);let t=this._typeinfos[e],r=t[0],n=t[1]||[],s=this[r];if(typeof s!="function")throw new Error(`Decoder method ${r} not implemented`);return s.apply(this,n)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_expect_skip(e){if(this._buffer.read_bits(8)!==e)throw new g(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),t=(e&1)!==0,r=BigInt(e>>1&63),n=6n;for(;(e&128)!==0;)e=this._buffer.read_bits(8),r|=BigInt(e&127)<<n,n+=7n;let s=t?-r:r;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,t){this._expect_skip(0);let r=Number(this._vint()),n=new Array(r);for(let s=0;s<r;s++)n[s]=this.instance(t);return n}_bitarray(e){this._expect_skip(1);let t=Number(this._vint());return[t,this._buffer.read_aligned_bytes(Math.floor((t+7)/8))]}_blob(e){this._expect_skip(2);let t=Number(this._vint());return this._buffer.read_aligned_bytes(t)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,t){this._expect_skip(3);let r=Number(this._vint());if(!(r in t))return this._skip_instance(),{};let n=t[r];return{[n[0]]:this.instance(n[1])}}_fourcc(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4)}_int(e){return this._expect_skip(9),Number(this._vint())}_null(){return null}_optional(e){return this._expect_skip(4),this._buffer.read_bits(8)!==0?this.instance(e):null}_real32(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4).readFloatBE(0)}_real64(){return this._expect_skip(8),this._buffer.read_aligned_bytes(8).readDoubleBE(0)}_struct(e){this._expect_skip(5);let t={},r=Number(this._vint());for(let n=0;n<r;n++){let s=Number(this._vint()),l=e.find(a=>a[2]===s);if(l)if(l[0]==="__parent"){let a=this.instance(l[1]);typeof a=="object"&&a!==null?t={...t,...a}:e.length===1?t=a:t[l[0]]=a}else t[l[0]]=this.instance(l[1]);else this._skip_instance()}return t}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let t=Number(this._vint());for(let r=0;r<t;r++)this._skip_instance()}else if(e===1){let t=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((t+7)/8))}else if(e===2){let t=Number(this._vint());this._buffer.read_aligned_bytes(t)}else if(e===3)this._vint(),this._skip_instance();else if(e===4)this._buffer.read_bits(8)!==0&&this._skip_instance();else if(e===5){let t=Number(this._vint());for(let r=0;r<t;r++)this._vint(),this._skip_instance()}else e===6?this._buffer.read_aligned_bytes(1):e===7?this._buffer.read_aligned_bytes(4):e===8?this._buffer.read_aligned_bytes(8):e===9&&this._vint()}};import T from"fs";import B from"path";function V(i){let e=i;for(;e!==B.dirname(e);){if(T.existsSync(B.join(e,"package.json")))return e;e=B.dirname(e)}throw new Error("@astefanski/storm-parser: Could not find package root (no package.json found)")}function Q(){let i;try{i=__dirname}catch{i=process.cwd()}let e=V(i),t=B.join(e,"protocols");if(T.existsSync(t))return t;throw new Error("@astefanski/storm-parser: Protocols directory not found at "+t+". Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts")}var H=new Map;function M(i){if(H.has(i))return H.get(i);let e=Q(),t=B.join(e,`protocol${i}.json`);if(!T.existsSync(t))return null;let r=JSON.parse(T.readFileSync(t,"utf-8"));return H.set(i,r),r}function z(){let i=Q();return T.readdirSync(i).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,t)=>e-t)}var I=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new P(e,!1)}init(){let e=M(29406);if(!e)throw new Error("Base protocol29406 not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts");this.baseProtocol=e;let t=this.mpq.header.userDataHeader;if(!t||!t.content)throw new Error("Replay does not have a user data header");let r=new v(t.content,this.baseProtocol.typeinfos);this.header=r.instance(this.baseProtocol.replay_header_typeid),this.build=this.header.m_version.m_baseBuild,this.protocol=this.loadProtocolForBuild(this.build)}loadProtocolForBuild(e){let t=M(e);if(!t){let r=z(),n=r[0];for(let s of r)s<=e&&s>n&&(n=s);t=M(n)}if(!t)throw new Error(`No protocol found for build ${e}`);return t}*decodeEventStream(e,t,r,n){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let l=e.used_bits(),a=e.instance(this.protocol.svaruint32_typeid),c=Object.keys(a)[0],_=a[c];s+=_;let h=n?e.instance(this.protocol.replay_userid_typeid):void 0,o=Number(e.instance(t)),u=r[o];if(!u)throw new Error(`Unknown eventid(${o})`);let f=u[0],b=u[1],d=e.instance(f);d._event=b,d._eventid=o,d._gameloop=s,n&&(d._userid=h),e.byte_align(),d._bits=e.used_bits()-l,yield d}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new v(e,this.protocol.typeinfos).instance(this.protocol.game_details_typeid):null}getInitData(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.initData");return e?new S(e,this.protocol.typeinfos).instance(this.protocol.replay_initdata_typeid):null}getTrackerEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.tracker.events");if(!e)return[];let t=new v(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.tracker_eventid_typeid,this.protocol.tracker_event_types,!1))}getGameEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.game.events");if(!e)return[];let t=new S(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.game_eventid_typeid,this.protocol.game_event_types,!0))}extractFile(e){return this.mpq.readFile(e)}};var L=class{static async analyze(e){try{let t=new I(e);await t.init();let r=t.getDetails(),n=t.getTrackerEvents();if(!r)throw new Error("Missing replay.details from parsed MPQ archive");console.log("Raw TimeUTC:",r?.m_timeUTC,"Type:",typeof r?.m_timeUTC);let s={map:r?.m_title?.toString("utf8"),date:r?this.fileTimeToDate(r.m_timeUTC).toISOString():new Date().toISOString(),length:0,winner:-1,version:{m_build:t.build},teams:{0:{level:0,takedowns:0,ids:[]},1:{level:0,takedowns:0,ids:[]}}},l={};for(let o of r.m_playerList){if(!o||!o.m_toon)continue;let u=o.m_toon,f=`${u.m_region}-${u.m_programId}-${u.m_realm}-${u.m_id}`;l[f]={hero:o.m_hero?.toString("utf8")||"",name:o.m_name?.toString("utf8")||"",tag:"",team:o.m_teamId,win:o.m_result===1,gameStats:{}}}let a=t.extractFile("replay.server.battlelobby");if(a)try{let o=new RegExp("([\\p{L}\\d]{3,24}#\\d{4,10})[z\xD8]?","gu"),u=a.toString("utf8").match(o);if(u){let f=0;for(let b of r.m_playerList){if(!b||!b.m_toon)continue;let d=b.m_name?.toString("utf8");for(;f<u.length;){let p=u[f].split("#"),R=p[0],w=p[1].replace(/[zØ]/g,"");if(f++,R===d){let y=`${b.m_toon.m_region}-${b.m_toon.m_programId}-${b.m_toon.m_realm}-${b.m_toon.m_id}`;l[y]&&(l[y].tag=w);break}}}}}catch(o){console.error("BattleTag regex error:",o)}let c={},_=0,h=0;for(let o of n){if(o._event==="NNet.Replay.Tracker.SStatGameEvent"){let u=o.m_eventName?.toString("utf8");if(u==="PlayerInit"){let f=o.m_intData,b=o.m_stringData,d=f[0].m_value,m=b[1].m_value?.toString("utf8");m&&l[m]&&(c[d]=m)}else u==="GatesOpen"&&(_=o._gameloop)}h=Math.max(h,o._gameloop)}s.length=Math.floor((h-_)/16);for(let o of n)if(o._event==="NNet.Replay.Tracker.SScoreResultEvent"){let u=o.m_instanceList;for(let f of u){let b=f.m_name?.toString("utf8"),d=f.m_values,m=0;if(!b.startsWith("EndOfMatchAward"))for(let p of d)if(p&&p.length>0&&p[0]!==void 0){let R=m+1,w=c[R];if(w&&l[w]){let y=typeof p[0]=="object"&&p[0]!==null&&"m_value"in p[0]?p[0].m_value:p[0];y!=null&&(l[w].gameStats[b]=y)}m++}else p&&p.length===0&&m++}}for(let[o,u]of Object.entries(l)){u.win&&(s.winner=u.team);let f=s.teams[u.team.toString()];f&&(f.level=Math.max(f.level,u.gameStats.Level||0),f.takedowns+=u.gameStats.Takedowns||0,f.ids.push(o))}return{status:1,match:s,players:l}}catch(t){return console.error("ReplayAnalyzer Error:",t),{status:-2,error:String(t)}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};export{L as ReplayAnalyzer,I as ReplayParser};
|
|
1
|
+
var q=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});import*as G from"fs";import*as I from"zlib";var Y=q("seek-bzip"),j=512,J=65536,Z=16777216,ee=67108864,te=2147483648,ne={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function re(){let r=1048577,e=new Uint32Array(256*5);for(let t=0;t<256;t++){let n=t;for(let i=0;i<5;i++){r=(r*125+3)%2796203;let s=(r&65535)<<16;r=(r*125+3)%2796203;let a=r&65535;e[n]=(s|a)>>>0,n+=256}}return e}var K=re(),L=class{file;header;hashTable;blockTable;files;constructor(e,t=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=G.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),t){let n=this.readFile("(listfile)");n?this.files=n.toString("utf8").trim().split(`\r
|
|
2
|
+
`):this.files=null}else this.files=null}readHeader(){let e=this.file.toString("utf8",0,4),t;if(e==="MPQ")t=this.readMPQHeader(),t.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();t=this.readMPQHeader(n.mpqHeaderOffset),t.offset=n.mpqHeaderOffset,t.userDataHeader=n}else throw new Error("Invalid MPQ file header");return t}readMPQHeader(e=0){let t=this.file.subarray(e,e+32),n={magic:t.toString("utf8",0,4),headerSize:t.readUInt32LE(4),archiveSize:t.readUInt32LE(8),formatVersion:t.readUInt16LE(12),sectorSizeShift:t.readUInt16LE(14),hashTableOffset:t.readUInt32LE(16),blockTableOffset:t.readUInt32LE(20),hashTableEntries:t.readUInt32LE(24),blockTableEntries:t.readUInt32LE(28)};if(n.formatVersion===1){let i=this.file.subarray(e+32,e+32+12);n.extendedBlockTableOffset=i.readUInt32LE(0)+i.readUInt32LE(4)*4294967296,n.hashTableOffsetHigh=i.readInt8(8),n.blockTableOffsetHigh=i.readInt8(10)}return n}readMPQUserDataHeader(){let e=this.file.subarray(0,16),t={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return t.content=this.file.subarray(16,16+t.userDataHeaderSize),t}readTable(e){let t=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[t],s=this.header[n];if(i==null||s==null)throw new Error("Missing "+e+" offset or entries");let a=this.hash("("+e+" table)","TABLE"),o=this.file.subarray(i+(this.header.offset||0),i+(this.header.offset||0)+s*16);o=this.decrypt(o,a);let l=[];for(let u=0;u<s;u++){let c=o.subarray(u*16,u*16+16);e==="hash"?l.push({hashA:c.readUInt32LE(0),hashB:c.readUInt32LE(4),locale:c.readUInt16LE(8),platform:c.readUInt16LE(10),blockTableIndex:c.readUInt32LE(12)}):l.push({offset:c.readUInt32LE(0),archivedSize:c.readUInt32LE(4),size:c.readUInt32LE(8),flags:c.readUInt32LE(12)})}return l}getHashTableEntry(e){let t=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===t&&i.hashB===n)return i}readFile(e,t=!1){function n(l){let u=l[0];if(u===0)return l;if(u===2)return I.inflateSync(l.subarray(1));if(u===16)return Y.decode(l.subarray(1));try{return I.inflateSync(l.subarray(1))}catch{return I.inflateRawSync(l.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&te))return null;if(s.archivedSize===0)return Buffer.alloc(0);let a=s.offset+(this.header.offset||0),o=this.file.subarray(a,a+s.archivedSize);if(s.flags&J)throw new Error("Encryption is not supported");if(s.flags&Z)s.flags&j&&(t||s.size>s.archivedSize)&&(o=n(o));else{let l=512<<this.header.sectorSizeShift,u=Math.trunc(s.size/l)+1,c=!1;s.flags&ee&&(c=!0,u+=1);let d=[];for(let h=0;h<u+1;h++)d.push(o.readUInt32LE(4*h));let f=d.length-(c?2:1),b=[],m=s.size;for(let h=0;h<f;h++){let p=o.subarray(d[h],d[h+1]);s.flags&j&&(t||m>p.length)&&(p=n(p)),m-=p.length,b.push(p)}o=Buffer.concat(b)}return o}hash(e,t){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let a=e.toUpperCase().charCodeAt(s);n=(K[(ne[t]<<8)+a]^n+i)>>>0,i=a+n+i+(i<<5)+3>>>0}return n}decrypt(e,t){let n=t>>>0,i=4008636142,s=Buffer.alloc(e.length),a=e.length/4;for(let o=0;o<a;o++){i=i+K[1024+(n&255)]>>>0;let l=e.readUInt32LE(o*4);l=(l^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=l+i+(i<<5)+3>>>0,s.writeUInt32LE(l,o*4)}return s}};var D=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},P=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},M=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,t="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=t==="big"}done(){return this._nextbits===0&&this._used>=this._data.length}used_bits(){return this._used*8-this._nextbits}byte_align(){this._nextbits=0}read_aligned_bytes(e){if(this.byte_align(),this._used+e>this._data.length)throw new D;let t=this._data.subarray(this._used,this._used+e);return this._used+=e,t}read_bits(e){let t=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new D;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let i=Math.min(e-n,this._nextbits),s=this._next&(1<<i)-1;this._bigendian?t+=s*Math.pow(2,e-n-i):t+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return t}read_bits_bigint(e){let t=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new D;this._next=this._data[this._used],this._used+=1,this._nextbits=8}let i=Math.min(e-n,this._nextbits),s=BigInt(this._next&(1<<i)-1);this._bigendian?t|=s<<BigInt(e-n-i):t|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return t}read_unaligned_bytes(e){let t=Buffer.alloc(e);for(let n=0;n<e;n++)t[n]=this.read_bits(8);return t}};var H=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new M(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new P(`Invalid typeid ${e}`);let t=this._typeinfos[e],n=t[0],i=t[1]||[],s=this[n];if(typeof s!="function")throw new Error(`Decoder method ${n} not implemented`);return s.apply(this,i)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_array(e,t){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(t);return i}_bitarray(e){let t=this._int(e);return[t,this._buffer.read_bits(t)]}_blob(e){let t=this._int(e);return this._buffer.read_aligned_bytes(t)}_bool(){return this._int([0,1])!==0}_choice(e,t){let n=this._int(e);if(!(n in t))throw new P(`Choice tag ${n} not found`);let i=t[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),t=Buffer.alloc(4);return t.writeUInt32BE(e,0),t.toString("ascii")}_int(e){return e[0]+this._buffer.read_bits(e[1])}_null(){return null}_optional(e){return this._bool()?this.instance(e):null}_real32(){return this._buffer.read_unaligned_bytes(4).readFloatBE(0)}_real64(){return this._buffer.read_unaligned_bytes(8).readDoubleBE(0)}_struct(e){let t={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null)t={...t,...i};else{if(e.length===1)return i;t[n[0]]=i}}else t[n[0]]=this.instance(n[1]);return t}},x=class{_buffer;_typeinfos;constructor(e,t){this._buffer=new M(e),this._typeinfos=t}instance(e){if(e>=this._typeinfos.length)throw new P(`Invalid typeid ${e}`);let t=this._typeinfos[e],n=t[0],i=t[1]||[],s=this[n];if(typeof s!="function")throw new Error(`Decoder method ${n} not implemented`);return s.apply(this,i)}byte_align(){this._buffer.byte_align()}done(){return this._buffer.done()}used_bits(){return this._buffer.used_bits()}_expect_skip(e){if(this._buffer.read_bits(8)!==e)throw new P(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),t=(e&1)!==0,n=BigInt(e>>1&63),i=6n;for(;(e&128)!==0;)e=this._buffer.read_bits(8),n|=BigInt(e&127)<<i,i+=7n;let s=t?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,t){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(t);return i}_bitarray(e){this._expect_skip(1);let t=Number(this._vint());return[t,this._buffer.read_aligned_bytes(Math.floor((t+7)/8))]}_blob(e){this._expect_skip(2);let t=Number(this._vint());return this._buffer.read_aligned_bytes(t)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,t){this._expect_skip(3);let n=Number(this._vint());if(!(n in t))return this._skip_instance(),{};let i=t[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4)}_int(e){return this._expect_skip(9),Number(this._vint())}_null(){return null}_optional(e){return this._expect_skip(4),this._buffer.read_bits(8)!==0?this.instance(e):null}_real32(){return this._expect_skip(7),this._buffer.read_aligned_bytes(4).readFloatBE(0)}_real64(){return this._expect_skip(8),this._buffer.read_aligned_bytes(8).readDoubleBE(0)}_struct(e){this._expect_skip(5);let t={},n=Number(this._vint());for(let i=0;i<n;i++){let s=Number(this._vint()),a=e.find(o=>o[2]===s);if(a)if(a[0]==="__parent"){let o=this.instance(a[1]);typeof o=="object"&&o!==null?t={...t,...o}:e.length===1?t=o:t[a[0]]=o}else t[a[0]]=this.instance(a[1]);else this._skip_instance()}return t}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let t=Number(this._vint());for(let n=0;n<t;n++)this._skip_instance()}else if(e===1){let t=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((t+7)/8))}else if(e===2){let t=Number(this._vint());this._buffer.read_aligned_bytes(t)}else if(e===3)this._vint(),this._skip_instance();else if(e===4)this._buffer.read_bits(8)!==0&&this._skip_instance();else if(e===5){let t=Number(this._vint());for(let n=0;n<t;n++)this._vint(),this._skip_instance()}else e===6?this._buffer.read_aligned_bytes(1):e===7?this._buffer.read_aligned_bytes(4):e===8?this._buffer.read_aligned_bytes(8):e===9&&this._vint()}};import B from"fs";import w from"path";function ie(){let r=[];try{r.push(w.resolve(__dirname,"..","protocols")),r.push(w.resolve(__dirname,"..","..","protocols"))}catch{}r.push(w.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==w.dirname(e);){let t=w.join(e,"node_modules","@astefanski","storm-parser","protocols");r.push(t),e=w.dirname(e)}for(let t of r)if(B.existsSync(t)&&B.readdirSync(t).some(n=>n.endsWith(".json")))return t;throw new Error(`@astefanski/storm-parser: Protocols directory not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts
|
|
3
|
+
Searched: `+r.join(", "))}var U=new Map,X=null;function F(){return X||(X=ie()),X}function A(r){if(U.has(r))return U.get(r);let e=F(),t=w.join(e,`protocol${r}.json`);if(!B.existsSync(t))return null;let n=JSON.parse(B.readFileSync(t,"utf-8"));return U.set(r,n),n}function Q(){let r=F();return B.readdirSync(r).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,t)=>e-t)}var R=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new L(e,!1)}init(){let e=A(29406);if(!e)throw new Error("Base protocol29406 not found. Did postinstall run? Try: npx tsx node_modules/@astefanski/storm-parser/scripts/postinstall.ts");this.baseProtocol=e;let t=this.mpq.header.userDataHeader;if(!t||!t.content)throw new Error("Replay does not have a user data header");let n=new x(t.content,this.baseProtocol.typeinfos);this.header=n.instance(this.baseProtocol.replay_header_typeid),this.build=this.header.m_version.m_baseBuild,this.protocol=this.loadProtocolForBuild(this.build)}loadProtocolForBuild(e){let t=A(e);if(!t){let n=Q(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);t=A(i)}if(!t)throw new Error(`No protocol found for build ${e}`);return t}*decodeEventStream(e,t,n,i){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let a=e.used_bits(),o=e.instance(this.protocol.svaruint32_typeid),l=Object.keys(o)[0],u=o[l];s+=u;let c=i?e.instance(this.protocol.replay_userid_typeid):void 0,d=Number(e.instance(t)),f=n[d];if(!f)throw new Error(`Unknown eventid(${d})`);let b=f[0],m=f[1],h=e.instance(b);h._event=m,h._eventid=d,h._gameloop=s,i&&(h._userid=c),e.byte_align(),h._bits=e.used_bits()-a,yield h}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new x(e,this.protocol.typeinfos).instance(this.protocol.game_details_typeid):null}getInitData(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.initData");return e?new H(e,this.protocol.typeinfos).instance(this.protocol.replay_initdata_typeid):null}getTrackerEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.tracker.events");if(!e)return[];let t=new x(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.tracker_eventid_typeid,this.protocol.tracker_event_types,!1))}getGameEvents(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.game.events");if(!e)return[];let t=new H(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(t,this.protocol.game_eventid_typeid,this.protocol.game_event_types,!0))}extractFile(e){return this.mpq.readFile(e)}getHeader(){return this.header}getBuild(){return this.build}};var z={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},se={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function E(r){return Buffer.isBuffer(r)?r.toString("utf8"):typeof r=="string"?r:String(r??"")}function _(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?t.m_value:void 0}function C(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?E(t.m_value):void 0}function v(r,e){if(!r)return;let t=r.find(n=>E(n.m_key)===e);return t!==void 0?t.m_value:void 0}function N(r,e){return`${r}-${e}`}function k(r,e){return(r-e)/16}function $(r,e,t){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};t.levelTimes={0:{},1:{}},t.takedowns=[],t.structures={},t.XPBreakdown=[],t.mercs={captures:[],units:{}},t.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:t.map||""};for(let i of r)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":oe(i,n,e,t);break;case"NNet.Replay.Tracker.SUnitBornEvent":ce(i,n,t);break;case"NNet.Replay.Tracker.SUnitDiedEvent":ue(i,n,t);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":de(i,n);break}return t.loopGameStart=n.loopGameStart,t.loopLength=n.loopGameEnd,t.length=(n.loopGameEnd-n.loopGameStart)/16,fe(n,e),{playerIDMap:n.playerIDMap}}function oe(r,e,t,n){let i=E(r.m_eventName),s=r.m_intData,a=r.m_stringData,o=r.m_fixedData;switch(i){case"PlayerInit":{let l=_(s,"PlayerID"),u=C(a,"ToonHandle");l!==void 0&&u&&t[u]&&(e.playerIDMap[l]=u);break}case"GatesOpen":e.loopGameStart=r._gameloop;break;case"LevelUp":{let l=_(s,"PlayerID"),u=_(s,"Level");if(l===void 0||u===void 0)break;let c;if(l>=1&&l<=5)c="0";else if(l>=6&&l<=10)c="1";else break;n.levelTimes[c][String(u)]||(n.levelTimes[c][String(u)]={loop:r._gameloop,level:u,team:c,time:k(r._gameloop,e.loopGameStart)});break}case"TalentChosen":{let l=_(s,"PlayerID"),u=C(a,"PurchaseName");if(l===void 0||!u)break;let c=e.playerIDMap[l];if(!c||!t[c])break;let d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let f of d)if(!t[c].talents[f]){t[c].talents[f]=u;break}break}case"PlayerDeath":{let l=_(s,"PlayerID"),u=_(s,"KillingPlayer"),c=v(o,"PositionX")??0,d=v(o,"PositionY")??0;if(l===void 0)break;let f=e.playerIDMap[l];if(!f)break;let b={player:f,hero:t[f]?.hero||""},m=[],h=t[f]?.team;if(u&&u>0){let g=e.playerIDMap[u];g&&t[g]&&m.push({player:g,hero:t[g].hero})}for(let[g,y]of Object.entries(e.playerIDMap)){let T=t[y];!T||T.team===h||parseInt(g)===u||m.push({player:y,hero:T.hero})}let p={loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),x:c,y:d,killers:m,victim:b};n.takedowns.push(p),t[f]&&t[f].deaths.push(p);for(let g of m)t[g.player]&&t[g.player].takedowns.push(p);break}case"PeriodicXPBreakdown":{let l=_(s,"Team");if(l===void 0)break;let u={GameTime:_(s,"GameTime")??0,PreviousGameTime:_(s,"PreviousGameTime")??0,MinionXP:v(o,"MinionXP")??0,CreepXP:v(o,"CreepXP")??0,StructureXP:v(o,"StructureXP")??0,HeroXP:v(o,"HeroXP")??0,TrickleXP:v(o,"TrickleXP")??0};n.XPBreakdown.push({loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),team:l,teamLevel:_(s,"TeamLevel")??0,breakdown:u,theoreticalMinionXP:_(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let l=_(s,"Team");if(l===void 0)break;n.XPBreakdown.push({loop:r._gameloop,time:k(r._gameloop,e.loopGameStart),team:l,theoreticalMinionXP:_(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:v(o,"MinionXP")??0,CreepXP:v(o,"CreepXP")??0,StructureXP:v(o,"StructureXP")??0,HeroXP:v(o,"HeroXP")??0,TrickleXP:v(o,"TrickleXP")??0}});break}case"JungleCampCapture":{let l=_(s,"CampTeam")??_(s,"Team")??0;n.mercs.captures.push({loop:r._gameloop,type:C(a,"CampType")??C(a,"Result")??"Unknown Camp",team:l,time:k(r._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let l=_(s,"PlayerID");if(l===void 0)break;let u=e.playerIDMap[l];if(!u||!t[u])break;let c={},d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(a)for(let f=0;f<a.length&&f<d.length;f++){let b=E(a[f].m_value);b&&(c[d[f]]=b)}t[u].talents=c;break}default:le(i,r,e,n,s,o);break}}var ae=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function le(r,e,t,n,i,s){if(!ae.has(r)||!i)return;let a=_(i,"Team")??_(i,"Event")??0,o=a===0||a===1?a:0,l={team:a,loop:e._gameloop,time:k(e._gameloop,t.loopGameStart),score:_(i,"Score"),duration:v(s,"Duration")};n.objective[o].events.push(l),n.objective[o].count=n.objective[o].events.length}function ce(r,e,t){let n=E(r.m_unitTypeName),i=r.m_unitTagIndex,s=r.m_unitTagRecycle,a=N(i,s),o=r.m_controlPlayerId??r.m_upkeepPlayerId,l=r.m_x??0,u=r.m_y??0,c;if(o>=1&&o<=5?c=0:o>=6&&o<=10?c=1:o===11?c=0:o===12?c=1:c=o<=5?0:1,e.unitIndex[a]={type:n,playerId:o,team:c,x:l,y:u,bornLoop:r._gameloop},n.startsWith("Town")&&z[n]&&(t.structures[a]={type:n,name:z[n],tag:i,rtag:s,x:l,y:u,team:c}),n.startsWith("Hero")&&o>=1&&o<=10){e.heroUnits[a]=o,e.heroLives[o]||(e.heroLives[o]=[]);let d=k(r._gameloop,e.loopGameStart);e.heroLives[o].push({born:d,locations:[{x:l,y:u,time:d}],duration:0})}if(se[n]){let f=t.mercs.captures[t.mercs.captures.length-1]?.loop??r._gameloop;t.mercs.units[a]={loop:f,team:c,type:n,locations:[{x:l,y:u}],time:k(f,e.loopGameStart),duration:0}}}function ue(r,e,t){let n=N(r.m_unitTagIndex,r.m_unitTagRecycle),i=k(r._gameloop,e.loopGameStart);t.structures[n]&&(t.structures[n].destroyedLoop=r._gameloop,t.structures[n].destroyed=i),t.mercs.units[n]&&(t.mercs.units[n].duration=i-t.mercs.units[n].time);let s=e.heroUnits[n];if(s!==void 0&&e.heroLives[s]){let a=e.heroLives[s],o=a[a.length-1];o&&o.died===void 0&&(o.died=i,o.duration=i-o.born)}}function de(r,e){let t=N(r.m_unitTagIndex,r.m_unitTagRecycle),n=r.m_controlPlayerId??r.m_upkeepPlayerId;e.unitIndex[t]&&(e.unitIndex[t].playerId=n,n>=1&&n<=5?e.unitIndex[t].team=0:n>=6&&n<=10&&(e.unitIndex[t].team=1))}function fe(r,e){for(let[t,n]of Object.entries(r.heroLives)){let i=parseInt(t,10),s=r.playerIDMap[i];if(!s||!e[s])continue;for(let o of n)if(o.died===void 0){let l=o.locations[o.locations.length-1];o.duration=l?l.time-o.born:0}let a="";for(let[o,l]of Object.entries(r.heroUnits))if(l===i){a=o;break}a&&(e[s].units[a]={lives:n})}}var me=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function pe(){return{DamageTaken:0,CreepDamage:0,Healing:0,HeroDamage:0,MinionDamage:0,SelfHealing:0,SiegeDamage:0,ProtectionGivenToAllies:0,TeamfightDamageTaken:0,TeamfightHealingDone:0,TeamfightHeroDamage:0,TimeCCdEnemyHeroes:0,TimeRootingEnemyHeroes:0,TimeSpentDead:0,TimeStunningEnemyHeroes:0,TimeSilencingEnemyHeroes:0,avgTimeSpentDead:0,timeDeadPct:0}}function he(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:pe(),levelAdvTime:0,maxLevelAdv:0,avgLevelAdv:0,levelAdvPct:0,uptime:[],uptimeHistogram:{},wipes:0,avgHeroesAlive:0,aces:0,timeWithHeroAdv:0,pctWithHeroAdv:0,passiveXPRate:0,passiveXPDiff:0,passiveXPGain:0}}function W(r,e){be(r,e),_e(r,e),ye(r),ve(r),Te(r,e),Se(r)}function be(r,e){for(let t of["0","1"]){let n=r.teams[t];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=he();for(let d of n.ids){let f=e[d];f&&(n.names.push(f.name),n.heroes.push(f.hero),n.tags.push(f.tag))}for(let d of n.ids){let f=e[d];if(f)for(let b of me){let m=f.gameStats[b];typeof m=="number"&&(n.stats.totals[b]+=m)}}let i=n.ids.reduce((d,f)=>d+(e[f]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),r.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(r.length*n.ids.length));let s=n.takedowns,a=i;n.stats.KDA=a>0?s/a:s,n.stats.PPK=s>0?r.takedowns.filter(d=>n.ids.includes(d.killers[0]?.player)).reduce((d,f)=>d+f.killers.length,0)/s:0;let o=r.levelTimes[t];o?.["10"]&&(n.stats.timeTo10=o[10].time);let l=parseInt(t,10),u=r.mercs.captures.filter(d=>d.team===l);n.stats.mercCaptures=u.length;let c=0;for(let d of Object.values(r.mercs.units))d.team===l&&d.duration>0&&(c+=d.duration);n.stats.mercUptime=c,n.stats.mercUptimePercent=r.length>0?c/r.length:0,ge(r,n,t)}}function ge(r,e,t){let n=parseInt(t,10),i=n===0?1:0,s={};for(let a of Object.values(r.structures)){let o=a.name;s[o]||(s[o]={lost:0,destroyed:0,first:r.length}),a.team===i&&a.destroyed!==void 0&&(s[o].destroyed++,s[o].first=Math.min(s[o].first,a.destroyed)),a.team===n&&a.destroyed!==void 0&&s[o].lost++}e.stats.structures=s}function _e(r,e){let t=r.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,a=i.TeamTakedowns??0;t>0&&(i.DPM=(i.HeroDamage??0)/t,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/t,i.XPM=(i.ExperienceContribution??0)/t),i.KDA=s>0?(i.Takedowns??0)/s:i.Takedowns??0,i.KillParticipation=a>0?(i.Takedowns??0)/a:0,i.damageDonePerDeath=s>0?(i.HeroDamage??0)/s:i.HeroDamage??0,i.damageTakenPerDeath=s>0?(i.DamageTaken??0)/s:i.DamageTaken??0,i.healingDonePerDeath=s>0?((i.Healing??0)+(i.SelfHealing??0))/s:(i.Healing??0)+(i.SelfHealing??0),i.length=r.length}}function ye(r){let e=0,t=0,n=new Set(r.teams[0]?.ids||[]);for(let i of r.takedowns)n.has(i.victim.player)?t++:e++;r.team0Takedowns=e,r.team1Takedowns=t}function ve(r){let e=r.levelTimes[0]||{},t=r.levelTimes[1]||{},n=[];for(let u of Object.values(e))n.push({time:u.time,team:0,level:u.level});for(let u of Object.values(t))n.push({time:u.time,team:1,level:u.level});if(n.sort((u,c)=>u.time-c.time),n.length===0){r.levelAdvTimeline=[];return}let i=[],s=0,a=0,o=n[0].time;for(let u of n){let c=s-a;u.time>o&&i.push({start:o,end:u.time,levelDiff:c,length:u.time-o}),u.team===0?s=u.level:a=u.level,o=u.time}let l=s-a;r.length>o&&i.push({start:o,end:r.length,levelDiff:l,length:r.length-o}),r.levelAdvTimeline=i;for(let u of["0","1"]){let c=r.teams[u];if(!c)continue;let d=u==="0"?1:-1,f=0,b=0,m=0,h=0;for(let p of i){let g=p.levelDiff*d;g>0&&(f+=p.length),b=Math.max(b,g),m+=g*p.length,h+=p.length}c.stats.levelAdvTime=f,c.stats.maxLevelAdv=b,c.stats.avgLevelAdv=h>0?m/h:0,c.stats.levelAdvPct=r.length>0?f/r.length:0}}function Te(r,e){for(let s of["0","1"]){let a=r.teams[s];if(!a)continue;let o=[];for(let p of a.ids){let g=e[p];if(g)for(let y of g.deaths){o.push({time:y.time,delta:-1});let T=y.time+ke(g.gameStats.Level??1,y.time,r.length);T<r.length&&o.push({time:T,delta:1})}}o.sort((p,g)=>p.time-g.time);let l=[{time:0,heroes:a.ids.length}],u=a.ids.length;for(let p of o)u+=p.delta,u=Math.max(0,Math.min(a.ids.length,u)),l.push({time:p.time,heroes:u});a.stats.uptime=l;let c={};for(let p=0;p<l.length;p++){let y=(p<l.length-1?l[p+1].time:r.length)-l[p].time,T=String(l[p].heroes);c[T]=(c[T]||0)+y}a.stats.uptimeHistogram=c;let d=0,f=0;for(let p=0;p<l.length;p++){let y=(p<l.length-1?l[p+1].time:r.length)-l[p].time;d+=l[p].heroes*y,f+=y}a.stats.avgHeroesAlive=f>0?d/f:a.ids.length,a.stats.wipes=l.filter(p=>p.heroes===0).length,a.stats.aces=0;let b=s==="0"?"1":"0",m=r.teams[b];m?.stats?.uptime&&(a.stats.aces=m.stats.uptime.filter(p=>p.heroes===0).length);let h=r.XPBreakdown.filter(p=>p.team===parseInt(s,10));if(h.length>0){let g=h[h.length-1].breakdown.TrickleXP;a.stats.passiveXPGain=g,a.stats.passiveXPRate=r.length>0?g/(r.length/60):0}}for(let s of["0","1"]){let a=r.teams[s],o=s==="0"?"1":"0",l=r.teams[o];if(!a||!l)continue;let u=a.stats.uptime,c=l.stats.uptime;if(!u.length||!c.length)continue;let d=new Set;for(let m of u)d.add(m.time);for(let m of c)d.add(m.time);let f=Array.from(d).sort((m,h)=>m-h),b=0;for(let m=0;m<f.length;m++){let h=f[m],g=(m<f.length-1?f[m+1]:r.length)-h,y=V(u,h),T=V(c,h);y>T&&(b+=g)}a.stats.timeWithHeroAdv=b,a.stats.pctWithHeroAdv=r.length>0?b/r.length:0}let t=r.teams[0]?.stats.passiveXPRate??0,n=r.teams[1]?.stats.passiveXPRate??0,i=(t+n)/2;r.teams[0]&&(r.teams[0].stats.passiveXPDiff=i>0?t/i:0),r.teams[1]&&(r.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function V(r,e){let t=0;for(let n of r)if(n.time<=e)t=n.heroes;else break;return t}function ke(r,e,t){return r<=1?15:r<=5?15+(r-1)*2:r<=10?23+(r-5)*3:r<=15?38+(r-10)*4:58+(r-15)*5}function Se(r){let e=1/0,t=-1,n=1/0,i=-1;for(let a of Object.values(r.structures))a.destroyed!==void 0&&(a.name==="Fort"&&a.destroyed<e&&(e=a.destroyed,t=a.team===0?1:0),a.name==="Keep"&&a.destroyed<n&&(n=a.destroyed,i=a.team===0?1:0));t>=0&&(r.firstFort=t,r.firstFortWin=t===r.winner),i>=0&&(r.firstKeep=i,r.firstKeepWin=i===r.winner);let s=[...r.objective[0].events.map(a=>({...a,assignedTeam:0})),...r.objective[1].events.map(a=>({...a,assignedTeam:1}))].sort((a,o)=>a.loop-o.loop);s.length>0&&(r.firstObjective=s[0].assignedTeam,r.firstObjectiveWin=s[0].assignedTeam===r.winner),r.firstPickWin=r.picks.first===r.winner}function S(r){return Buffer.isBuffer(r)?r.toString("utf8"):typeof r=="string"?r:String(r??"")}var O=class{static async analyze(e){try{let t=new R(e);await t.init();let n=t.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=t.getTrackerEvents(),s=t.getInitData(),o=t.getHeader()?.m_version??{},l={m_flags:o.m_flags??0,m_major:o.m_major??0,m_minor:o.m_minor??0,m_revision:o.m_revision??0,m_build:o.m_baseBuild??t.getBuild(),m_baseBuild:o.m_baseBuild??t.getBuild()},u=n.m_playerList.find(b=>b?.m_toon)?.m_toon,c={version:l,map:S(n.m_title),date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:u?.m_region,playerIDs:[],heroes:[],levelTimes:{0:{},1:{}},bans:{0:[],1:[]},picks:{0:[],1:[],first:0},XPBreakdown:[],takedowns:[],mercs:{captures:[],units:{}},team0Takedowns:0,team1Takedowns:0,structures:{},objective:{0:{count:0,events:[]},1:{count:0,events:[]},type:""},teams:{0:this.emptyTeam(),1:this.emptyTeam()},winningPlayers:[],levelAdvTimeline:[],firstPickWin:!1};c.objective.type=c.map||"";let d={};for(let b of n.m_playerList){if(!b?.m_toon)continue;let m=b.m_toon,h=S(m.m_programId),p=`${m.m_region}-${h}-${m.m_realm}-${m.m_id}`,g=S(b.m_hero);d[p]={hero:g,name:S(b.m_name),uuid:m.m_id,region:m.m_region,realm:m.m_realm,ToonHandle:p,tag:0,team:b.m_teamId,win:b.m_result===1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},c.playerIDs.push(p),c.heroes.push(g)}this.extractBattleTags(t,n,d),this.extractDraft(s,c,n);let{playerIDMap:f}=$(i,d,c);this.processScoreEvents(i,f,d);for(let[b,m]of Object.entries(d)){m.win&&(c.winner=m.team,c.winningPlayers.push(b));let h=c.teams[m.team.toString()];h&&(h.level=Math.max(h.level,m.gameStats.Level||0),h.takedowns+=m.gameStats.Takedowns||0,h.ids.push(b))}return W(c,d),{status:1,match:c,players:d}}catch(t){return console.error("ReplayAnalyzer Error:",t),{status:-2,error:String(t)}}}static emptyTeam(){return{level:0,takedowns:0,ids:[],names:[],heroes:[],tags:[],stats:{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:{DamageTaken:0,CreepDamage:0,Healing:0,HeroDamage:0,MinionDamage:0,SelfHealing:0,SiegeDamage:0,ProtectionGivenToAllies:0,TeamfightDamageTaken:0,TeamfightHealingDone:0,TeamfightHeroDamage:0,TimeCCdEnemyHeroes:0,TimeRootingEnemyHeroes:0,TimeSpentDead:0,TimeStunningEnemyHeroes:0,TimeSilencingEnemyHeroes:0,avgTimeSpentDead:0,timeDeadPct:0},levelAdvTime:0,maxLevelAdv:0,avgLevelAdv:0,levelAdvPct:0,uptime:[],uptimeHistogram:{},wipes:0,avgHeroesAlive:0,aces:0,timeWithHeroAdv:0,pctWithHeroAdv:0,passiveXPRate:0,passiveXPDiff:0,passiveXPGain:0}}}static extractBattleTags(e,t,n){let i=e.extractFile("replay.server.battlelobby");if(i)try{let s=new RegExp("([\\p{L}\\d]{3,24}#\\d{4,10})[z\xD8]?","gu"),a=i.toString("utf8").match(s);if(!a)return;let o=0;for(let l of t.m_playerList){if(!l?.m_toon)continue;let u=S(l.m_name);for(;o<a.length;){let d=a[o].split("#"),f=d[0],b=d[1].replace(/[zØ]/g,"");if(o++,f===u){let m=l.m_toon,h=`${m.m_region}-${S(m.m_programId)}-${m.m_realm}-${m.m_id}`;n[h]&&(n[h].tag=parseInt(b,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,t,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let a=[],o=[];for(let c of n.m_playerList){if(!c?.m_toon)continue;let d=S(c.m_hero);c.m_teamId===0?a.push(d):c.m_teamId===1&&o.push(d)}if(t.picks={0:a,1:o,first:0},typeof s.m_gameMode=="number"&&(t.mode=s.m_gameMode),typeof s.m_gameType=="number"&&(t.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let c=s.m_firstPickTeam??0;t.picks.first=c}}catch{}}static processScoreEvents(e,t,n){for(let i of e){if(i._event!=="NNet.Replay.Tracker.SScoreResultEvent")continue;let s=i.m_instanceList;for(let a of s){let o=S(a.m_name),l=a.m_values,u=o.startsWith("EndOfMatchAward"),c=0;for(let d of l)if(d&&d.length>0&&d[0]!==void 0){let f=c+1,b=t[f];if(b&&n[b]){let m=typeof d[0]=="object"&&d[0]!==null&&"m_value"in d[0]?d[0].m_value:d[0];m!=null&&(u?m===1&&n[b].awards.push(o):n[b].gameStats[o]=m)}c++}else d&&d.length===0&&c++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};export{O as ReplayAnalyzer,R as ReplayParser};
|