@astefanski/storm-parser 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -57,6 +57,144 @@ if (result.status === 1) {
57
57
  }
58
58
  ```
59
59
 
60
+ ## Retrievable Data
61
+
62
+ The `ReplayAnalyzer` provides a comprehensive `AnalysisResult` containing structured data about the match, teams, and players.
63
+
64
+ ### Match Metadata
65
+
66
+ - **Basic Info**: Map name, match date (UTC), match length (seconds), game mode, game type, and region.
67
+ - **Result**: Winning team ID and a list of winning player handles.
68
+ - **Draft**:
69
+ - Hero picks for both teams.
70
+ - Hero bans for both teams (including ban order).
71
+ - First pick team identification.
72
+
73
+ ### In-Game Events
74
+
75
+ - **Takedowns**: Detailed kill events including killers, victim, time, and map coordinates (X, Y).
76
+ - **XP Breakdown**: Periodic and end-of-game breakdown of XP sources (Minion, Creep, Structure, Hero, and Trickle XP).
77
+ - **Mercenaries**:
78
+ - Capture events (camp type, team, time).
79
+ - Unit tracking (locations over time and total active duration).
80
+ - **Structures**: Tracking of all destroyed structures (Forts, Keeps, Towers, Wells) with destruction time and team ownership.
81
+ - **Objectives**: Map-specific objective progress, scores, and event types (e.g., Cursed Hollow Tributes, Volskaya Protectors).
82
+
83
+ ### Player Statistics
84
+
85
+ Each player object contains:
86
+
87
+ - **Profile**: Name, BattleTag, Hero played, Team, and Win/Loss status.
88
+ - **Game Stats**: All standard end-of-game stats (Damage, Healing, Deaths, Assists, Experience Contribution, Time Spent Dead, etc.).
89
+ - **Computed Analytics**:
90
+ - **DPM/HPM/XPM**: Damage/Healing/Experience per minute.
91
+ - **KDA**: Kill/Death/Assist ratio.
92
+ - **Kill Participation**: Percentage of team kills the player participated in.
93
+ - **Per-Death Stats**: Damage taken/done and healing per death.
94
+ - **Build**: Full talent choices for all tiers (Tier 1-7).
95
+ - **Awards**: Match awards (e.g., MVP, Siege Master).
96
+ - **Position Tracking**: Movement paths and life cycles of the player's hero unit.
97
+
98
+ ### Team Analytics
99
+
100
+ - **Performance Totals**: Aggregated stats for the entire team (Total Hero Damage, Self Healing, protection given, etc.).
101
+ - **Combat Stats**: KDA, average time spent dead, team wipes, and aces (enemy team wipes).
102
+ - **Level Dynamics**:
103
+ - Level-up timestamps.
104
+ - Level advantage timeline (who had the lead and by how much).
105
+ - Time spent with level/hero advantage.
106
+ - **Passive XP**: Passive XP gain rates and differences between teams.
107
+ - **Structure Control**: Counts of lost/destroyed Forts and Keeps, and identification of who destroyed the first Fort/Keep.
108
+
109
+ ## Technical Reference
110
+
111
+ After calling `ReplayAnalyzer.analyze()`, you receive an `AnalysisResult` object. Below are examples of how to access specific data points.
112
+
113
+ ### Accessing Player Data
114
+
115
+ Player data is stored in the `players` map, keyed by a unique `ToonHandle` (format: `region-programId-realm-id`).
116
+
117
+ ```typescript
118
+ const result = await ReplayAnalyzer.analyze("replay.StormReplay");
119
+
120
+ if (result.status === 1 && result.players) {
121
+ // result.match.playerIDs contains the list of all ToonHandles
122
+ const firstPlayerHandle = result.match.playerIDs[0];
123
+ const player = result.players[firstPlayerHandle];
124
+
125
+ console.log(`Hero: ${player.hero}`);
126
+ console.log(`BattleTag: ${player.name}#${player.tag}`);
127
+
128
+ // Accessing specific game stats
129
+ console.log(`Hero Damage: ${player.gameStats.HeroDamage}`);
130
+ console.log(`Deaths: ${player.gameStats.Deaths}`);
131
+
132
+ // Computed analytics
133
+ console.log(`DPM: ${player.gameStats.DPM}`);
134
+ console.log(`Kill Participation: ${player.gameStats.KillParticipation}`);
135
+ }
136
+ ```
137
+
138
+ ### Accessing Team Analytics
139
+
140
+ Team-specific data is located under `result.match.teams`, separated into "0" (Blue) and "1" (Red).
141
+
142
+ ```typescript
143
+ const team0 = result.match.teams["0"];
144
+
145
+ console.log(`Team Level: ${team0.level}`);
146
+ console.log(`Team Takedowns: ${team0.takedowns}`);
147
+
148
+ // Aggregated totals for the whole team
149
+ console.log(`Total Team Healing: ${team0.stats.totals.Healing}`);
150
+
151
+ // Team-level advantage stats
152
+ console.log(`Level Adv Time: ${team0.stats.levelAdvTime} seconds`);
153
+ console.log(`Average Heroes Alive: ${team0.stats.avgHeroesAlive}`);
154
+ ```
155
+
156
+ ### Accessing Match Events
157
+
158
+ The match object contains timelines for various game events.
159
+
160
+ ```typescript
161
+ // Takedowns (kills) with coordinates and participants
162
+ result.match.takedowns.forEach((event) => {
163
+ console.log(
164
+ `[${event.time}s] ${event.killers[0].hero} killed ${event.victim.hero} at (${event.x}, ${event.y})`,
165
+ );
166
+ });
167
+
168
+ // XP Breakdown over time
169
+ result.match.XPBreakdown.forEach((entry) => {
170
+ console.log(
171
+ `[${entry.time}s] Team ${entry.team} Level ${entry.teamLevel} - Minion XP: ${entry.breakdown.MinionXP}`,
172
+ );
173
+ });
174
+
175
+ // Mercenary captures
176
+ result.match.mercs.captures.forEach((capture) => {
177
+ console.log(
178
+ `Team ${capture.team} captured ${capture.type} at ${capture.time}s`,
179
+ );
180
+ });
181
+ ```
182
+
183
+ ### Accessing Draft Information
184
+
185
+ Draft data is split between picks and bans.
186
+
187
+ ```typescript
188
+ // Hero Bans by team and order
189
+ const team0Bans = result.match.bans["0"]; // Array of { hero: string, order: number }
190
+
191
+ // Hero Picks by team
192
+ const team1Picks = result.match.picks["1"]; // Array of hero names
193
+
194
+ // Which team had the first pick (0 or 1)
195
+ const firstPickTeam = result.match.picks.first;
196
+ ```
197
+
60
198
  ## Features
61
199
 
62
200
  - Parses `replay.details`, `replay.initData`, `replay.tracker.events`, and more.
package/dist/index.d.mts CHANGED
@@ -1,45 +1,3 @@
1
- interface Protocol {
2
- version: number;
3
- typeinfos: any[];
4
- game_event_types: Record<number, [number, string]>;
5
- message_event_types: Record<number, [number, string]>;
6
- tracker_event_types: Record<number, [number, string]>;
7
- game_eventid_typeid: number;
8
- message_eventid_typeid: number;
9
- tracker_eventid_typeid: number;
10
- svaruint32_typeid: number;
11
- replay_userid_typeid: number;
12
- replay_header_typeid: number;
13
- game_details_typeid: number;
14
- replay_initdata_typeid: number;
15
- }
16
- interface ReplayEvent {
17
- _event: string;
18
- _eventid: number;
19
- _gameloop: number;
20
- _userid?: unknown;
21
- _bits: number;
22
- [key: string]: unknown;
23
- }
24
- declare class ReplayParser {
25
- private mpq;
26
- private header;
27
- private build;
28
- private protocol?;
29
- private baseProtocol?;
30
- constructor(filenameOrData: string | Buffer);
31
- init(): void;
32
- private loadProtocolForBuild;
33
- private decodeEventStream;
34
- getDetails(): Record<string, unknown> | null;
35
- getInitData(): Record<string, unknown> | null;
36
- getTrackerEvents(): ReplayEvent[];
37
- getGameEvents(): ReplayEvent[];
38
- extractFile(filename: string): Buffer | null;
39
- getHeader(): Record<string, unknown> | undefined;
40
- getBuild(): number;
41
- }
42
-
43
1
  interface ReplayVersion {
44
2
  m_flags: number;
45
3
  m_major: number;
@@ -245,6 +203,18 @@ interface PlayerStat {
245
203
  tag: number;
246
204
  team: number;
247
205
  win: boolean;
206
+ heroLevel: number;
207
+ skin: string;
208
+ mount: string;
209
+ banner?: string;
210
+ spray?: string;
211
+ clanTag?: string;
212
+ highestLeague?: number;
213
+ combinedRaceLevels?: number;
214
+ randomSeed?: number;
215
+ announcer: string;
216
+ silenced: boolean;
217
+ voiceSilenced: boolean;
248
218
  gameStats: Record<string, number>;
249
219
  awards: string[];
250
220
  talents: TalentChoices;
@@ -255,10 +225,15 @@ interface PlayerStat {
255
225
  interface MatchStat {
256
226
  version: ReplayVersion;
257
227
  type?: number;
258
- mode?: number;
228
+ mode?: string;
259
229
  map?: string;
230
+ isBlizzardMap?: boolean;
231
+ timeLocalOffset?: number;
232
+ gameSpeed?: number;
233
+ randomValue?: number;
234
+ gameOptions?: Record<string, boolean | number>;
260
235
  date: string;
261
- rawDate?: number;
236
+ rawDate: number;
262
237
  length: number;
263
238
  winner: number;
264
239
  region?: number;
@@ -293,12 +268,247 @@ interface AnalysisResult {
293
268
  players?: Record<string, PlayerStat>;
294
269
  error?: string;
295
270
  }
271
+ interface RawHeader {
272
+ m_version: ReplayVersion;
273
+ m_type: number;
274
+ m_elapsedGameLoops: number;
275
+ m_useScaledTime: boolean;
276
+ m_ngdpRootKey: {
277
+ m_data: string | Buffer;
278
+ };
279
+ m_dataBuildNum: number;
280
+ m_replayCompatibilityMac: {
281
+ m_data: string | Buffer;
282
+ };
283
+ }
284
+ interface RawToon {
285
+ m_region: number;
286
+ m_programId: string | Buffer;
287
+ m_realm: number;
288
+ m_id: number;
289
+ }
290
+ interface RawColor {
291
+ m_a: number;
292
+ m_r: number;
293
+ m_g: number;
294
+ m_b: number;
295
+ }
296
+ interface RawPlayerDetails {
297
+ m_name: string | Buffer;
298
+ m_toon: RawToon;
299
+ m_race: string | Buffer;
300
+ m_color: RawColor;
301
+ m_control: number;
302
+ m_teamId: number;
303
+ m_handicap: number;
304
+ m_observe: number;
305
+ m_result: number;
306
+ m_workingSetSlotId: number;
307
+ m_hero: string | Buffer;
308
+ }
309
+ interface RawDetails {
310
+ m_playerList: RawPlayerDetails[];
311
+ m_title: string | Buffer;
312
+ m_difficulty: string | Buffer;
313
+ m_thumbnail: {
314
+ m_file: string | Buffer;
315
+ };
316
+ m_isBlizzardMap: boolean;
317
+ m_timeUTC: bigint | number;
318
+ m_timeLocalOffset: bigint | number;
319
+ m_mapFileName: string | Buffer;
320
+ m_cacheHandles: (string | Buffer)[];
321
+ m_miniSave: boolean;
322
+ m_gameSpeed: number;
323
+ m_defaultDifficulty: number;
324
+ m_modPaths: (string | Buffer)[] | null;
325
+ m_restartAsTransitionMap: boolean;
326
+ }
327
+ interface RawRacePreference {
328
+ m_race: number | null;
329
+ }
330
+ interface RawTeamPreference {
331
+ m_team: number | null;
332
+ }
333
+ interface RawUserInitialData {
334
+ m_name: string | Buffer;
335
+ m_clanTag: string | Buffer;
336
+ m_clanLogo: {
337
+ m_data: string | Buffer;
338
+ } | null;
339
+ m_highestLeague: number;
340
+ m_combinedRaceLevels: number;
341
+ m_randomSeed: number;
342
+ m_racePreference: RawRacePreference;
343
+ m_teamPreference: RawTeamPreference;
344
+ m_testMap: boolean;
345
+ m_testAuto: boolean;
346
+ m_examine: boolean;
347
+ m_customInterface: boolean;
348
+ m_testType: number;
349
+ m_observe: number;
350
+ m_hero: string | Buffer;
351
+ m_skin: string | Buffer;
352
+ m_mount: string | Buffer;
353
+ m_banner: string | Buffer;
354
+ m_spray: string | Buffer;
355
+ m_toonHandle: string | Buffer;
356
+ }
357
+ interface RawGameOptions {
358
+ m_lockTeams: boolean;
359
+ m_teamsTogether: boolean;
360
+ m_advancedSharedControl: boolean;
361
+ m_randomRaces: boolean;
362
+ m_battleNet: boolean;
363
+ m_amm: boolean;
364
+ m_competitive: boolean;
365
+ m_practice: boolean;
366
+ m_cooperative: boolean;
367
+ m_noVictoryOrDefeat: boolean;
368
+ m_heroDuplicatesAllowed: boolean;
369
+ m_fog: number;
370
+ m_observers: number;
371
+ m_userDifficulty: number;
372
+ m_clientDebugFlags: number;
373
+ m_ammId?: number;
374
+ }
375
+ interface RawSlot {
376
+ m_control: number;
377
+ m_userId: number;
378
+ m_teamId: number;
379
+ m_colorPref: number;
380
+ m_racePref: number;
381
+ m_difficulty: number;
382
+ m_aiBuild: number;
383
+ m_handicap: number;
384
+ m_observe: number;
385
+ m_logoIndex: number;
386
+ m_hero: string | Buffer;
387
+ m_skin: string | Buffer;
388
+ m_mount: string | Buffer;
389
+ m_artifacts: (string | Buffer)[];
390
+ m_workingSetSlotId: number;
391
+ m_rewards: number[];
392
+ m_toonHandle: string | Buffer;
393
+ m_licenses: number[];
394
+ m_tandemLeaderUserId: number;
395
+ m_commander: string | Buffer;
396
+ m_commanderLevel: number;
397
+ m_hasSilencePenalty: boolean;
398
+ m_hasVoiceSilencePenalty: boolean;
399
+ m_isBlizzardMap: boolean;
400
+ m_heroMasteryTiers: {
401
+ m_tier: number;
402
+ }[];
403
+ m_mountAdornment: string | Buffer;
404
+ m_spray: string | Buffer;
405
+ m_announcerPack: string | Buffer;
406
+ m_voiceLine: string | Buffer;
407
+ m_heroStatue: string | Buffer;
408
+ m_banner: string | Buffer;
409
+ }
410
+ interface RawGameDescription {
411
+ m_randomValue: number;
412
+ m_gameCacheName: string | Buffer;
413
+ m_gameOptions: RawGameOptions;
414
+ m_gameSpeed: number;
415
+ m_gameType: number;
416
+ m_maxUsers: number;
417
+ m_maxObservers: number;
418
+ m_maxPlayers: number;
419
+ m_maxTeams: number;
420
+ m_maxColors: number;
421
+ m_maxRaces: number;
422
+ m_maxControls: number;
423
+ m_mapSizeX: number;
424
+ m_mapSizeY: number;
425
+ m_mapFileSyncChecksum: number;
426
+ m_mapFileName: string | Buffer;
427
+ m_mapAuthorName: string | Buffer;
428
+ m_modFileSyncChecksum: number;
429
+ m_slotDescriptions: {
430
+ m_allowedColors: number[];
431
+ }[];
432
+ m_defaultDifficulty: number;
433
+ m_defaultAIBuild: number;
434
+ m_cacheHandles: (string | Buffer)[];
435
+ m_isBlizzardMap: boolean;
436
+ m_isPremadeFFA: boolean;
437
+ m_isCoopMode: boolean;
438
+ m_isRealtimeMode: boolean;
439
+ }
440
+ interface RawLobbyState {
441
+ m_userInitialData: RawUserInitialData[];
442
+ m_gameDescription: RawGameDescription;
443
+ m_lobbyState: {
444
+ m_maxUsers: number;
445
+ m_maxObservers: number;
446
+ m_slots: RawSlot[];
447
+ m_randomSeed: number;
448
+ m_hostUserId: number;
449
+ m_isSinglePlayer: boolean;
450
+ m_pickedMapTag: number;
451
+ m_gameDuration: number;
452
+ m_defaultDifficulty: number;
453
+ m_defaultAIBuild: number;
454
+ m_gameMode?: number;
455
+ m_gameType?: number;
456
+ m_firstPickTeam?: number;
457
+ };
458
+ }
459
+ interface RawInitData {
460
+ m_syncLobbyState: RawLobbyState;
461
+ }
462
+
463
+ type TypeInfo = [string, number[]?];
464
+ interface Protocol {
465
+ version: number;
466
+ typeinfos: TypeInfo[];
467
+ game_event_types: Record<number, [number, string]>;
468
+ message_event_types: Record<number, [number, string]>;
469
+ tracker_event_types: Record<number, [number, string]>;
470
+ game_eventid_typeid: number;
471
+ message_eventid_typeid: number;
472
+ tracker_eventid_typeid: number;
473
+ svaruint32_typeid: number;
474
+ replay_userid_typeid: number;
475
+ replay_header_typeid: number;
476
+ game_details_typeid: number;
477
+ replay_initdata_typeid: number;
478
+ }
479
+ interface ReplayEvent {
480
+ _event: string;
481
+ _eventid: number;
482
+ _gameloop: number;
483
+ _userid?: unknown;
484
+ _bits: number;
485
+ [key: string]: unknown;
486
+ }
487
+ declare class ReplayParser {
488
+ private mpq;
489
+ private header;
490
+ private build;
491
+ private protocol?;
492
+ private baseProtocol?;
493
+ constructor(filenameOrData: string | Buffer);
494
+ init(): void;
495
+ private loadProtocolForBuild;
496
+ private decodeEventStream;
497
+ getDetails(): RawDetails | null;
498
+ getInitData(): RawInitData | null;
499
+ getTrackerEvents(): ReplayEvent[];
500
+ getGameEvents(): ReplayEvent[];
501
+ extractFile(filename: string): Buffer | null;
502
+ getHeader(): RawHeader | undefined;
503
+ getBuild(): number;
504
+ }
296
505
 
297
506
  declare class ReplayAnalyzer {
298
507
  static analyze(filePath: string): Promise<AnalysisResult>;
299
508
  private static emptyTeam;
300
509
  private static extractBattleTags;
301
510
  private static extractDraft;
511
+ private static extractCosmetics;
302
512
  private static processScoreEvents;
303
513
  private static fileTimeToDate;
304
514
  }
package/dist/index.d.ts CHANGED
@@ -1,45 +1,3 @@
1
- interface Protocol {
2
- version: number;
3
- typeinfos: any[];
4
- game_event_types: Record<number, [number, string]>;
5
- message_event_types: Record<number, [number, string]>;
6
- tracker_event_types: Record<number, [number, string]>;
7
- game_eventid_typeid: number;
8
- message_eventid_typeid: number;
9
- tracker_eventid_typeid: number;
10
- svaruint32_typeid: number;
11
- replay_userid_typeid: number;
12
- replay_header_typeid: number;
13
- game_details_typeid: number;
14
- replay_initdata_typeid: number;
15
- }
16
- interface ReplayEvent {
17
- _event: string;
18
- _eventid: number;
19
- _gameloop: number;
20
- _userid?: unknown;
21
- _bits: number;
22
- [key: string]: unknown;
23
- }
24
- declare class ReplayParser {
25
- private mpq;
26
- private header;
27
- private build;
28
- private protocol?;
29
- private baseProtocol?;
30
- constructor(filenameOrData: string | Buffer);
31
- init(): void;
32
- private loadProtocolForBuild;
33
- private decodeEventStream;
34
- getDetails(): Record<string, unknown> | null;
35
- getInitData(): Record<string, unknown> | null;
36
- getTrackerEvents(): ReplayEvent[];
37
- getGameEvents(): ReplayEvent[];
38
- extractFile(filename: string): Buffer | null;
39
- getHeader(): Record<string, unknown> | undefined;
40
- getBuild(): number;
41
- }
42
-
43
1
  interface ReplayVersion {
44
2
  m_flags: number;
45
3
  m_major: number;
@@ -245,6 +203,18 @@ interface PlayerStat {
245
203
  tag: number;
246
204
  team: number;
247
205
  win: boolean;
206
+ heroLevel: number;
207
+ skin: string;
208
+ mount: string;
209
+ banner?: string;
210
+ spray?: string;
211
+ clanTag?: string;
212
+ highestLeague?: number;
213
+ combinedRaceLevels?: number;
214
+ randomSeed?: number;
215
+ announcer: string;
216
+ silenced: boolean;
217
+ voiceSilenced: boolean;
248
218
  gameStats: Record<string, number>;
249
219
  awards: string[];
250
220
  talents: TalentChoices;
@@ -255,10 +225,15 @@ interface PlayerStat {
255
225
  interface MatchStat {
256
226
  version: ReplayVersion;
257
227
  type?: number;
258
- mode?: number;
228
+ mode?: string;
259
229
  map?: string;
230
+ isBlizzardMap?: boolean;
231
+ timeLocalOffset?: number;
232
+ gameSpeed?: number;
233
+ randomValue?: number;
234
+ gameOptions?: Record<string, boolean | number>;
260
235
  date: string;
261
- rawDate?: number;
236
+ rawDate: number;
262
237
  length: number;
263
238
  winner: number;
264
239
  region?: number;
@@ -293,12 +268,247 @@ interface AnalysisResult {
293
268
  players?: Record<string, PlayerStat>;
294
269
  error?: string;
295
270
  }
271
+ interface RawHeader {
272
+ m_version: ReplayVersion;
273
+ m_type: number;
274
+ m_elapsedGameLoops: number;
275
+ m_useScaledTime: boolean;
276
+ m_ngdpRootKey: {
277
+ m_data: string | Buffer;
278
+ };
279
+ m_dataBuildNum: number;
280
+ m_replayCompatibilityMac: {
281
+ m_data: string | Buffer;
282
+ };
283
+ }
284
+ interface RawToon {
285
+ m_region: number;
286
+ m_programId: string | Buffer;
287
+ m_realm: number;
288
+ m_id: number;
289
+ }
290
+ interface RawColor {
291
+ m_a: number;
292
+ m_r: number;
293
+ m_g: number;
294
+ m_b: number;
295
+ }
296
+ interface RawPlayerDetails {
297
+ m_name: string | Buffer;
298
+ m_toon: RawToon;
299
+ m_race: string | Buffer;
300
+ m_color: RawColor;
301
+ m_control: number;
302
+ m_teamId: number;
303
+ m_handicap: number;
304
+ m_observe: number;
305
+ m_result: number;
306
+ m_workingSetSlotId: number;
307
+ m_hero: string | Buffer;
308
+ }
309
+ interface RawDetails {
310
+ m_playerList: RawPlayerDetails[];
311
+ m_title: string | Buffer;
312
+ m_difficulty: string | Buffer;
313
+ m_thumbnail: {
314
+ m_file: string | Buffer;
315
+ };
316
+ m_isBlizzardMap: boolean;
317
+ m_timeUTC: bigint | number;
318
+ m_timeLocalOffset: bigint | number;
319
+ m_mapFileName: string | Buffer;
320
+ m_cacheHandles: (string | Buffer)[];
321
+ m_miniSave: boolean;
322
+ m_gameSpeed: number;
323
+ m_defaultDifficulty: number;
324
+ m_modPaths: (string | Buffer)[] | null;
325
+ m_restartAsTransitionMap: boolean;
326
+ }
327
+ interface RawRacePreference {
328
+ m_race: number | null;
329
+ }
330
+ interface RawTeamPreference {
331
+ m_team: number | null;
332
+ }
333
+ interface RawUserInitialData {
334
+ m_name: string | Buffer;
335
+ m_clanTag: string | Buffer;
336
+ m_clanLogo: {
337
+ m_data: string | Buffer;
338
+ } | null;
339
+ m_highestLeague: number;
340
+ m_combinedRaceLevels: number;
341
+ m_randomSeed: number;
342
+ m_racePreference: RawRacePreference;
343
+ m_teamPreference: RawTeamPreference;
344
+ m_testMap: boolean;
345
+ m_testAuto: boolean;
346
+ m_examine: boolean;
347
+ m_customInterface: boolean;
348
+ m_testType: number;
349
+ m_observe: number;
350
+ m_hero: string | Buffer;
351
+ m_skin: string | Buffer;
352
+ m_mount: string | Buffer;
353
+ m_banner: string | Buffer;
354
+ m_spray: string | Buffer;
355
+ m_toonHandle: string | Buffer;
356
+ }
357
+ interface RawGameOptions {
358
+ m_lockTeams: boolean;
359
+ m_teamsTogether: boolean;
360
+ m_advancedSharedControl: boolean;
361
+ m_randomRaces: boolean;
362
+ m_battleNet: boolean;
363
+ m_amm: boolean;
364
+ m_competitive: boolean;
365
+ m_practice: boolean;
366
+ m_cooperative: boolean;
367
+ m_noVictoryOrDefeat: boolean;
368
+ m_heroDuplicatesAllowed: boolean;
369
+ m_fog: number;
370
+ m_observers: number;
371
+ m_userDifficulty: number;
372
+ m_clientDebugFlags: number;
373
+ m_ammId?: number;
374
+ }
375
+ interface RawSlot {
376
+ m_control: number;
377
+ m_userId: number;
378
+ m_teamId: number;
379
+ m_colorPref: number;
380
+ m_racePref: number;
381
+ m_difficulty: number;
382
+ m_aiBuild: number;
383
+ m_handicap: number;
384
+ m_observe: number;
385
+ m_logoIndex: number;
386
+ m_hero: string | Buffer;
387
+ m_skin: string | Buffer;
388
+ m_mount: string | Buffer;
389
+ m_artifacts: (string | Buffer)[];
390
+ m_workingSetSlotId: number;
391
+ m_rewards: number[];
392
+ m_toonHandle: string | Buffer;
393
+ m_licenses: number[];
394
+ m_tandemLeaderUserId: number;
395
+ m_commander: string | Buffer;
396
+ m_commanderLevel: number;
397
+ m_hasSilencePenalty: boolean;
398
+ m_hasVoiceSilencePenalty: boolean;
399
+ m_isBlizzardMap: boolean;
400
+ m_heroMasteryTiers: {
401
+ m_tier: number;
402
+ }[];
403
+ m_mountAdornment: string | Buffer;
404
+ m_spray: string | Buffer;
405
+ m_announcerPack: string | Buffer;
406
+ m_voiceLine: string | Buffer;
407
+ m_heroStatue: string | Buffer;
408
+ m_banner: string | Buffer;
409
+ }
410
+ interface RawGameDescription {
411
+ m_randomValue: number;
412
+ m_gameCacheName: string | Buffer;
413
+ m_gameOptions: RawGameOptions;
414
+ m_gameSpeed: number;
415
+ m_gameType: number;
416
+ m_maxUsers: number;
417
+ m_maxObservers: number;
418
+ m_maxPlayers: number;
419
+ m_maxTeams: number;
420
+ m_maxColors: number;
421
+ m_maxRaces: number;
422
+ m_maxControls: number;
423
+ m_mapSizeX: number;
424
+ m_mapSizeY: number;
425
+ m_mapFileSyncChecksum: number;
426
+ m_mapFileName: string | Buffer;
427
+ m_mapAuthorName: string | Buffer;
428
+ m_modFileSyncChecksum: number;
429
+ m_slotDescriptions: {
430
+ m_allowedColors: number[];
431
+ }[];
432
+ m_defaultDifficulty: number;
433
+ m_defaultAIBuild: number;
434
+ m_cacheHandles: (string | Buffer)[];
435
+ m_isBlizzardMap: boolean;
436
+ m_isPremadeFFA: boolean;
437
+ m_isCoopMode: boolean;
438
+ m_isRealtimeMode: boolean;
439
+ }
440
+ interface RawLobbyState {
441
+ m_userInitialData: RawUserInitialData[];
442
+ m_gameDescription: RawGameDescription;
443
+ m_lobbyState: {
444
+ m_maxUsers: number;
445
+ m_maxObservers: number;
446
+ m_slots: RawSlot[];
447
+ m_randomSeed: number;
448
+ m_hostUserId: number;
449
+ m_isSinglePlayer: boolean;
450
+ m_pickedMapTag: number;
451
+ m_gameDuration: number;
452
+ m_defaultDifficulty: number;
453
+ m_defaultAIBuild: number;
454
+ m_gameMode?: number;
455
+ m_gameType?: number;
456
+ m_firstPickTeam?: number;
457
+ };
458
+ }
459
+ interface RawInitData {
460
+ m_syncLobbyState: RawLobbyState;
461
+ }
462
+
463
+ type TypeInfo = [string, number[]?];
464
+ interface Protocol {
465
+ version: number;
466
+ typeinfos: TypeInfo[];
467
+ game_event_types: Record<number, [number, string]>;
468
+ message_event_types: Record<number, [number, string]>;
469
+ tracker_event_types: Record<number, [number, string]>;
470
+ game_eventid_typeid: number;
471
+ message_eventid_typeid: number;
472
+ tracker_eventid_typeid: number;
473
+ svaruint32_typeid: number;
474
+ replay_userid_typeid: number;
475
+ replay_header_typeid: number;
476
+ game_details_typeid: number;
477
+ replay_initdata_typeid: number;
478
+ }
479
+ interface ReplayEvent {
480
+ _event: string;
481
+ _eventid: number;
482
+ _gameloop: number;
483
+ _userid?: unknown;
484
+ _bits: number;
485
+ [key: string]: unknown;
486
+ }
487
+ declare class ReplayParser {
488
+ private mpq;
489
+ private header;
490
+ private build;
491
+ private protocol?;
492
+ private baseProtocol?;
493
+ constructor(filenameOrData: string | Buffer);
494
+ init(): void;
495
+ private loadProtocolForBuild;
496
+ private decodeEventStream;
497
+ getDetails(): RawDetails | null;
498
+ getInitData(): RawInitData | null;
499
+ getTrackerEvents(): ReplayEvent[];
500
+ getGameEvents(): ReplayEvent[];
501
+ extractFile(filename: string): Buffer | null;
502
+ getHeader(): RawHeader | undefined;
503
+ getBuild(): number;
504
+ }
296
505
 
297
506
  declare class ReplayAnalyzer {
298
507
  static analyze(filePath: string): Promise<AnalysisResult>;
299
508
  private static emptyTeam;
300
509
  private static extractBattleTags;
301
510
  private static extractDraft;
511
+ private static extractCosmetics;
302
512
  private static processScoreEvents;
303
513
  private static fileTimeToDate;
304
514
  }
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";var Ue=Object.create;var Y=Object.defineProperty;var Le=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var ze=Object.getPrototypeOf,Xe=Object.prototype.hasOwnProperty;var K=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Fe=(t,e)=>{for(var r in e)Y(t,r,{get:e[r],enumerable:!0})},ue=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Xe.call(t,i)&&i!==r&&Y(t,i,{get:()=>e[i],enumerable:!(n=Le(e,i))||n.enumerable});return t};var Z=(t,e,r)=>(r=t!=null?Ue(ze(t)):{},ue(e||!t||!t.__esModule?Y(r,"default",{value:t,enumerable:!0}):r,t)),Ke=t=>ue(Y({},"__esModule",{value:!0}),t);var be=K((Pt,me)=>{"use strict";var de=[0,1,3,7,15,31,63,127,255],j=function(t){this.stream=t,this.bitOffset=0,this.curByte=0,this.hasByte=!1};j.prototype._ensureByte=function(){this.hasByte||(this.curByte=this.stream.readByte(),this.hasByte=!0)};j.prototype.read=function(t){for(var e=0;t>0;){this._ensureByte();var r=8-this.bitOffset;if(t>=r)e<<=r,e|=de[r]&this.curByte,this.hasByte=!1,this.bitOffset=0,t-=r;else{e<<=t;var n=r-t;e|=(this.curByte&de[t]<<n)>>n,this.bitOffset+=t,t=0}}return e};j.prototype.seek=function(t){var e=t%8,r=(t-e)/8;this.bitOffset=e,this.stream.seek(r),this.hasByte=!1};j.prototype.pi=function(){var t=new Buffer(6),e;for(e=0;e<t.length;e++)t[e]=this.read(8);return t.toString("hex")};me.exports=j});var he=K((Rt,pe)=>{"use strict";var M=function(){};M.prototype.readByte=function(){throw new Error("abstract method readByte() not implemented")};M.prototype.read=function(t,e,r){for(var n=0;n<r;){var i=this.readByte();if(i<0)return n===0?-1:n;t[e++]=i,n++}return n};M.prototype.seek=function(t){throw new Error("abstract method seek() not implemented")};M.prototype.writeByte=function(t){throw new Error("abstract method readByte() not implemented")};M.prototype.write=function(t,e,r){var n;for(n=0;n<r;n++)this.writeByte(t[e++]);return r};M.prototype.flush=function(){};pe.exports=M});var ge=K((Bt,_e)=>{"use strict";_e.exports=(function(){var t=new Uint32Array([0,79764919,159529838,222504665,319059676,398814059,445009330,507990021,638119352,583659535,797628118,726387553,890018660,835552979,1015980042,944750013,1276238704,1221641927,1167319070,1095957929,1595256236,1540665371,1452775106,1381403509,1780037320,1859660671,1671105958,1733955601,2031960084,2111593891,1889500026,1952343757,2552477408,2632100695,2443283854,2506133561,2334638140,2414271883,2191915858,2254759653,3190512472,3135915759,3081330742,3009969537,2905550212,2850959411,2762807018,2691435357,3560074640,3505614887,3719321342,3648080713,3342211916,3287746299,3467911202,3396681109,4063920168,4143685023,4223187782,4286162673,3779000052,3858754371,3904687514,3967668269,881225847,809987520,1023691545,969234094,662832811,591600412,771767749,717299826,311336399,374308984,453813921,533576470,25881363,88864420,134795389,214552010,2023205639,2086057648,1897238633,1976864222,1804852699,1867694188,1645340341,1724971778,1587496639,1516133128,1461550545,1406951526,1302016099,1230646740,1142491917,1087903418,2896545431,2825181984,2770861561,2716262478,3215044683,3143675388,3055782693,3001194130,2326604591,2389456536,2200899649,2280525302,2578013683,2640855108,2418763421,2498394922,3769900519,3832873040,3912640137,3992402750,4088425275,4151408268,4197601365,4277358050,3334271071,3263032808,3476998961,3422541446,3585640067,3514407732,3694837229,3640369242,1762451694,1842216281,1619975040,1682949687,2047383090,2127137669,1938468188,2001449195,1325665622,1271206113,1183200824,1111960463,1543535498,1489069629,1434599652,1363369299,622672798,568075817,748617968,677256519,907627842,853037301,1067152940,995781531,51762726,131386257,177728840,240578815,269590778,349224269,429104020,491947555,4046411278,4126034873,4172115296,4234965207,3794477266,3874110821,3953728444,4016571915,3609705398,3555108353,3735388376,3664026991,3290680682,3236090077,3449943556,3378572211,3174993278,3120533705,3032266256,2961025959,2923101090,2868635157,2813903052,2742672763,2604032198,2683796849,2461293480,2524268063,2284983834,2364738477,2175806836,2238787779,1569362073,1498123566,1409854455,1355396672,1317987909,1246755826,1192025387,1137557660,2072149281,2135122070,1912620623,1992383480,1753615357,1816598090,1627664531,1707420964,295390185,358241886,404320391,483945776,43990325,106832002,186451547,266083308,932423249,861060070,1041341759,986742920,613929101,542559546,756411363,701822548,3316196985,3244833742,3425377559,3370778784,3601682597,3530312978,3744426955,3689838204,3819031489,3881883254,3928223919,4007849240,4037393693,4100235434,4180117107,4259748804,2310601993,2373574846,2151335527,2231098320,2596047829,2659030626,2470359227,2550115596,2947551409,2876312838,2788305887,2733848168,3165939309,3094707162,3040238851,2985771188]),e=function(){var r=4294967295;this.getCRC=function(){return~r>>>0},this.updateCRC=function(n){r=r<<8^t[(r>>>24^n)&255]},this.updateCRCRun=function(n,i){for(;i-- >0;)r=r<<8^t[(r>>>24^n)&255]}};return e})()});var ye=K((Dt,je)=>{je.exports={name:"seek-bzip",version:"2.0.0",contributors:["C. Scott Ananian (http://cscott.net)","Eli Skeggs","Kevin Kwok","Rob Landley (http://landley.net)"],description:"a pure-JavaScript Node.JS module for random-access decoding bzip2 data",main:"./lib/index.js",repository:{type:"git",url:"https://github.com/cscott/seek-bzip.git"},license:"MIT",bin:{"seek-bunzip":"./bin/seek-bunzip","seek-table":"./bin/seek-bzip-table"},directories:{test:"test"},dependencies:{commander:"^6.0.0"},devDependencies:{fibers:"^5.0.0",mocha:"^8.1.0"},scripts:{test:"mocha"}}});var Pe=K((Ct,Ee)=>{"use strict";var Ge=be(),G=he(),ke=ge(),we=ye(),J=20,xe=258,ve=0,Qe=1,$e=2,Ve=6,We=50,qe="314159265359",Ye="177245385090",Te=function(t,e){var r=t[e],n;for(n=e;n>0;n--)t[n]=t[n-1];return t[0]=r,r},g={OK:0,LAST_BLOCK:-1,NOT_BZIP_DATA:-2,UNEXPECTED_INPUT_EOF:-3,UNEXPECTED_OUTPUT_EOF:-4,DATA_ERROR:-5,OUT_OF_MEMORY:-6,OBSOLETE_INPUT:-7,END_OF_BLOCK:-8},B={};B[g.LAST_BLOCK]="Bad file checksum";B[g.NOT_BZIP_DATA]="Not bzip data";B[g.UNEXPECTED_INPUT_EOF]="Unexpected input EOF";B[g.UNEXPECTED_OUTPUT_EOF]="Unexpected output EOF";B[g.DATA_ERROR]="Data error";B[g.OUT_OF_MEMORY]="Out of memory";B[g.OBSOLETE_INPUT]="Obsolete (pre 0.9.5) bzip format not supported.";var T=function(t,e){var r=B[t]||"unknown error";e&&(r+=": "+e);var n=new TypeError(r);throw n.errorCode=t,n},k=function(t,e){this.writePos=this.writeCurrent=this.writeCount=0,this._start_bunzip(t,e)};k.prototype._init_block=function(){var t=this._get_next_block();return t?(this.blockCRC=new ke,!0):(this.writeCount=-1,!1)};k.prototype._start_bunzip=function(t,e){var r=new Buffer(4);(t.read(r,0,4)!==4||String.fromCharCode(r[0],r[1],r[2])!=="BZh")&&T(g.NOT_BZIP_DATA,"bad magic");var n=r[3]-48;(n<1||n>9)&&T(g.NOT_BZIP_DATA,"level out of range"),this.reader=new Ge(t),this.dbufSize=1e5*n,this.nextoutput=0,this.outputStream=e,this.streamCRC=0};k.prototype._get_next_block=function(){var t,e,r,n=this.reader,i=n.pi();if(i===Ye)return!1;i!==qe&&T(g.NOT_BZIP_DATA),this.targetBlockCRC=n.read(32)>>>0,this.streamCRC=(this.targetBlockCRC^(this.streamCRC<<1|this.streamCRC>>>31))>>>0,n.read(1)&&T(g.OBSOLETE_INPUT);var s=n.read(24);s>this.dbufSize&&T(g.DATA_ERROR,"initial position out of bounds");var o=n.read(16),a=new Buffer(256),f=0;for(t=0;t<16;t++)if(o&1<<15-t){var l=t*16;for(r=n.read(16),e=0;e<16;e++)r&1<<15-e&&(a[f++]=l+e)}var c=n.read(3);(c<$e||c>Ve)&&T(g.DATA_ERROR);var u=n.read(15);u===0&&T(g.DATA_ERROR);var b=new Buffer(256);for(t=0;t<c;t++)b[t]=t;var h=new Buffer(u);for(t=0;t<u;t++){for(e=0;n.read(1);e++)e>=c&&T(g.DATA_ERROR);h[t]=Te(b,e)}var m=f+2,p=[],d;for(e=0;e<c;e++){var _=new Buffer(m),y=new Uint16Array(J+1);for(o=n.read(5),t=0;t<m;t++){for(;(o<1||o>J)&&T(g.DATA_ERROR),!!n.read(1);)n.read(1)?o--:o++;_[t]=o}var x,E;for(x=E=_[0],t=1;t<m;t++)_[t]>E?E=_[t]:_[t]<x&&(x=_[t]);d={},p.push(d),d.permute=new Uint16Array(xe),d.limit=new Uint32Array(J+2),d.base=new Uint32Array(J+1),d.minLen=x,d.maxLen=E;var I=0;for(t=x;t<=E;t++)for(y[t]=d.limit[t]=0,o=0;o<m;o++)_[o]===t&&(d.permute[I++]=o);for(t=0;t<m;t++)y[_[t]]++;for(I=o=0,t=x;t<E;t++)I+=y[t],d.limit[t]=I-1,I<<=1,o+=y[t],d.base[t+1]=I-o;d.limit[E+1]=Number.MAX_VALUE,d.limit[E]=I+y[E]-1,d.base[x]=0}var U=new Uint32Array(256);for(t=0;t<256;t++)b[t]=t;var A=0,R=0,fe=0,S,X=this.dbuf=new Uint32Array(this.dbufSize);for(m=0;;){for(m--||(m=We-1,fe>=u&&T(g.DATA_ERROR),d=p[h[fe++]]),t=d.minLen,e=n.read(t);t>d.maxLen&&T(g.DATA_ERROR),!(e<=d.limit[t]);t++)e=e<<1|n.read(1);e-=d.base[t],(e<0||e>=xe)&&T(g.DATA_ERROR);var F=d.permute[e];if(F===ve||F===Qe){A||(A=1,o=0),F===ve?o+=A:o+=2*A,A<<=1;continue}if(A)for(A=0,R+o>this.dbufSize&&T(g.DATA_ERROR),S=a[b[0]],U[S]+=o;o--;)X[R++]=S;if(F>f)break;R>=this.dbufSize&&T(g.DATA_ERROR),t=F-1,S=Te(b,t),S=a[S],U[S]++,X[R++]=S}for((s<0||s>=R)&&T(g.DATA_ERROR),e=0,t=0;t<256;t++)r=e+U[t],U[t]=e,e=r;for(t=0;t<R;t++)S=X[t]&255,X[U[S]]|=t<<8,U[S]++;var q=0,ce=0,le=0;return R&&(q=X[s],ce=q&255,q>>=8,le=-1),this.writePos=q,this.writeCurrent=ce,this.writeCount=R,this.writeRun=le,!0};k.prototype._read_bunzip=function(t,e){var r,n,i;if(this.writeCount<0)return 0;for(var s=0,o=this.dbuf,a=this.writePos,f=this.writeCurrent,l=this.writeCount,c=this.outputsize,u=this.writeRun;l;){for(l--,n=f,a=o[a],f=a&255,a>>=8,u++===3?(r=f,i=n,f=-1):(r=1,i=f),this.blockCRC.updateCRCRun(i,r);r--;)this.outputStream.writeByte(i),this.nextoutput++;f!=n&&(u=0)}return this.writeCount=l,this.blockCRC.getCRC()!==this.targetBlockCRC&&T(g.DATA_ERROR,"Bad block CRC (got "+this.blockCRC.getCRC().toString(16)+" expected "+this.targetBlockCRC.toString(16)+")"),this.nextoutput};var ie=function(t){if("readByte"in t)return t;var e=new G;return e.pos=0,e.readByte=function(){return t[this.pos++]},e.seek=function(r){this.pos=r},e.eof=function(){return this.pos>=t.length},e},Se=function(t){var e=new G,r=!0;if(t)if(typeof t=="number")e.buffer=new Buffer(t),r=!1;else{if("writeByte"in t)return t;e.buffer=t,r=!1}else e.buffer=new Buffer(16384);return e.pos=0,e.writeByte=function(n){if(r&&this.pos>=this.buffer.length){var i=new Buffer(this.buffer.length*2);this.buffer.copy(i),this.buffer=i}this.buffer[this.pos++]=n},e.getBuffer=function(){if(this.pos!==this.buffer.length){if(!r)throw new TypeError("outputsize does not match decoded input");var n=new Buffer(this.pos);this.buffer.copy(n,0,0,this.pos),this.buffer=n}return this.buffer},e._coerced=!0,e};k.Err=g;k.decode=function(t,e,r){for(var n=ie(t),i=Se(e),s=new k(n,i);!("eof"in n&&n.eof());)if(s._init_block())s._read_bunzip();else{var o=s.reader.read(32)>>>0;if(o!==s.streamCRC&&T(g.DATA_ERROR,"Bad stream CRC (got "+s.streamCRC.toString(16)+" expected "+o.toString(16)+")"),r&&"eof"in n&&!n.eof())s._start_bunzip(n,i);else break}if("getBuffer"in i)return i.getBuffer()};k.decodeBlock=function(t,e,r){var n=ie(t),i=Se(r),s=new k(n,i);s.reader.seek(e);var o=s._get_next_block();if(o&&(s.blockCRC=new ke,s.writeCopies=0,s._read_bunzip()),"getBuffer"in i)return i.getBuffer()};k.table=function(t,e,r){var n=new G;n.delegate=ie(t),n.pos=0,n.readByte=function(){return this.pos++,this.delegate.readByte()},n.delegate.eof&&(n.eof=n.delegate.eof.bind(n.delegate));var i=new G;i.pos=0,i.writeByte=function(){this.pos++};for(var s=new k(n,i),o=s.dbufSize;!("eof"in n&&n.eof());){var a=n.pos*8+s.reader.bitOffset;if(s.reader.hasByte&&(a-=8),s._init_block()){var f=i.pos;s._read_bunzip(),e(a,i.pos-f)}else{var l=s.reader.read(32);if(r&&"eof"in n&&!n.eof())s._start_bunzip(n,i),console.assert(s.dbufSize===o,"shouldn't change block size within multistream file");else break}}};k.Stream=G;k.version=we.version;k.license=we.license;Ee.exports=k});var St={};Fe(St,{ReplayAnalyzer:()=>ne,ReplayParser:()=>z});module.exports=Ke(St);var De=Z(require("fs")),Q=Z(require("zlib")),Ze=Pe(),Re=512,Je=65536,et=16777216,tt=67108864,rt=2147483648,nt={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function it(){let t=1048577,e=new Uint32Array(256*5);for(let r=0;r<256;r++){let n=r;for(let i=0;i<5;i++){t=(t*125+3)%2796203;let s=(t&65535)<<16;t=(t*125+3)%2796203;let o=t&65535;e[n]=(s|o)>>>0,n+=256}}return e}var Be=it(),ee=class{file;header;hashTable;blockTable;files;constructor(e,r=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=De.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),r){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),r;if(e==="MPQ")r=this.readMPQHeader(),r.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();r=this.readMPQHeader(n.mpqHeaderOffset),r.offset=n.mpqHeaderOffset,r.userDataHeader=n}else throw new Error("Invalid MPQ file header");return r}readMPQHeader(e=0){let r=this.file.subarray(e,e+32),n={magic:r.toString("utf8",0,4),headerSize:r.readUInt32LE(4),archiveSize:r.readUInt32LE(8),formatVersion:r.readUInt16LE(12),sectorSizeShift:r.readUInt16LE(14),hashTableOffset:r.readUInt32LE(16),blockTableOffset:r.readUInt32LE(20),hashTableEntries:r.readUInt32LE(24),blockTableEntries:r.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),r={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return r.content=this.file.subarray(16,16+r.userDataHeaderSize),r}readTable(e){let r=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[r],s=this.header[n];if(i==null||s==null)throw new Error("Missing "+e+" offset or entries");let o=this.hash("("+e+" table)","TABLE"),a=this.file.subarray(i+(this.header.offset||0),i+(this.header.offset||0)+s*16);a=this.decrypt(a,o);let f=[];for(let l=0;l<s;l++){let c=a.subarray(l*16,l*16+16);e==="hash"?f.push({hashA:c.readUInt32LE(0),hashB:c.readUInt32LE(4),locale:c.readUInt16LE(8),platform:c.readUInt16LE(10),blockTableIndex:c.readUInt32LE(12)}):f.push({offset:c.readUInt32LE(0),archivedSize:c.readUInt32LE(4),size:c.readUInt32LE(8),flags:c.readUInt32LE(12)})}return f}getHashTableEntry(e){let r=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===r&&i.hashB===n)return i}readFile(e,r=!1){function n(f){let l=f[0];if(l===0)return f;if(l===2)return Q.inflateSync(f.subarray(1));if(l===16)return Ze.decode(f.subarray(1));try{return Q.inflateSync(f.subarray(1))}catch{return Q.inflateRawSync(f.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&rt))return null;if(s.archivedSize===0)return Buffer.alloc(0);let o=s.offset+(this.header.offset||0),a=this.file.subarray(o,o+s.archivedSize);if(s.flags&Je)throw new Error("Encryption is not supported");if(s.flags&et)s.flags&Re&&(r||s.size>s.archivedSize)&&(a=n(a));else{let f=512<<this.header.sectorSizeShift,l=Math.trunc(s.size/f)+1,c=!1;s.flags&tt&&(c=!0,l+=1);let u=[];for(let p=0;p<l+1;p++)u.push(a.readUInt32LE(4*p));let b=u.length-(c?2:1),h=[],m=s.size;for(let p=0;p<b;p++){let d=a.subarray(u[p],u[p+1]);s.flags&Re&&(r||m>d.length)&&(d=n(d)),m-=d.length,h.push(d)}a=Buffer.concat(h)}return a}hash(e,r){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let o=e.toUpperCase().charCodeAt(s);n=(Be[(nt[r]<<8)+o]^n+i)>>>0,i=o+n+i+(i<<5)+3>>>0}return n}decrypt(e,r){let n=r>>>0,i=4008636142,s=Buffer.alloc(e.length),o=e.length/4;for(let a=0;a<o;a++){i=i+Be[1024+(n&255)]>>>0;let f=e.readUInt32LE(a*4);f=(f^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=f+i+(i<<5)+3>>>0,s.writeUInt32LE(f,a*4)}return s}};var $=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},H=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},V=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,r="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=r==="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 $;let r=this._data.subarray(this._used,this._used+e);return this._used+=e,r}read_bits(e){let r=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new $;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?r+=s*Math.pow(2,e-n-i):r+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_bits_bigint(e){let r=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new $;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?r|=s<<BigInt(e-n-i):r|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_unaligned_bytes(e){let r=Buffer.alloc(e);for(let n=0;n<e;n++)r[n]=this.read_bits(8);return r}};var W=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new V(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new H(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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,r){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){let r=this._int(e);return[r,this._buffer.read_bits(r)]}_blob(e){let r=this._int(e);return this._buffer.read_aligned_bytes(r)}_bool(){return this._int([0,1])!==0}_choice(e,r){let n=this._int(e);if(!(n in r))throw new H(`Choice tag ${n} not found`);let i=r[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),r=Buffer.alloc(4);return r.writeUInt32BE(e,0),r.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 r={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null)r={...r,...i};else{if(e.length===1)return i;r[n[0]]=i}}else r[n[0]]=this.instance(n[1]);return r}},L=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new V(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new H(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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 H(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),r=(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=r?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,r){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){this._expect_skip(1);let r=Number(this._vint());return[r,this._buffer.read_aligned_bytes(Math.floor((r+7)/8))]}_blob(e){this._expect_skip(2);let r=Number(this._vint());return this._buffer.read_aligned_bytes(r)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,r){this._expect_skip(3);let n=Number(this._vint());if(!(n in r))return this._skip_instance(),{};let i=r[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 r={},n=Number(this._vint());for(let i=0;i<n;i++){let s=Number(this._vint()),o=e.find(a=>a[2]===s);if(o)if(o[0]==="__parent"){let a=this.instance(o[1]);typeof a=="object"&&a!==null?r={...r,...a}:e.length===1?r=a:r[o[0]]=a}else r[o[0]]=this.instance(o[1]);else this._skip_instance()}return r}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let r=Number(this._vint());for(let n=0;n<r;n++)this._skip_instance()}else if(e===1){let r=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((r+7)/8))}else if(e===2){let r=Number(this._vint());this._buffer.read_aligned_bytes(r)}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 r=Number(this._vint());for(let n=0;n<r;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 N=Z(require("fs")),D=Z(require("path"));function st(){let t=[];try{t.push(D.default.resolve(__dirname,"..","protocols")),t.push(D.default.resolve(__dirname,"..","..","protocols"))}catch{}t.push(D.default.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==D.default.dirname(e);){let r=D.default.join(e,"node_modules","@astefanski","storm-parser","protocols");t.push(r),e=D.default.dirname(e)}for(let r of t)if(N.default.existsSync(r)&&N.default.readdirSync(r).some(n=>n.endsWith(".json")))return r;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: `+t.join(", "))}var se=new Map,oe=null;function Ce(){return oe||(oe=st()),oe}function te(t){if(se.has(t))return se.get(t);let e=Ce(),r=D.default.join(e,`protocol${t}.json`);if(!N.default.existsSync(r))return null;let n=JSON.parse(N.default.readFileSync(r,"utf-8"));return se.set(t,n),n}function Ie(){let t=Ce();return N.default.readdirSync(t).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,r)=>e-r)}var z=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new ee(e,!1)}init(){let e=te(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 r=this.mpq.header.userDataHeader;if(!r||!r.content)throw new Error("Replay does not have a user data header");let n=new L(r.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 r=te(e);if(!r){let n=Ie(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);r=te(i)}if(!r)throw new Error(`No protocol found for build ${e}`);return r}*decodeEventStream(e,r,n,i){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let o=e.used_bits(),a=e.instance(this.protocol.svaruint32_typeid),f=Object.keys(a)[0],l=a[f];s+=l;let c=i?e.instance(this.protocol.replay_userid_typeid):void 0,u=Number(e.instance(r)),b=n[u];if(!b)throw new Error(`Unknown eventid(${u})`);let h=b[0],m=b[1],p=e.instance(h);p._event=m,p._eventid=u,p._gameloop=s,i&&(p._userid=c),e.byte_align(),p._bits=e.used_bits()-o,yield p}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new L(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 W(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 r=new L(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 r=new W(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 Ae={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},ot={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function O(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}function v(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?r.m_value:void 0}function re(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?O(r.m_value):void 0}function w(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?r.m_value:void 0}function ae(t,e){return`${t}-${e}`}function P(t,e){return(t-e)/16}function Me(t,e,r){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};r.levelTimes={0:{},1:{}},r.takedowns=[],r.structures={},r.XPBreakdown=[],r.mercs={captures:[],units:{}},r.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:r.map||""};for(let i of t)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":at(i,n,e,r);break;case"NNet.Replay.Tracker.SUnitBornEvent":lt(i,n,r);break;case"NNet.Replay.Tracker.SUnitDiedEvent":ut(i,n,r);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":dt(i,n);break}return r.loopGameStart=n.loopGameStart,r.loopLength=n.loopGameEnd,r.length=(n.loopGameEnd-n.loopGameStart)/16,mt(n,e),{playerIDMap:n.playerIDMap}}function at(t,e,r,n){let i=O(t.m_eventName),s=t.m_intData,o=t.m_stringData,a=t.m_fixedData;switch(i){case"PlayerInit":{let f=v(s,"PlayerID"),l=re(o,"ToonHandle");f!==void 0&&l&&r[l]&&(e.playerIDMap[f]=l);break}case"GatesOpen":e.loopGameStart=t._gameloop;break;case"LevelUp":{let f=v(s,"PlayerID"),l=v(s,"Level");if(f===void 0||l===void 0)break;let c;if(f>=1&&f<=5)c="0";else if(f>=6&&f<=10)c="1";else break;n.levelTimes[c][String(l)]||(n.levelTimes[c][String(l)]={loop:t._gameloop,level:l,team:c,time:P(t._gameloop,e.loopGameStart)});break}case"TalentChosen":{let f=v(s,"PlayerID"),l=re(o,"PurchaseName");if(f===void 0||!l)break;let c=e.playerIDMap[f];if(!c||!r[c])break;let u=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let b of u)if(!r[c].talents[b]){r[c].talents[b]=l;break}break}case"PlayerDeath":{let f=v(s,"PlayerID"),l=v(s,"KillingPlayer"),c=w(a,"PositionX")??0,u=w(a,"PositionY")??0;if(f===void 0)break;let b=e.playerIDMap[f];if(!b)break;let h={player:b,hero:r[b]?.hero||""},m=[],p=r[b]?.team;if(l&&l>0){let _=e.playerIDMap[l];_&&r[_]&&m.push({player:_,hero:r[_].hero})}for(let[_,y]of Object.entries(e.playerIDMap)){let x=r[y];!x||x.team===p||parseInt(_)===l||m.push({player:y,hero:x.hero})}let d={loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),x:c,y:u,killers:m,victim:h};n.takedowns.push(d),r[b]&&r[b].deaths.push(d);for(let _ of m)r[_.player]&&r[_.player].takedowns.push(d);break}case"PeriodicXPBreakdown":{let f=v(s,"Team");if(f===void 0)break;let l={GameTime:v(s,"GameTime")??0,PreviousGameTime:v(s,"PreviousGameTime")??0,MinionXP:w(a,"MinionXP")??0,CreepXP:w(a,"CreepXP")??0,StructureXP:w(a,"StructureXP")??0,HeroXP:w(a,"HeroXP")??0,TrickleXP:w(a,"TrickleXP")??0};n.XPBreakdown.push({loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),team:f,teamLevel:v(s,"TeamLevel")??0,breakdown:l,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let f=v(s,"Team");if(f===void 0)break;n.XPBreakdown.push({loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),team:f,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:w(a,"MinionXP")??0,CreepXP:w(a,"CreepXP")??0,StructureXP:w(a,"StructureXP")??0,HeroXP:w(a,"HeroXP")??0,TrickleXP:w(a,"TrickleXP")??0}});break}case"JungleCampCapture":{let f=v(s,"CampTeam")??v(s,"Team")??0;n.mercs.captures.push({loop:t._gameloop,type:re(o,"CampType")??re(o,"Result")??"Unknown Camp",team:f,time:P(t._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let f=v(s,"PlayerID");if(f===void 0)break;let l=e.playerIDMap[f];if(!l||!r[l])break;let c={},u=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(o)for(let b=0;b<o.length&&b<u.length;b++){let h=O(o[b].m_value);h&&(c[u[b]]=h)}r[l].talents=c;break}default:ct(i,t,e,n,s,a);break}}var ft=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function ct(t,e,r,n,i,s){if(!ft.has(t)||!i)return;let o=v(i,"Team")??v(i,"Event")??0,a=o===0||o===1?o:0,f={team:o,loop:e._gameloop,time:P(e._gameloop,r.loopGameStart),score:v(i,"Score"),duration:w(s,"Duration")};n.objective[a].events.push(f),n.objective[a].count=n.objective[a].events.length}function lt(t,e,r){let n=O(t.m_unitTypeName),i=t.m_unitTagIndex,s=t.m_unitTagRecycle,o=ae(i,s),a=t.m_controlPlayerId??t.m_upkeepPlayerId,f=t.m_x??0,l=t.m_y??0,c;if(a>=1&&a<=5?c=0:a>=6&&a<=10?c=1:a===11?c=0:a===12?c=1:c=a<=5?0:1,e.unitIndex[o]={type:n,playerId:a,team:c,x:f,y:l,bornLoop:t._gameloop},n.startsWith("Town")&&Ae[n]&&(r.structures[o]={type:n,name:Ae[n],tag:i,rtag:s,x:f,y:l,team:c}),n.startsWith("Hero")&&a>=1&&a<=10){e.heroUnits[o]=a,e.heroLives[a]||(e.heroLives[a]=[]);let u=P(t._gameloop,e.loopGameStart);e.heroLives[a].push({born:u,locations:[{x:f,y:l,time:u}],duration:0})}if(ot[n]){let b=r.mercs.captures[r.mercs.captures.length-1]?.loop??t._gameloop;r.mercs.units[o]={loop:b,team:c,type:n,locations:[{x:f,y:l}],time:P(b,e.loopGameStart),duration:0}}}function ut(t,e,r){let n=ae(t.m_unitTagIndex,t.m_unitTagRecycle),i=P(t._gameloop,e.loopGameStart);r.structures[n]&&(r.structures[n].destroyedLoop=t._gameloop,r.structures[n].destroyed=i),r.mercs.units[n]&&(r.mercs.units[n].duration=i-r.mercs.units[n].time);let s=e.heroUnits[n];if(s!==void 0&&e.heroLives[s]){let o=e.heroLives[s],a=o[o.length-1];a&&a.died===void 0&&(a.died=i,a.duration=i-a.born)}}function dt(t,e){let r=ae(t.m_unitTagIndex,t.m_unitTagRecycle),n=t.m_controlPlayerId??t.m_upkeepPlayerId;e.unitIndex[r]&&(e.unitIndex[r].playerId=n,n>=1&&n<=5?e.unitIndex[r].team=0:n>=6&&n<=10&&(e.unitIndex[r].team=1))}function mt(t,e){for(let[r,n]of Object.entries(t.heroLives)){let i=parseInt(r,10),s=t.playerIDMap[i];if(!s||!e[s])continue;for(let a of n)if(a.died===void 0){let f=a.locations[a.locations.length-1];a.duration=f?f.time-a.born:0}let o="";for(let[a,f]of Object.entries(t.heroUnits))if(f===i){o=a;break}o&&(e[s].units[o]={lives:n})}}var bt=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function pt(){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 ht(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:pt(),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 Oe(t,e){_t(t,e),yt(t,e),xt(t),vt(t),Tt(t,e),wt(t)}function _t(t,e){for(let r of["0","1"]){let n=t.teams[r];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=ht();for(let u of n.ids){let b=e[u];b&&(n.names.push(b.name),n.heroes.push(b.hero),n.tags.push(b.tag))}for(let u of n.ids){let b=e[u];if(b)for(let h of bt){let m=b.gameStats[h];typeof m=="number"&&(n.stats.totals[h]+=m)}}let i=n.ids.reduce((u,b)=>u+(e[b]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),t.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(t.length*n.ids.length));let s=n.takedowns,o=i;n.stats.KDA=o>0?s/o:s,n.stats.PPK=s>0?t.takedowns.filter(u=>n.ids.includes(u.killers[0]?.player)).reduce((u,b)=>u+b.killers.length,0)/s:0;let a=t.levelTimes[r];a?.["10"]&&(n.stats.timeTo10=a[10].time);let f=parseInt(r,10),l=t.mercs.captures.filter(u=>u.team===f);n.stats.mercCaptures=l.length;let c=0;for(let u of Object.values(t.mercs.units))u.team===f&&u.duration>0&&(c+=u.duration);n.stats.mercUptime=c,n.stats.mercUptimePercent=t.length>0?c/t.length:0,gt(t,n,r)}}function gt(t,e,r){let n=parseInt(r,10),i=n===0?1:0,s={};for(let o of Object.values(t.structures)){let a=o.name;s[a]||(s[a]={lost:0,destroyed:0,first:t.length}),o.team===i&&o.destroyed!==void 0&&(s[a].destroyed++,s[a].first=Math.min(s[a].first,o.destroyed)),o.team===n&&o.destroyed!==void 0&&s[a].lost++}e.stats.structures=s}function yt(t,e){let r=t.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,o=i.TeamTakedowns??0;r>0&&(i.DPM=(i.HeroDamage??0)/r,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/r,i.XPM=(i.ExperienceContribution??0)/r),i.KDA=s>0?(i.Takedowns??0)/s:i.Takedowns??0,i.KillParticipation=o>0?(i.Takedowns??0)/o: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=t.length}}function xt(t){let e=0,r=0,n=new Set(t.teams[0]?.ids||[]);for(let i of t.takedowns)n.has(i.victim.player)?r++:e++;t.team0Takedowns=e,t.team1Takedowns=r}function vt(t){let e=t.levelTimes[0]||{},r=t.levelTimes[1]||{},n=[];for(let l of Object.values(e))n.push({time:l.time,team:0,level:l.level});for(let l of Object.values(r))n.push({time:l.time,team:1,level:l.level});if(n.sort((l,c)=>l.time-c.time),n.length===0){t.levelAdvTimeline=[];return}let i=[],s=0,o=0,a=n[0].time;for(let l of n){let c=s-o;l.time>a&&i.push({start:a,end:l.time,levelDiff:c,length:l.time-a}),l.team===0?s=l.level:o=l.level,a=l.time}let f=s-o;t.length>a&&i.push({start:a,end:t.length,levelDiff:f,length:t.length-a}),t.levelAdvTimeline=i;for(let l of["0","1"]){let c=t.teams[l];if(!c)continue;let u=l==="0"?1:-1,b=0,h=0,m=0,p=0;for(let d of i){let _=d.levelDiff*u;_>0&&(b+=d.length),h=Math.max(h,_),m+=_*d.length,p+=d.length}c.stats.levelAdvTime=b,c.stats.maxLevelAdv=h,c.stats.avgLevelAdv=p>0?m/p:0,c.stats.levelAdvPct=t.length>0?b/t.length:0}}function Tt(t,e){for(let s of["0","1"]){let o=t.teams[s];if(!o)continue;let a=[];for(let d of o.ids){let _=e[d];if(_)for(let y of _.deaths){a.push({time:y.time,delta:-1});let x=y.time+kt(_.gameStats.Level??1,y.time,t.length);x<t.length&&a.push({time:x,delta:1})}}a.sort((d,_)=>d.time-_.time);let f=[{time:0,heroes:o.ids.length}],l=o.ids.length;for(let d of a)l+=d.delta,l=Math.max(0,Math.min(o.ids.length,l)),f.push({time:d.time,heroes:l});o.stats.uptime=f;let c={};for(let d=0;d<f.length;d++){let y=(d<f.length-1?f[d+1].time:t.length)-f[d].time,x=String(f[d].heroes);c[x]=(c[x]||0)+y}o.stats.uptimeHistogram=c;let u=0,b=0;for(let d=0;d<f.length;d++){let y=(d<f.length-1?f[d+1].time:t.length)-f[d].time;u+=f[d].heroes*y,b+=y}o.stats.avgHeroesAlive=b>0?u/b:o.ids.length,o.stats.wipes=f.filter(d=>d.heroes===0).length,o.stats.aces=0;let h=s==="0"?"1":"0",m=t.teams[h];m?.stats?.uptime&&(o.stats.aces=m.stats.uptime.filter(d=>d.heroes===0).length);let p=t.XPBreakdown.filter(d=>d.team===parseInt(s,10));if(p.length>0){let _=p[p.length-1].breakdown.TrickleXP;o.stats.passiveXPGain=_,o.stats.passiveXPRate=t.length>0?_/(t.length/60):0}}for(let s of["0","1"]){let o=t.teams[s],a=s==="0"?"1":"0",f=t.teams[a];if(!o||!f)continue;let l=o.stats.uptime,c=f.stats.uptime;if(!l.length||!c.length)continue;let u=new Set;for(let m of l)u.add(m.time);for(let m of c)u.add(m.time);let b=Array.from(u).sort((m,p)=>m-p),h=0;for(let m=0;m<b.length;m++){let p=b[m],_=(m<b.length-1?b[m+1]:t.length)-p,y=He(l,p),x=He(c,p);y>x&&(h+=_)}o.stats.timeWithHeroAdv=h,o.stats.pctWithHeroAdv=t.length>0?h/t.length:0}let r=t.teams[0]?.stats.passiveXPRate??0,n=t.teams[1]?.stats.passiveXPRate??0,i=(r+n)/2;t.teams[0]&&(t.teams[0].stats.passiveXPDiff=i>0?r/i:0),t.teams[1]&&(t.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function He(t,e){let r=0;for(let n of t)if(n.time<=e)r=n.heroes;else break;return r}function kt(t,e,r){return t<=1?15:t<=5?15+(t-1)*2:t<=10?23+(t-5)*3:t<=15?38+(t-10)*4:58+(t-15)*5}function wt(t){let e=1/0,r=-1,n=1/0,i=-1;for(let o of Object.values(t.structures))o.destroyed!==void 0&&(o.name==="Fort"&&o.destroyed<e&&(e=o.destroyed,r=o.team===0?1:0),o.name==="Keep"&&o.destroyed<n&&(n=o.destroyed,i=o.team===0?1:0));r>=0&&(t.firstFort=r,t.firstFortWin=r===t.winner),i>=0&&(t.firstKeep=i,t.firstKeepWin=i===t.winner);let s=[...t.objective[0].events.map(o=>({...o,assignedTeam:0})),...t.objective[1].events.map(o=>({...o,assignedTeam:1}))].sort((o,a)=>o.loop-a.loop);s.length>0&&(t.firstObjective=s[0].assignedTeam,t.firstObjectiveWin=s[0].assignedTeam===t.winner),t.firstPickWin=t.picks.first===t.winner}function C(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}var ne=class{static async analyze(e){try{let r=new z(e);await r.init();let n=r.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=r.getTrackerEvents(),s=r.getInitData(),a=r.getHeader()?.m_version??{},f={m_flags:a.m_flags??0,m_major:a.m_major??0,m_minor:a.m_minor??0,m_revision:a.m_revision??0,m_build:a.m_baseBuild??r.getBuild(),m_baseBuild:a.m_baseBuild??r.getBuild()},l=n.m_playerList.find(h=>h?.m_toon)?.m_toon,c={version:f,map:C(n.m_title),date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:l?.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 u={};for(let h of n.m_playerList){if(!h?.m_toon)continue;let m=h.m_toon,p=C(m.m_programId),d=`${m.m_region}-${p}-${m.m_realm}-${m.m_id}`,_=C(h.m_hero);u[d]={hero:_,name:C(h.m_name),uuid:m.m_id,region:m.m_region,realm:m.m_realm,ToonHandle:d,tag:0,team:h.m_teamId,win:h.m_result===1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},c.playerIDs.push(d),c.heroes.push(_)}this.extractBattleTags(r,n,u),this.extractDraft(s,c,n);let{playerIDMap:b}=Me(i,u,c);this.processScoreEvents(i,b,u);for(let[h,m]of Object.entries(u)){m.win&&(c.winner=m.team,c.winningPlayers.push(h));let p=c.teams[m.team.toString()];p&&(p.level=Math.max(p.level,m.gameStats.Level||0),p.takedowns+=m.gameStats.Takedowns||0,p.ids.push(h))}return Oe(c,u),{status:1,match:c,players:u}}catch(r){return console.error("ReplayAnalyzer Error:",r),{status:-2,error:String(r)}}}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,r,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"),o=i.toString("utf8").match(s);if(!o)return;let a=0;for(let f of r.m_playerList){if(!f?.m_toon)continue;let l=C(f.m_name);for(;a<o.length;){let u=o[a].split("#"),b=u[0],h=u[1].replace(/[zØ]/g,"");if(a++,b===l){let m=f.m_toon,p=`${m.m_region}-${C(m.m_programId)}-${m.m_realm}-${m.m_id}`;n[p]&&(n[p].tag=parseInt(h,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let o=[],a=[];for(let c of n.m_playerList){if(!c?.m_toon)continue;let u=C(c.m_hero);c.m_teamId===0?o.push(u):c.m_teamId===1&&a.push(u)}if(r.picks={0:o,1:a,first:0},typeof s.m_gameMode=="number"&&(r.mode=s.m_gameMode),typeof s.m_gameType=="number"&&(r.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let c=s.m_firstPickTeam??0;r.picks.first=c}}catch{}}static processScoreEvents(e,r,n){for(let i of e){if(i._event!=="NNet.Replay.Tracker.SScoreResultEvent")continue;let s=i.m_instanceList;for(let o of s){let a=C(o.m_name),f=o.m_values,l=a.startsWith("EndOfMatchAward"),c=0;for(let u of f)if(u&&u.length>0&&u[0]!==void 0){let b=c+1,h=r[b];if(h&&n[h]){let m=typeof u[0]=="object"&&u[0]!==null&&"m_value"in u[0]?u[0].m_value:u[0];m!=null&&(l?m===1&&n[h].awards.push(a):n[h].gameStats[a]=m)}c++}else u&&u.length===0&&c++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};0&&(module.exports={ReplayAnalyzer,ReplayParser});
1
+ "use strict";var Oe=Object.create;var Y=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var ze=Object.getPrototypeOf,Xe=Object.prototype.hasOwnProperty;var j=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Fe=(t,e)=>{for(var r in e)Y(t,r,{get:e[r],enumerable:!0})},de=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Xe.call(t,i)&&i!==r&&Y(t,i,{get:()=>e[i],enumerable:!(n=Ue(e,i))||n.enumerable});return t};var Z=(t,e,r)=>(r=t!=null?Oe(ze(t)):{},de(e||!t||!t.__esModule?Y(r,"default",{value:t,enumerable:!0}):r,t)),je=t=>de(Y({},"__esModule",{value:!0}),t);var be=j((Pt,me)=>{"use strict";var ue=[0,1,3,7,15,31,63,127,255],K=function(t){this.stream=t,this.bitOffset=0,this.curByte=0,this.hasByte=!1};K.prototype._ensureByte=function(){this.hasByte||(this.curByte=this.stream.readByte(),this.hasByte=!0)};K.prototype.read=function(t){for(var e=0;t>0;){this._ensureByte();var r=8-this.bitOffset;if(t>=r)e<<=r,e|=ue[r]&this.curByte,this.hasByte=!1,this.bitOffset=0,t-=r;else{e<<=t;var n=r-t;e|=(this.curByte&ue[t]<<n)>>n,this.bitOffset+=t,t=0}}return e};K.prototype.seek=function(t){var e=t%8,r=(t-e)/8;this.bitOffset=e,this.stream.seek(r),this.hasByte=!1};K.prototype.pi=function(){var t=new Buffer(6),e;for(e=0;e<t.length;e++)t[e]=this.read(8);return t.toString("hex")};me.exports=K});var he=j((Dt,pe)=>{"use strict";var M=function(){};M.prototype.readByte=function(){throw new Error("abstract method readByte() not implemented")};M.prototype.read=function(t,e,r){for(var n=0;n<r;){var i=this.readByte();if(i<0)return n===0?-1:n;t[e++]=i,n++}return n};M.prototype.seek=function(t){throw new Error("abstract method seek() not implemented")};M.prototype.writeByte=function(t){throw new Error("abstract method readByte() not implemented")};M.prototype.write=function(t,e,r){var n;for(n=0;n<r;n++)this.writeByte(t[e++]);return r};M.prototype.flush=function(){};pe.exports=M});var ge=j((Rt,_e)=>{"use strict";_e.exports=(function(){var t=new Uint32Array([0,79764919,159529838,222504665,319059676,398814059,445009330,507990021,638119352,583659535,797628118,726387553,890018660,835552979,1015980042,944750013,1276238704,1221641927,1167319070,1095957929,1595256236,1540665371,1452775106,1381403509,1780037320,1859660671,1671105958,1733955601,2031960084,2111593891,1889500026,1952343757,2552477408,2632100695,2443283854,2506133561,2334638140,2414271883,2191915858,2254759653,3190512472,3135915759,3081330742,3009969537,2905550212,2850959411,2762807018,2691435357,3560074640,3505614887,3719321342,3648080713,3342211916,3287746299,3467911202,3396681109,4063920168,4143685023,4223187782,4286162673,3779000052,3858754371,3904687514,3967668269,881225847,809987520,1023691545,969234094,662832811,591600412,771767749,717299826,311336399,374308984,453813921,533576470,25881363,88864420,134795389,214552010,2023205639,2086057648,1897238633,1976864222,1804852699,1867694188,1645340341,1724971778,1587496639,1516133128,1461550545,1406951526,1302016099,1230646740,1142491917,1087903418,2896545431,2825181984,2770861561,2716262478,3215044683,3143675388,3055782693,3001194130,2326604591,2389456536,2200899649,2280525302,2578013683,2640855108,2418763421,2498394922,3769900519,3832873040,3912640137,3992402750,4088425275,4151408268,4197601365,4277358050,3334271071,3263032808,3476998961,3422541446,3585640067,3514407732,3694837229,3640369242,1762451694,1842216281,1619975040,1682949687,2047383090,2127137669,1938468188,2001449195,1325665622,1271206113,1183200824,1111960463,1543535498,1489069629,1434599652,1363369299,622672798,568075817,748617968,677256519,907627842,853037301,1067152940,995781531,51762726,131386257,177728840,240578815,269590778,349224269,429104020,491947555,4046411278,4126034873,4172115296,4234965207,3794477266,3874110821,3953728444,4016571915,3609705398,3555108353,3735388376,3664026991,3290680682,3236090077,3449943556,3378572211,3174993278,3120533705,3032266256,2961025959,2923101090,2868635157,2813903052,2742672763,2604032198,2683796849,2461293480,2524268063,2284983834,2364738477,2175806836,2238787779,1569362073,1498123566,1409854455,1355396672,1317987909,1246755826,1192025387,1137557660,2072149281,2135122070,1912620623,1992383480,1753615357,1816598090,1627664531,1707420964,295390185,358241886,404320391,483945776,43990325,106832002,186451547,266083308,932423249,861060070,1041341759,986742920,613929101,542559546,756411363,701822548,3316196985,3244833742,3425377559,3370778784,3601682597,3530312978,3744426955,3689838204,3819031489,3881883254,3928223919,4007849240,4037393693,4100235434,4180117107,4259748804,2310601993,2373574846,2151335527,2231098320,2596047829,2659030626,2470359227,2550115596,2947551409,2876312838,2788305887,2733848168,3165939309,3094707162,3040238851,2985771188]),e=function(){var r=4294967295;this.getCRC=function(){return~r>>>0},this.updateCRC=function(n){r=r<<8^t[(r>>>24^n)&255]},this.updateCRCRun=function(n,i){for(;i-- >0;)r=r<<8^t[(r>>>24^n)&255]}};return e})()});var ye=j((Bt,Ke)=>{Ke.exports={name:"seek-bzip",version:"2.0.0",contributors:["C. Scott Ananian (http://cscott.net)","Eli Skeggs","Kevin Kwok","Rob Landley (http://landley.net)"],description:"a pure-JavaScript Node.JS module for random-access decoding bzip2 data",main:"./lib/index.js",repository:{type:"git",url:"https://github.com/cscott/seek-bzip.git"},license:"MIT",bin:{"seek-bunzip":"./bin/seek-bunzip","seek-table":"./bin/seek-bzip-table"},directories:{test:"test"},dependencies:{commander:"^6.0.0"},devDependencies:{fibers:"^5.0.0",mocha:"^8.1.0"},scripts:{test:"mocha"}}});var Pe=j((It,Ee)=>{"use strict";var Ge=be(),G=he(),Se=ge(),ke=ye(),J=20,xe=258,ve=0,Qe=1,$e=2,Ve=6,We=50,qe="314159265359",Ye="177245385090",Te=function(t,e){var r=t[e],n;for(n=e;n>0;n--)t[n]=t[n-1];return t[0]=r,r},g={OK:0,LAST_BLOCK:-1,NOT_BZIP_DATA:-2,UNEXPECTED_INPUT_EOF:-3,UNEXPECTED_OUTPUT_EOF:-4,DATA_ERROR:-5,OUT_OF_MEMORY:-6,OBSOLETE_INPUT:-7,END_OF_BLOCK:-8},B={};B[g.LAST_BLOCK]="Bad file checksum";B[g.NOT_BZIP_DATA]="Not bzip data";B[g.UNEXPECTED_INPUT_EOF]="Unexpected input EOF";B[g.UNEXPECTED_OUTPUT_EOF]="Unexpected output EOF";B[g.DATA_ERROR]="Data error";B[g.OUT_OF_MEMORY]="Out of memory";B[g.OBSOLETE_INPUT]="Obsolete (pre 0.9.5) bzip format not supported.";var T=function(t,e){var r=B[t]||"unknown error";e&&(r+=": "+e);var n=new TypeError(r);throw n.errorCode=t,n},k=function(t,e){this.writePos=this.writeCurrent=this.writeCount=0,this._start_bunzip(t,e)};k.prototype._init_block=function(){var t=this._get_next_block();return t?(this.blockCRC=new Se,!0):(this.writeCount=-1,!1)};k.prototype._start_bunzip=function(t,e){var r=new Buffer(4);(t.read(r,0,4)!==4||String.fromCharCode(r[0],r[1],r[2])!=="BZh")&&T(g.NOT_BZIP_DATA,"bad magic");var n=r[3]-48;(n<1||n>9)&&T(g.NOT_BZIP_DATA,"level out of range"),this.reader=new Ge(t),this.dbufSize=1e5*n,this.nextoutput=0,this.outputStream=e,this.streamCRC=0};k.prototype._get_next_block=function(){var t,e,r,n=this.reader,i=n.pi();if(i===Ye)return!1;i!==qe&&T(g.NOT_BZIP_DATA),this.targetBlockCRC=n.read(32)>>>0,this.streamCRC=(this.targetBlockCRC^(this.streamCRC<<1|this.streamCRC>>>31))>>>0,n.read(1)&&T(g.OBSOLETE_INPUT);var s=n.read(24);s>this.dbufSize&&T(g.DATA_ERROR,"initial position out of bounds");var a=n.read(16),o=new Buffer(256),c=0;for(t=0;t<16;t++)if(a&1<<15-t){var f=t*16;for(r=n.read(16),e=0;e<16;e++)r&1<<15-e&&(o[c++]=f+e)}var l=n.read(3);(l<$e||l>Ve)&&T(g.DATA_ERROR);var d=n.read(15);d===0&&T(g.DATA_ERROR);var u=new Buffer(256);for(t=0;t<l;t++)u[t]=t;var m=new Buffer(d);for(t=0;t<d;t++){for(e=0;n.read(1);e++)e>=l&&T(g.DATA_ERROR);m[t]=Te(u,e)}var b=c+2,h=[],p;for(e=0;e<l;e++){var _=new Buffer(b),y=new Uint16Array(J+1);for(a=n.read(5),t=0;t<b;t++){for(;(a<1||a>J)&&T(g.DATA_ERROR),!!n.read(1);)n.read(1)?a--:a++;_[t]=a}var x,P;for(x=P=_[0],t=1;t<b;t++)_[t]>P?P=_[t]:_[t]<x&&(x=_[t]);p={},h.push(p),p.permute=new Uint16Array(xe),p.limit=new Uint32Array(J+2),p.base=new Uint32Array(J+1),p.minLen=x,p.maxLen=P;var C=0;for(t=x;t<=P;t++)for(y[t]=p.limit[t]=0,a=0;a<b;a++)_[a]===t&&(p.permute[C++]=a);for(t=0;t<b;t++)y[_[t]]++;for(C=a=0,t=x;t<P;t++)C+=y[t],p.limit[t]=C-1,C<<=1,a+=y[t],p.base[t+1]=C-a;p.limit[P+1]=Number.MAX_VALUE,p.limit[P]=C+y[P]-1,p.base[x]=0}var O=new Uint32Array(256);for(t=0;t<256;t++)u[t]=t;var A=0,R=0,ce=0,E,X=this.dbuf=new Uint32Array(this.dbufSize);for(b=0;;){for(b--||(b=We-1,ce>=d&&T(g.DATA_ERROR),p=h[m[ce++]]),t=p.minLen,e=n.read(t);t>p.maxLen&&T(g.DATA_ERROR),!(e<=p.limit[t]);t++)e=e<<1|n.read(1);e-=p.base[t],(e<0||e>=xe)&&T(g.DATA_ERROR);var F=p.permute[e];if(F===ve||F===Qe){A||(A=1,a=0),F===ve?a+=A:a+=2*A,A<<=1;continue}if(A)for(A=0,R+a>this.dbufSize&&T(g.DATA_ERROR),E=o[u[0]],O[E]+=a;a--;)X[R++]=E;if(F>c)break;R>=this.dbufSize&&T(g.DATA_ERROR),t=F-1,E=Te(u,t),E=o[E],O[E]++,X[R++]=E}for((s<0||s>=R)&&T(g.DATA_ERROR),e=0,t=0;t<256;t++)r=e+O[t],O[t]=e,e=r;for(t=0;t<R;t++)E=X[t]&255,X[O[E]]|=t<<8,O[E]++;var q=0,fe=0,le=0;return R&&(q=X[s],fe=q&255,q>>=8,le=-1),this.writePos=q,this.writeCurrent=fe,this.writeCount=R,this.writeRun=le,!0};k.prototype._read_bunzip=function(t,e){var r,n,i;if(this.writeCount<0)return 0;for(var s=0,a=this.dbuf,o=this.writePos,c=this.writeCurrent,f=this.writeCount,l=this.outputsize,d=this.writeRun;f;){for(f--,n=c,o=a[o],c=o&255,o>>=8,d++===3?(r=c,i=n,c=-1):(r=1,i=c),this.blockCRC.updateCRCRun(i,r);r--;)this.outputStream.writeByte(i),this.nextoutput++;c!=n&&(d=0)}return this.writeCount=f,this.blockCRC.getCRC()!==this.targetBlockCRC&&T(g.DATA_ERROR,"Bad block CRC (got "+this.blockCRC.getCRC().toString(16)+" expected "+this.targetBlockCRC.toString(16)+")"),this.nextoutput};var ie=function(t){if("readByte"in t)return t;var e=new G;return e.pos=0,e.readByte=function(){return t[this.pos++]},e.seek=function(r){this.pos=r},e.eof=function(){return this.pos>=t.length},e},we=function(t){var e=new G,r=!0;if(t)if(typeof t=="number")e.buffer=new Buffer(t),r=!1;else{if("writeByte"in t)return t;e.buffer=t,r=!1}else e.buffer=new Buffer(16384);return e.pos=0,e.writeByte=function(n){if(r&&this.pos>=this.buffer.length){var i=new Buffer(this.buffer.length*2);this.buffer.copy(i),this.buffer=i}this.buffer[this.pos++]=n},e.getBuffer=function(){if(this.pos!==this.buffer.length){if(!r)throw new TypeError("outputsize does not match decoded input");var n=new Buffer(this.pos);this.buffer.copy(n,0,0,this.pos),this.buffer=n}return this.buffer},e._coerced=!0,e};k.Err=g;k.decode=function(t,e,r){for(var n=ie(t),i=we(e),s=new k(n,i);!("eof"in n&&n.eof());)if(s._init_block())s._read_bunzip();else{var a=s.reader.read(32)>>>0;if(a!==s.streamCRC&&T(g.DATA_ERROR,"Bad stream CRC (got "+s.streamCRC.toString(16)+" expected "+a.toString(16)+")"),r&&"eof"in n&&!n.eof())s._start_bunzip(n,i);else break}if("getBuffer"in i)return i.getBuffer()};k.decodeBlock=function(t,e,r){var n=ie(t),i=we(r),s=new k(n,i);s.reader.seek(e);var a=s._get_next_block();if(a&&(s.blockCRC=new Se,s.writeCopies=0,s._read_bunzip()),"getBuffer"in i)return i.getBuffer()};k.table=function(t,e,r){var n=new G;n.delegate=ie(t),n.pos=0,n.readByte=function(){return this.pos++,this.delegate.readByte()},n.delegate.eof&&(n.eof=n.delegate.eof.bind(n.delegate));var i=new G;i.pos=0,i.writeByte=function(){this.pos++};for(var s=new k(n,i),a=s.dbufSize;!("eof"in n&&n.eof());){var o=n.pos*8+s.reader.bitOffset;if(s.reader.hasByte&&(o-=8),s._init_block()){var c=i.pos;s._read_bunzip(),e(o,i.pos-c)}else{var f=s.reader.read(32);if(r&&"eof"in n&&!n.eof())s._start_bunzip(n,i),console.assert(s.dbufSize===a,"shouldn't change block size within multistream file");else break}}};k.Stream=G;k.version=ke.version;k.license=ke.license;Ee.exports=k});var wt={};Fe(wt,{ReplayAnalyzer:()=>ne,ReplayParser:()=>z});module.exports=je(wt);var Be=Z(require("fs")),Q=Z(require("zlib")),Ze=Pe(),De=512,Je=65536,et=16777216,tt=67108864,rt=2147483648,nt={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function it(){let t=1048577,e=new Uint32Array(256*5);for(let r=0;r<256;r++){let n=r;for(let i=0;i<5;i++){t=(t*125+3)%2796203;let s=(t&65535)<<16;t=(t*125+3)%2796203;let a=t&65535;e[n]=(s|a)>>>0,n+=256}}return e}var Re=it(),ee=class{file;header;hashTable;blockTable;files;constructor(e,r=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=Be.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),r){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),r;if(e==="MPQ")r=this.readMPQHeader(),r.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();r=this.readMPQHeader(n.mpqHeaderOffset),r.offset=n.mpqHeaderOffset,r.userDataHeader=n}else throw new Error("Invalid MPQ file header");return r}readMPQHeader(e=0){let r=this.file.subarray(e,e+32),n={magic:r.toString("utf8",0,4),headerSize:r.readUInt32LE(4),archiveSize:r.readUInt32LE(8),formatVersion:r.readUInt16LE(12),sectorSizeShift:r.readUInt16LE(14),hashTableOffset:r.readUInt32LE(16),blockTableOffset:r.readUInt32LE(20),hashTableEntries:r.readUInt32LE(24),blockTableEntries:r.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),r={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return r.content=this.file.subarray(16,16+r.userDataHeaderSize),r}readTable(e){let r=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[r],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 c=[];for(let f=0;f<s;f++){let l=o.subarray(f*16,f*16+16);e==="hash"?c.push({hashA:l.readUInt32LE(0),hashB:l.readUInt32LE(4),locale:l.readUInt16LE(8),platform:l.readUInt16LE(10),blockTableIndex:l.readUInt32LE(12)}):c.push({offset:l.readUInt32LE(0),archivedSize:l.readUInt32LE(4),size:l.readUInt32LE(8),flags:l.readUInt32LE(12)})}return c}getHashTableEntry(e){let r=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===r&&i.hashB===n)return i}readFile(e,r=!1){function n(c){let f=c[0];if(f===0)return c;if(f===2)return Q.inflateSync(c.subarray(1));if(f===16)return Ze.decode(c.subarray(1));try{return Q.inflateSync(c.subarray(1))}catch{return Q.inflateRawSync(c.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&rt))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&Je)throw new Error("Encryption is not supported");if(s.flags&et)s.flags&De&&(r||s.size>s.archivedSize)&&(o=n(o));else{let c=512<<this.header.sectorSizeShift,f=Math.trunc(s.size/c)+1,l=!1;s.flags&tt&&(l=!0,f+=1);let d=[];for(let h=0;h<f+1;h++)d.push(o.readUInt32LE(4*h));let u=d.length-(l?2:1),m=[],b=s.size;for(let h=0;h<u;h++){let p=o.subarray(d[h],d[h+1]);s.flags&De&&(r||b>p.length)&&(p=n(p)),b-=p.length,m.push(p)}o=Buffer.concat(m)}return o}hash(e,r){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let a=e.toUpperCase().charCodeAt(s);n=(Re[(nt[r]<<8)+a]^n+i)>>>0,i=a+n+i+(i<<5)+3>>>0}return n}decrypt(e,r){let n=r>>>0,i=4008636142,s=Buffer.alloc(e.length),a=e.length/4;for(let o=0;o<a;o++){i=i+Re[1024+(n&255)]>>>0;let c=e.readUInt32LE(o*4);c=(c^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=c+i+(i<<5)+3>>>0,s.writeUInt32LE(c,o*4)}return s}};var $=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},H=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},V=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,r="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=r==="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 $;let r=this._data.subarray(this._used,this._used+e);return this._used+=e,r}read_bits(e){let r=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new $;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?r+=s*Math.pow(2,e-n-i):r+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_bits_bigint(e){let r=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new $;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?r|=s<<BigInt(e-n-i):r|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_unaligned_bytes(e){let r=Buffer.alloc(e);for(let n=0;n<e;n++)r[n]=this.read_bits(8);return r}};var W=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new V(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new H(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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,r){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){let r=this._int(e);return[r,this._buffer.read_bits(r)]}_blob(e){let r=this._int(e);return this._buffer.read_aligned_bytes(r)}_bool(){return this._int([0,1])!==0}_choice(e,r){let n=this._int(e);if(!(n in r))throw new H(`Choice tag ${n} not found`);let i=r[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),r=Buffer.alloc(4);return r.writeUInt32BE(e,0),r.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 r={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null&&!Array.isArray(i)&&!Buffer.isBuffer(i))r={...r,...i};else{if(e.length===1)return i;r[n[0]]=i}}else r[n[0]]=this.instance(n[1]);return r}},U=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new V(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new H(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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 H(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),r=(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=r?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,r){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){this._expect_skip(1);let r=Number(this._vint());return[r,this._buffer.read_aligned_bytes(Math.floor((r+7)/8))]}_blob(e){this._expect_skip(2);let r=Number(this._vint());return this._buffer.read_aligned_bytes(r)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,r){this._expect_skip(3);let n=Number(this._vint());if(!(n in r))return this._skip_instance(),{};let i=r[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 r={},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&&!Array.isArray(o)&&!Buffer.isBuffer(o)?r={...r,...o}:e.length===1?r=o:r[a[0]]=o}else r[a[0]]=this.instance(a[1]);else this._skip_instance()}return r}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let r=Number(this._vint());for(let n=0;n<r;n++)this._skip_instance()}else if(e===1){let r=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((r+7)/8))}else if(e===2){let r=Number(this._vint());this._buffer.read_aligned_bytes(r)}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 r=Number(this._vint());for(let n=0;n<r;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 N=Z(require("fs")),I=Z(require("path"));function st(){let t=[];try{t.push(I.default.resolve(__dirname,"..","protocols")),t.push(I.default.resolve(__dirname,"..","..","protocols"))}catch{}t.push(I.default.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==I.default.dirname(e);){let r=I.default.join(e,"node_modules","@astefanski","storm-parser","protocols");t.push(r),e=I.default.dirname(e)}for(let r of t)if(N.default.existsSync(r)&&N.default.readdirSync(r).some(n=>n.endsWith(".json")))return r;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: `+t.join(", "))}var se=new Map,ae=null;function Ie(){return ae||(ae=st()),ae}function te(t){if(se.has(t))return se.get(t);let e=Ie(),r=I.default.join(e,`protocol${t}.json`);if(!N.default.existsSync(r))return null;let n=JSON.parse(N.default.readFileSync(r,"utf-8"));return se.set(t,n),n}function Ce(){let t=Ie();return N.default.readdirSync(t).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,r)=>e-r)}var z=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new ee(e,!1)}init(){let e=te(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 r=this.mpq.header.userDataHeader;if(!r||!r.content)throw new Error("Replay does not have a user data header");let n=new U(r.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 r=te(e);if(!r){let n=Ce(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);r=te(i)}if(!r)throw new Error(`No protocol found for build ${e}`);return r}*decodeEventStream(e,r,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),c=Object.keys(o)[0],f=o[c];s+=f;let l=i?e.instance(this.protocol.replay_userid_typeid):void 0,d=Number(e.instance(r)),u=n[d];if(!u)throw new Error(`Unknown eventid(${d})`);let m=u[0],b=u[1],h=e.instance(m);h._event=b,h._eventid=d,h._gameloop=s,i&&(h._userid=l),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 U(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 W(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 r=new U(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 r=new W(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 Ae={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},at={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function L(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}function v(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?r.m_value:void 0}function re(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?L(r.m_value):void 0}function w(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?r.m_value:void 0}function oe(t,e){return`${t}-${e}`}function D(t,e){return(t-e)/16}function Me(t,e,r){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};r.levelTimes={0:{},1:{}},r.takedowns=[],r.structures={},r.XPBreakdown=[],r.mercs={captures:[],units:{}},r.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:r.map||""};for(let i of t)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":ot(i,n,e,r);break;case"NNet.Replay.Tracker.SUnitBornEvent":lt(i,n,r);break;case"NNet.Replay.Tracker.SUnitDiedEvent":dt(i,n,r);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":ut(i,n);break}return r.loopGameStart=n.loopGameStart,r.loopLength=n.loopGameEnd,r.length=(n.loopGameEnd-n.loopGameStart)/16,mt(n,e),{playerIDMap:n.playerIDMap}}function ot(t,e,r,n){let i=L(t.m_eventName),s=t.m_intData,a=t.m_stringData,o=t.m_fixedData;switch(i){case"PlayerInit":{let c=v(s,"PlayerID"),f=re(a,"ToonHandle");c!==void 0&&f&&r[f]&&(e.playerIDMap[c]=f);break}case"GatesOpen":e.loopGameStart=t._gameloop;break;case"LevelUp":{let c=v(s,"PlayerID"),f=v(s,"Level");if(c===void 0||f===void 0)break;let l;if(c>=1&&c<=5)l="0";else if(c>=6&&c<=10)l="1";else break;n.levelTimes[l][String(f)]||(n.levelTimes[l][String(f)]={loop:t._gameloop,level:f,team:l,time:D(t._gameloop,e.loopGameStart)});break}case"TalentChosen":{let c=v(s,"PlayerID"),f=re(a,"PurchaseName");if(c===void 0||!f)break;let l=e.playerIDMap[c];if(!l||!r[l])break;let d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let u of d)if(!r[l].talents[u]){r[l].talents[u]=f;break}break}case"PlayerDeath":{let c=v(s,"PlayerID"),f=v(s,"KillingPlayer"),l=w(o,"PositionX")??0,d=w(o,"PositionY")??0;if(c===void 0)break;let u=e.playerIDMap[c];if(!u)break;let m={player:u,hero:r[u]?.hero||""},b=[],h=r[u]?.team;if(f&&f>0){let _=e.playerIDMap[f];_&&r[_]&&b.push({player:_,hero:r[_].hero})}for(let[_,y]of Object.entries(e.playerIDMap)){let x=r[y];!x||x.team===h||parseInt(_)===f||b.push({player:y,hero:x.hero})}let p={loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),x:l,y:d,killers:b,victim:m};n.takedowns.push(p),r[u]&&r[u].deaths.push(p);for(let _ of b)r[_.player]&&r[_.player].takedowns.push(p);break}case"PeriodicXPBreakdown":{let c=v(s,"Team");if(c===void 0)break;let f={GameTime:v(s,"GameTime")??0,PreviousGameTime:v(s,"PreviousGameTime")??0,MinionXP:w(o,"MinionXP")??0,CreepXP:w(o,"CreepXP")??0,StructureXP:w(o,"StructureXP")??0,HeroXP:w(o,"HeroXP")??0,TrickleXP:w(o,"TrickleXP")??0};n.XPBreakdown.push({loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),team:c,teamLevel:v(s,"TeamLevel")??0,breakdown:f,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let c=v(s,"Team");if(c===void 0)break;n.XPBreakdown.push({loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),team:c,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:w(o,"MinionXP")??0,CreepXP:w(o,"CreepXP")??0,StructureXP:w(o,"StructureXP")??0,HeroXP:w(o,"HeroXP")??0,TrickleXP:w(o,"TrickleXP")??0}});break}case"JungleCampCapture":{let c=v(s,"CampTeam")??v(s,"Team")??0;n.mercs.captures.push({loop:t._gameloop,type:re(a,"CampType")??re(a,"Result")??"Unknown Camp",team:c,time:D(t._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let c=v(s,"PlayerID");if(c===void 0)break;let f=e.playerIDMap[c];if(!f||!r[f])break;let l={},d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(a)for(let u=0;u<a.length&&u<d.length;u++){let m=L(a[u].m_value);m&&(l[d[u]]=m)}r[f].talents=l;break}default:ft(i,t,e,n,s,o);break}}var ct=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function ft(t,e,r,n,i,s){if(!ct.has(t)||!i)return;let a=v(i,"Team")??v(i,"Event")??0,o=a===0||a===1?a:0,c={team:a,loop:e._gameloop,time:D(e._gameloop,r.loopGameStart),score:v(i,"Score"),duration:w(s,"Duration")};n.objective[o].events.push(c),n.objective[o].count=n.objective[o].events.length}function lt(t,e,r){let n=L(t.m_unitTypeName),i=t.m_unitTagIndex,s=t.m_unitTagRecycle,a=oe(i,s),o=t.m_controlPlayerId??t.m_upkeepPlayerId,c=t.m_x??0,f=t.m_y??0,l;if(o>=1&&o<=5?l=0:o>=6&&o<=10?l=1:o===11?l=0:o===12?l=1:l=o<=5?0:1,e.unitIndex[a]={type:n,playerId:o,team:l,x:c,y:f,bornLoop:t._gameloop},n.startsWith("Town")&&Ae[n]&&(r.structures[a]={type:n,name:Ae[n],tag:i,rtag:s,x:c,y:f,team:l}),n.startsWith("Hero")&&o>=1&&o<=10){e.heroUnits[a]=o,e.heroLives[o]||(e.heroLives[o]=[]);let d=D(t._gameloop,e.loopGameStart);e.heroLives[o].push({born:d,locations:[{x:c,y:f,time:d}],duration:0})}if(at[n]){let u=r.mercs.captures[r.mercs.captures.length-1]?.loop??t._gameloop;r.mercs.units[a]={loop:u,team:l,type:n,locations:[{x:c,y:f}],time:D(u,e.loopGameStart),duration:0}}}function dt(t,e,r){let n=oe(t.m_unitTagIndex,t.m_unitTagRecycle),i=D(t._gameloop,e.loopGameStart);r.structures[n]&&(r.structures[n].destroyedLoop=t._gameloop,r.structures[n].destroyed=i),r.mercs.units[n]&&(r.mercs.units[n].duration=i-r.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 ut(t,e){let r=oe(t.m_unitTagIndex,t.m_unitTagRecycle),n=t.m_controlPlayerId??t.m_upkeepPlayerId;e.unitIndex[r]&&(e.unitIndex[r].playerId=n,n>=1&&n<=5?e.unitIndex[r].team=0:n>=6&&n<=10&&(e.unitIndex[r].team=1))}function mt(t,e){for(let[r,n]of Object.entries(t.heroLives)){let i=parseInt(r,10),s=t.playerIDMap[i];if(!s||!e[s])continue;for(let o of n)if(o.died===void 0){let c=o.locations[o.locations.length-1];o.duration=c?c.time-o.born:0}let a="";for(let[o,c]of Object.entries(t.heroUnits))if(c===i){a=o;break}a&&(e[s].units[a]={lives:n})}}var bt=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function pt(){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 ht(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:pt(),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 Le(t,e){_t(t,e),yt(t,e),xt(t),vt(t),Tt(t,e),kt(t)}function _t(t,e){for(let r of["0","1"]){let n=t.teams[r];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=ht();for(let d of n.ids){let u=e[d];u&&(n.names.push(u.name),n.heroes.push(u.hero),n.tags.push(u.tag))}for(let d of n.ids){let u=e[d];if(u)for(let m of bt){let b=u.gameStats[m];typeof b=="number"&&(n.stats.totals[m]+=b)}}let i=n.ids.reduce((d,u)=>d+(e[u]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),t.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(t.length*n.ids.length));let s=n.takedowns,a=i;n.stats.KDA=a>0?s/a:s,n.stats.PPK=s>0?t.takedowns.filter(d=>n.ids.includes(d.killers[0]?.player)).reduce((d,u)=>d+u.killers.length,0)/s:0;let o=t.levelTimes[r];o?.["10"]&&(n.stats.timeTo10=o[10].time);let c=parseInt(r,10),f=t.mercs.captures.filter(d=>d.team===c);n.stats.mercCaptures=f.length;let l=0;for(let d of Object.values(t.mercs.units))d.team===c&&d.duration>0&&(l+=d.duration);n.stats.mercUptime=l,n.stats.mercUptimePercent=t.length>0?l/t.length:0,gt(t,n,r)}}function gt(t,e,r){let n=parseInt(r,10),i=n===0?1:0,s={};for(let a of Object.values(t.structures)){let o=a.name;s[o]||(s[o]={lost:0,destroyed:0,first:t.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 yt(t,e){let r=t.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,a=i.TeamTakedowns??0;r>0&&(i.DPM=(i.HeroDamage??0)/r,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/r,i.XPM=(i.ExperienceContribution??0)/r),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=t.length}}function xt(t){let e=0,r=0,n=new Set(t.teams[0]?.ids||[]);for(let i of t.takedowns)n.has(i.victim.player)?r++:e++;t.team0Takedowns=e,t.team1Takedowns=r}function vt(t){let e=t.levelTimes[0]||{},r=t.levelTimes[1]||{},n=[];for(let f of Object.values(e))n.push({time:f.time,team:0,level:f.level});for(let f of Object.values(r))n.push({time:f.time,team:1,level:f.level});if(n.sort((f,l)=>f.time-l.time),n.length===0){t.levelAdvTimeline=[];return}let i=[],s=0,a=0,o=n[0].time;for(let f of n){let l=s-a;f.time>o&&i.push({start:o,end:f.time,levelDiff:l,length:f.time-o}),f.team===0?s=f.level:a=f.level,o=f.time}let c=s-a;t.length>o&&i.push({start:o,end:t.length,levelDiff:c,length:t.length-o}),t.levelAdvTimeline=i;for(let f of["0","1"]){let l=t.teams[f];if(!l)continue;let d=f==="0"?1:-1,u=0,m=0,b=0,h=0;for(let p of i){let _=p.levelDiff*d;_>0&&(u+=p.length),m=Math.max(m,_),b+=_*p.length,h+=p.length}l.stats.levelAdvTime=u,l.stats.maxLevelAdv=m,l.stats.avgLevelAdv=h>0?b/h:0,l.stats.levelAdvPct=t.length>0?u/t.length:0}}function Tt(t,e){for(let s of["0","1"]){let a=t.teams[s];if(!a)continue;let o=[];for(let p of a.ids){let _=e[p];if(_)for(let y of _.deaths){o.push({time:y.time,delta:-1});let x=y.time+St(_.gameStats.Level??1,y.time,t.length);x<t.length&&o.push({time:x,delta:1})}}o.sort((p,_)=>p.time-_.time);let c=[{time:0,heroes:a.ids.length}],f=a.ids.length;for(let p of o)f+=p.delta,f=Math.max(0,Math.min(a.ids.length,f)),c.push({time:p.time,heroes:f});a.stats.uptime=c;let l={};for(let p=0;p<c.length;p++){let y=(p<c.length-1?c[p+1].time:t.length)-c[p].time,x=String(c[p].heroes);l[x]=(l[x]||0)+y}a.stats.uptimeHistogram=l;let d=0,u=0;for(let p=0;p<c.length;p++){let y=(p<c.length-1?c[p+1].time:t.length)-c[p].time;d+=c[p].heroes*y,u+=y}a.stats.avgHeroesAlive=u>0?d/u:a.ids.length,a.stats.wipes=c.filter(p=>p.heroes===0).length,a.stats.aces=0;let m=s==="0"?"1":"0",b=t.teams[m];b?.stats?.uptime&&(a.stats.aces=b.stats.uptime.filter(p=>p.heroes===0).length);let h=t.XPBreakdown.filter(p=>p.team===parseInt(s,10));if(h.length>0){let _=h[h.length-1].breakdown.TrickleXP;a.stats.passiveXPGain=_,a.stats.passiveXPRate=t.length>0?_/(t.length/60):0}}for(let s of["0","1"]){let a=t.teams[s],o=s==="0"?"1":"0",c=t.teams[o];if(!a||!c)continue;let f=a.stats.uptime,l=c.stats.uptime;if(!f.length||!l.length)continue;let d=new Set;for(let b of f)d.add(b.time);for(let b of l)d.add(b.time);let u=Array.from(d).sort((b,h)=>b-h),m=0;for(let b=0;b<u.length;b++){let h=u[b],_=(b<u.length-1?u[b+1]:t.length)-h,y=He(f,h),x=He(l,h);y>x&&(m+=_)}a.stats.timeWithHeroAdv=m,a.stats.pctWithHeroAdv=t.length>0?m/t.length:0}let r=t.teams[0]?.stats.passiveXPRate??0,n=t.teams[1]?.stats.passiveXPRate??0,i=(r+n)/2;t.teams[0]&&(t.teams[0].stats.passiveXPDiff=i>0?r/i:0),t.teams[1]&&(t.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function He(t,e){let r=0;for(let n of t)if(n.time<=e)r=n.heroes;else break;return r}function St(t,e,r){return t<=1?15:t<=5?15+(t-1)*2:t<=10?23+(t-5)*3:t<=15?38+(t-10)*4:58+(t-15)*5}function kt(t){let e=1/0,r=-1,n=1/0,i=-1;for(let a of Object.values(t.structures))a.destroyed!==void 0&&(a.name==="Fort"&&a.destroyed<e&&(e=a.destroyed,r=a.team===0?1:0),a.name==="Keep"&&a.destroyed<n&&(n=a.destroyed,i=a.team===0?1:0));r>=0&&(t.firstFort=r,t.firstFortWin=r===t.winner),i>=0&&(t.firstKeep=i,t.firstKeepWin=i===t.winner);let s=[...t.objective[0].events.map(a=>({...a,assignedTeam:0})),...t.objective[1].events.map(a=>({...a,assignedTeam:1}))].sort((a,o)=>a.loop-o.loop);s.length>0&&(t.firstObjective=s[0].assignedTeam,t.firstObjectiveWin=s[0].assignedTeam===t.winner),t.firstPickWin=t.picks.first===t.winner}function S(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}var ne=class{static async analyze(e){try{let r=new z(e);await r.init();let n=r.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=r.getTrackerEvents(),s=r.getInitData(),o=r.getHeader()?.m_version??{},c={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??r.getBuild(),m_baseBuild:o.m_baseBuild??r.getBuild()},f=n.m_playerList.find(m=>m?.m_toon)?.m_toon,l={version:c,map:S(n.m_title),isBlizzardMap:n.m_isBlizzardMap,timeLocalOffset:Number(n.m_timeLocalOffset),gameSpeed:n.m_gameSpeed,date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:f?.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};l.objective.type=l.map||"";let d={};for(let m of n.m_playerList){if(!m?.m_toon)continue;let b=m.m_toon,h=S(b.m_programId),p=`${b.m_region}-${h}-${b.m_realm}-${b.m_id}`,_=S(m.m_hero);d[p]={hero:_,name:S(m.m_name),uuid:b.m_id,region:b.m_region,realm:b.m_realm,ToonHandle:p,tag:0,team:m.m_teamId,win:m.m_result===1,heroLevel:0,skin:"",mount:"",banner:"",spray:"",clanTag:"",highestLeague:0,combinedRaceLevels:0,randomSeed:0,announcer:"",silenced:!1,voiceSilenced:!1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},l.playerIDs.push(p),l.heroes.push(_)}this.extractBattleTags(r,n,d),this.extractDraft(s,l,n),this.extractCosmetics(s,n,d);let{playerIDMap:u}=Me(i,d,l);this.processScoreEvents(i,u,d);for(let[m,b]of Object.entries(d)){b.heroLevel=b.gameStats.Level||0,b.win&&(l.winner=b.team,l.winningPlayers.push(m));let h=l.teams[b.team.toString()];h&&(h.level=Math.max(h.level,b.gameStats.Level||0),h.takedowns+=b.gameStats.Takedowns||0,h.ids.push(m))}return Le(l,d),{status:1,match:l,players:d}}catch(r){return console.error("ReplayAnalyzer Error:",r),{status:-2,error:String(r)}}}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,r,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 c of r.m_playerList){if(!c?.m_toon)continue;let f=S(c.m_name);for(;o<a.length;){let d=a[o].split("#"),u=d[0],m=d[1].replace(/[zØ]/g,"");if(o++,u===f){let b=c.m_toon,h=`${b.m_region}-${S(b.m_programId)}-${b.m_realm}-${b.m_id}`;n[h]&&(n[h].tag=parseInt(m,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState,a=i.m_gameDescription;if(!s)return;a&&(r.randomValue=a.m_randomValue,r.gameOptions=a.m_gameOptions);let o=[],c=[];for(let m of n.m_playerList){if(!m?.m_toon)continue;let b=S(m.m_hero);m.m_teamId===0?o.push(b):m.m_teamId===1&&c.push(b)}r.picks={0:o,1:c,first:0};let f,l=a?.m_gameOptions;if(l&&typeof l.m_ammId=="number"){let m=l.m_ammId;m===50001?f="Quick Match":m===50031?f="ARAM":m===50041?f="Unranked Draft":m===50051?f="Custom":m===50061?f="Hero League":m===50071?f="Team League":m===50091&&(f="Storm League")}if(!f&&typeof s.m_gameMode=="number"){let m=s.m_gameMode;m===3?f="Quick Match":m===4?f="Custom":m===5?f="Hero League":m===6?f="Team League":m===7?f="Unranked Draft":m===8&&(f="ARAM")}if(r.mode=f,typeof s.m_gameType=="number"&&(r.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let m=s.m_firstPickTeam??0;r.picks.first=m}}catch{}}static extractCosmetics(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let a=s.m_slots,o=i.m_userInitialData;if(!a)return;let c=0;for(let f of r.m_playerList){if(!f?.m_toon)continue;let l=f.m_toon,d=`${l.m_region}-${S(l.m_programId)}-${l.m_realm}-${l.m_id}`,u=n[d];for(;c<a.length;){let m=a[c];c++;let b=m.m_hero;if(!(!b||Buffer.isBuffer(b)&&b.length===0)){if(u&&(u.skin=S(m.m_skin)||"",u.mount=S(m.m_mount)||"",u.announcer=S(m.m_announcerPack)||"",u.banner=S(m.m_banner)||"",u.spray=S(m.m_spray)||"",u.silenced=!!m.m_hasSilencePenalty,u.voiceSilenced=!!m.m_hasVoiceSilencePenalty,o)){let h=o.find(p=>S(p.m_name)===u.name);h&&(u.clanTag=S(h.m_clanTag),u.highestLeague=h.m_highestLeague,u.combinedRaceLevels=h.m_combinedRaceLevels,u.randomSeed=h.m_randomSeed)}break}}}}catch{}}static processScoreEvents(e,r,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),c=a.m_values,f=o.startsWith("EndOfMatchAward"),l=0;for(let d of c)if(d&&d.length>0&&d[0]!==void 0){let u=l+1,m=r[u];if(m&&n[m]){let b=typeof d[0]=="object"&&d[0]!==null&&"m_value"in d[0]?d[0].m_value:d[0];b!=null&&(f?b===1&&n[m].awards.push(o):n[m].gameStats[o]=b)}l++}else d&&d.length===0&&l++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};0&&(module.exports={ReplayAnalyzer,ReplayParser});
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- var X=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var ue=X((ht,le)=>{"use strict";var ce=[0,1,3,7,15,31,63,127,255],F=function(t){this.stream=t,this.bitOffset=0,this.curByte=0,this.hasByte=!1};F.prototype._ensureByte=function(){this.hasByte||(this.curByte=this.stream.readByte(),this.hasByte=!0)};F.prototype.read=function(t){for(var e=0;t>0;){this._ensureByte();var r=8-this.bitOffset;if(t>=r)e<<=r,e|=ce[r]&this.curByte,this.hasByte=!1,this.bitOffset=0,t-=r;else{e<<=t;var n=r-t;e|=(this.curByte&ce[t]<<n)>>n,this.bitOffset+=t,t=0}}return e};F.prototype.seek=function(t){var e=t%8,r=(t-e)/8;this.bitOffset=e,this.stream.seek(r),this.hasByte=!1};F.prototype.pi=function(){var t=new Buffer(6),e;for(e=0;e<t.length;e++)t[e]=this.read(8);return t.toString("hex")};le.exports=F});var me=X((_t,de)=>{"use strict";var A=function(){};A.prototype.readByte=function(){throw new Error("abstract method readByte() not implemented")};A.prototype.read=function(t,e,r){for(var n=0;n<r;){var i=this.readByte();if(i<0)return n===0?-1:n;t[e++]=i,n++}return n};A.prototype.seek=function(t){throw new Error("abstract method seek() not implemented")};A.prototype.writeByte=function(t){throw new Error("abstract method readByte() not implemented")};A.prototype.write=function(t,e,r){var n;for(n=0;n<r;n++)this.writeByte(t[e++]);return r};A.prototype.flush=function(){};de.exports=A});var pe=X((gt,be)=>{"use strict";be.exports=(function(){var t=new Uint32Array([0,79764919,159529838,222504665,319059676,398814059,445009330,507990021,638119352,583659535,797628118,726387553,890018660,835552979,1015980042,944750013,1276238704,1221641927,1167319070,1095957929,1595256236,1540665371,1452775106,1381403509,1780037320,1859660671,1671105958,1733955601,2031960084,2111593891,1889500026,1952343757,2552477408,2632100695,2443283854,2506133561,2334638140,2414271883,2191915858,2254759653,3190512472,3135915759,3081330742,3009969537,2905550212,2850959411,2762807018,2691435357,3560074640,3505614887,3719321342,3648080713,3342211916,3287746299,3467911202,3396681109,4063920168,4143685023,4223187782,4286162673,3779000052,3858754371,3904687514,3967668269,881225847,809987520,1023691545,969234094,662832811,591600412,771767749,717299826,311336399,374308984,453813921,533576470,25881363,88864420,134795389,214552010,2023205639,2086057648,1897238633,1976864222,1804852699,1867694188,1645340341,1724971778,1587496639,1516133128,1461550545,1406951526,1302016099,1230646740,1142491917,1087903418,2896545431,2825181984,2770861561,2716262478,3215044683,3143675388,3055782693,3001194130,2326604591,2389456536,2200899649,2280525302,2578013683,2640855108,2418763421,2498394922,3769900519,3832873040,3912640137,3992402750,4088425275,4151408268,4197601365,4277358050,3334271071,3263032808,3476998961,3422541446,3585640067,3514407732,3694837229,3640369242,1762451694,1842216281,1619975040,1682949687,2047383090,2127137669,1938468188,2001449195,1325665622,1271206113,1183200824,1111960463,1543535498,1489069629,1434599652,1363369299,622672798,568075817,748617968,677256519,907627842,853037301,1067152940,995781531,51762726,131386257,177728840,240578815,269590778,349224269,429104020,491947555,4046411278,4126034873,4172115296,4234965207,3794477266,3874110821,3953728444,4016571915,3609705398,3555108353,3735388376,3664026991,3290680682,3236090077,3449943556,3378572211,3174993278,3120533705,3032266256,2961025959,2923101090,2868635157,2813903052,2742672763,2604032198,2683796849,2461293480,2524268063,2284983834,2364738477,2175806836,2238787779,1569362073,1498123566,1409854455,1355396672,1317987909,1246755826,1192025387,1137557660,2072149281,2135122070,1912620623,1992383480,1753615357,1816598090,1627664531,1707420964,295390185,358241886,404320391,483945776,43990325,106832002,186451547,266083308,932423249,861060070,1041341759,986742920,613929101,542559546,756411363,701822548,3316196985,3244833742,3425377559,3370778784,3601682597,3530312978,3744426955,3689838204,3819031489,3881883254,3928223919,4007849240,4037393693,4100235434,4180117107,4259748804,2310601993,2373574846,2151335527,2231098320,2596047829,2659030626,2470359227,2550115596,2947551409,2876312838,2788305887,2733848168,3165939309,3094707162,3040238851,2985771188]),e=function(){var r=4294967295;this.getCRC=function(){return~r>>>0},this.updateCRC=function(n){r=r<<8^t[(r>>>24^n)&255]},this.updateCRCRun=function(n,i){for(;i-- >0;)r=r<<8^t[(r>>>24^n)&255]}};return e})()});var he=X((yt,Me)=>{Me.exports={name:"seek-bzip",version:"2.0.0",contributors:["C. Scott Ananian (http://cscott.net)","Eli Skeggs","Kevin Kwok","Rob Landley (http://landley.net)"],description:"a pure-JavaScript Node.JS module for random-access decoding bzip2 data",main:"./lib/index.js",repository:{type:"git",url:"https://github.com/cscott/seek-bzip.git"},license:"MIT",bin:{"seek-bunzip":"./bin/seek-bunzip","seek-table":"./bin/seek-bzip-table"},directories:{test:"test"},dependencies:{commander:"^6.0.0"},devDependencies:{fibers:"^5.0.0",mocha:"^8.1.0"},scripts:{test:"mocha"}}});var we=X((xt,ke)=>{"use strict";var He=ue(),K=me(),xe=pe(),ve=he(),Y=20,_e=258,ge=0,Oe=1,Ue=2,Le=6,Ne=50,ze="314159265359",Xe="177245385090",ye=function(t,e){var r=t[e],n;for(n=e;n>0;n--)t[n]=t[n-1];return t[0]=r,r},g={OK:0,LAST_BLOCK:-1,NOT_BZIP_DATA:-2,UNEXPECTED_INPUT_EOF:-3,UNEXPECTED_OUTPUT_EOF:-4,DATA_ERROR:-5,OUT_OF_MEMORY:-6,OBSOLETE_INPUT:-7,END_OF_BLOCK:-8},B={};B[g.LAST_BLOCK]="Bad file checksum";B[g.NOT_BZIP_DATA]="Not bzip data";B[g.UNEXPECTED_INPUT_EOF]="Unexpected input EOF";B[g.UNEXPECTED_OUTPUT_EOF]="Unexpected output EOF";B[g.DATA_ERROR]="Data error";B[g.OUT_OF_MEMORY]="Out of memory";B[g.OBSOLETE_INPUT]="Obsolete (pre 0.9.5) bzip format not supported.";var T=function(t,e){var r=B[t]||"unknown error";e&&(r+=": "+e);var n=new TypeError(r);throw n.errorCode=t,n},k=function(t,e){this.writePos=this.writeCurrent=this.writeCount=0,this._start_bunzip(t,e)};k.prototype._init_block=function(){var t=this._get_next_block();return t?(this.blockCRC=new xe,!0):(this.writeCount=-1,!1)};k.prototype._start_bunzip=function(t,e){var r=new Buffer(4);(t.read(r,0,4)!==4||String.fromCharCode(r[0],r[1],r[2])!=="BZh")&&T(g.NOT_BZIP_DATA,"bad magic");var n=r[3]-48;(n<1||n>9)&&T(g.NOT_BZIP_DATA,"level out of range"),this.reader=new He(t),this.dbufSize=1e5*n,this.nextoutput=0,this.outputStream=e,this.streamCRC=0};k.prototype._get_next_block=function(){var t,e,r,n=this.reader,i=n.pi();if(i===Xe)return!1;i!==ze&&T(g.NOT_BZIP_DATA),this.targetBlockCRC=n.read(32)>>>0,this.streamCRC=(this.targetBlockCRC^(this.streamCRC<<1|this.streamCRC>>>31))>>>0,n.read(1)&&T(g.OBSOLETE_INPUT);var s=n.read(24);s>this.dbufSize&&T(g.DATA_ERROR,"initial position out of bounds");var o=n.read(16),a=new Buffer(256),f=0;for(t=0;t<16;t++)if(o&1<<15-t){var l=t*16;for(r=n.read(16),e=0;e<16;e++)r&1<<15-e&&(a[f++]=l+e)}var c=n.read(3);(c<Ue||c>Le)&&T(g.DATA_ERROR);var u=n.read(15);u===0&&T(g.DATA_ERROR);var b=new Buffer(256);for(t=0;t<c;t++)b[t]=t;var h=new Buffer(u);for(t=0;t<u;t++){for(e=0;n.read(1);e++)e>=c&&T(g.DATA_ERROR);h[t]=ye(b,e)}var m=f+2,p=[],d;for(e=0;e<c;e++){var _=new Buffer(m),y=new Uint16Array(Y+1);for(o=n.read(5),t=0;t<m;t++){for(;(o<1||o>Y)&&T(g.DATA_ERROR),!!n.read(1);)n.read(1)?o--:o++;_[t]=o}var x,E;for(x=E=_[0],t=1;t<m;t++)_[t]>E?E=_[t]:_[t]<x&&(x=_[t]);d={},p.push(d),d.permute=new Uint16Array(_e),d.limit=new Uint32Array(Y+2),d.base=new Uint32Array(Y+1),d.minLen=x,d.maxLen=E;var C=0;for(t=x;t<=E;t++)for(y[t]=d.limit[t]=0,o=0;o<m;o++)_[o]===t&&(d.permute[C++]=o);for(t=0;t<m;t++)y[_[t]]++;for(C=o=0,t=x;t<E;t++)C+=y[t],d.limit[t]=C-1,C<<=1,o+=y[t],d.base[t+1]=C-o;d.limit[E+1]=Number.MAX_VALUE,d.limit[E]=C+y[E]-1,d.base[x]=0}var U=new Uint32Array(256);for(t=0;t<256;t++)b[t]=t;var I=0,R=0,oe=0,S,N=this.dbuf=new Uint32Array(this.dbufSize);for(m=0;;){for(m--||(m=Ne-1,oe>=u&&T(g.DATA_ERROR),d=p[h[oe++]]),t=d.minLen,e=n.read(t);t>d.maxLen&&T(g.DATA_ERROR),!(e<=d.limit[t]);t++)e=e<<1|n.read(1);e-=d.base[t],(e<0||e>=_e)&&T(g.DATA_ERROR);var z=d.permute[e];if(z===ge||z===Oe){I||(I=1,o=0),z===ge?o+=I:o+=2*I,I<<=1;continue}if(I)for(I=0,R+o>this.dbufSize&&T(g.DATA_ERROR),S=a[b[0]],U[S]+=o;o--;)N[R++]=S;if(z>f)break;R>=this.dbufSize&&T(g.DATA_ERROR),t=z-1,S=ye(b,t),S=a[S],U[S]++,N[R++]=S}for((s<0||s>=R)&&T(g.DATA_ERROR),e=0,t=0;t<256;t++)r=e+U[t],U[t]=e,e=r;for(t=0;t<R;t++)S=N[t]&255,N[U[S]]|=t<<8,U[S]++;var q=0,ae=0,fe=0;return R&&(q=N[s],ae=q&255,q>>=8,fe=-1),this.writePos=q,this.writeCurrent=ae,this.writeCount=R,this.writeRun=fe,!0};k.prototype._read_bunzip=function(t,e){var r,n,i;if(this.writeCount<0)return 0;for(var s=0,o=this.dbuf,a=this.writePos,f=this.writeCurrent,l=this.writeCount,c=this.outputsize,u=this.writeRun;l;){for(l--,n=f,a=o[a],f=a&255,a>>=8,u++===3?(r=f,i=n,f=-1):(r=1,i=f),this.blockCRC.updateCRCRun(i,r);r--;)this.outputStream.writeByte(i),this.nextoutput++;f!=n&&(u=0)}return this.writeCount=l,this.blockCRC.getCRC()!==this.targetBlockCRC&&T(g.DATA_ERROR,"Bad block CRC (got "+this.blockCRC.getCRC().toString(16)+" expected "+this.targetBlockCRC.toString(16)+")"),this.nextoutput};var te=function(t){if("readByte"in t)return t;var e=new K;return e.pos=0,e.readByte=function(){return t[this.pos++]},e.seek=function(r){this.pos=r},e.eof=function(){return this.pos>=t.length},e},Te=function(t){var e=new K,r=!0;if(t)if(typeof t=="number")e.buffer=new Buffer(t),r=!1;else{if("writeByte"in t)return t;e.buffer=t,r=!1}else e.buffer=new Buffer(16384);return e.pos=0,e.writeByte=function(n){if(r&&this.pos>=this.buffer.length){var i=new Buffer(this.buffer.length*2);this.buffer.copy(i),this.buffer=i}this.buffer[this.pos++]=n},e.getBuffer=function(){if(this.pos!==this.buffer.length){if(!r)throw new TypeError("outputsize does not match decoded input");var n=new Buffer(this.pos);this.buffer.copy(n,0,0,this.pos),this.buffer=n}return this.buffer},e._coerced=!0,e};k.Err=g;k.decode=function(t,e,r){for(var n=te(t),i=Te(e),s=new k(n,i);!("eof"in n&&n.eof());)if(s._init_block())s._read_bunzip();else{var o=s.reader.read(32)>>>0;if(o!==s.streamCRC&&T(g.DATA_ERROR,"Bad stream CRC (got "+s.streamCRC.toString(16)+" expected "+o.toString(16)+")"),r&&"eof"in n&&!n.eof())s._start_bunzip(n,i);else break}if("getBuffer"in i)return i.getBuffer()};k.decodeBlock=function(t,e,r){var n=te(t),i=Te(r),s=new k(n,i);s.reader.seek(e);var o=s._get_next_block();if(o&&(s.blockCRC=new xe,s.writeCopies=0,s._read_bunzip()),"getBuffer"in i)return i.getBuffer()};k.table=function(t,e,r){var n=new K;n.delegate=te(t),n.pos=0,n.readByte=function(){return this.pos++,this.delegate.readByte()},n.delegate.eof&&(n.eof=n.delegate.eof.bind(n.delegate));var i=new K;i.pos=0,i.writeByte=function(){this.pos++};for(var s=new k(n,i),o=s.dbufSize;!("eof"in n&&n.eof());){var a=n.pos*8+s.reader.bitOffset;if(s.reader.hasByte&&(a-=8),s._init_block()){var f=i.pos;s._read_bunzip(),e(a,i.pos-f)}else{var l=s.reader.read(32);if(r&&"eof"in n&&!n.eof())s._start_bunzip(n,i),console.assert(s.dbufSize===o,"shouldn't change block size within multistream file");else break}}};k.Stream=K;k.version=ve.version;k.license=ve.license;ke.exports=k});import*as Pe from"fs";import*as j from"zlib";var Fe=we(),Se=512,Ke=65536,je=16777216,Ge=67108864,Qe=2147483648,$e={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function Ve(){let t=1048577,e=new Uint32Array(256*5);for(let r=0;r<256;r++){let n=r;for(let i=0;i<5;i++){t=(t*125+3)%2796203;let s=(t&65535)<<16;t=(t*125+3)%2796203;let o=t&65535;e[n]=(s|o)>>>0,n+=256}}return e}var Ee=Ve(),Z=class{file;header;hashTable;blockTable;files;constructor(e,r=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=Pe.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),r){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),r;if(e==="MPQ")r=this.readMPQHeader(),r.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();r=this.readMPQHeader(n.mpqHeaderOffset),r.offset=n.mpqHeaderOffset,r.userDataHeader=n}else throw new Error("Invalid MPQ file header");return r}readMPQHeader(e=0){let r=this.file.subarray(e,e+32),n={magic:r.toString("utf8",0,4),headerSize:r.readUInt32LE(4),archiveSize:r.readUInt32LE(8),formatVersion:r.readUInt16LE(12),sectorSizeShift:r.readUInt16LE(14),hashTableOffset:r.readUInt32LE(16),blockTableOffset:r.readUInt32LE(20),hashTableEntries:r.readUInt32LE(24),blockTableEntries:r.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),r={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return r.content=this.file.subarray(16,16+r.userDataHeaderSize),r}readTable(e){let r=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[r],s=this.header[n];if(i==null||s==null)throw new Error("Missing "+e+" offset or entries");let o=this.hash("("+e+" table)","TABLE"),a=this.file.subarray(i+(this.header.offset||0),i+(this.header.offset||0)+s*16);a=this.decrypt(a,o);let f=[];for(let l=0;l<s;l++){let c=a.subarray(l*16,l*16+16);e==="hash"?f.push({hashA:c.readUInt32LE(0),hashB:c.readUInt32LE(4),locale:c.readUInt16LE(8),platform:c.readUInt16LE(10),blockTableIndex:c.readUInt32LE(12)}):f.push({offset:c.readUInt32LE(0),archivedSize:c.readUInt32LE(4),size:c.readUInt32LE(8),flags:c.readUInt32LE(12)})}return f}getHashTableEntry(e){let r=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===r&&i.hashB===n)return i}readFile(e,r=!1){function n(f){let l=f[0];if(l===0)return f;if(l===2)return j.inflateSync(f.subarray(1));if(l===16)return Fe.decode(f.subarray(1));try{return j.inflateSync(f.subarray(1))}catch{return j.inflateRawSync(f.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&Qe))return null;if(s.archivedSize===0)return Buffer.alloc(0);let o=s.offset+(this.header.offset||0),a=this.file.subarray(o,o+s.archivedSize);if(s.flags&Ke)throw new Error("Encryption is not supported");if(s.flags&je)s.flags&Se&&(r||s.size>s.archivedSize)&&(a=n(a));else{let f=512<<this.header.sectorSizeShift,l=Math.trunc(s.size/f)+1,c=!1;s.flags&Ge&&(c=!0,l+=1);let u=[];for(let p=0;p<l+1;p++)u.push(a.readUInt32LE(4*p));let b=u.length-(c?2:1),h=[],m=s.size;for(let p=0;p<b;p++){let d=a.subarray(u[p],u[p+1]);s.flags&Se&&(r||m>d.length)&&(d=n(d)),m-=d.length,h.push(d)}a=Buffer.concat(h)}return a}hash(e,r){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let o=e.toUpperCase().charCodeAt(s);n=(Ee[($e[r]<<8)+o]^n+i)>>>0,i=o+n+i+(i<<5)+3>>>0}return n}decrypt(e,r){let n=r>>>0,i=4008636142,s=Buffer.alloc(e.length),o=e.length/4;for(let a=0;a<o;a++){i=i+Ee[1024+(n&255)]>>>0;let f=e.readUInt32LE(a*4);f=(f^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=f+i+(i<<5)+3>>>0,s.writeUInt32LE(f,a*4)}return s}};var G=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},M=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},Q=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,r="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=r==="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 G;let r=this._data.subarray(this._used,this._used+e);return this._used+=e,r}read_bits(e){let r=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new G;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?r+=s*Math.pow(2,e-n-i):r+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_bits_bigint(e){let r=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new G;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?r|=s<<BigInt(e-n-i):r|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_unaligned_bytes(e){let r=Buffer.alloc(e);for(let n=0;n<e;n++)r[n]=this.read_bits(8);return r}};var $=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new Q(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new M(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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,r){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){let r=this._int(e);return[r,this._buffer.read_bits(r)]}_blob(e){let r=this._int(e);return this._buffer.read_aligned_bytes(r)}_bool(){return this._int([0,1])!==0}_choice(e,r){let n=this._int(e);if(!(n in r))throw new M(`Choice tag ${n} not found`);let i=r[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),r=Buffer.alloc(4);return r.writeUInt32BE(e,0),r.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 r={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null)r={...r,...i};else{if(e.length===1)return i;r[n[0]]=i}}else r[n[0]]=this.instance(n[1]);return r}},L=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new Q(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new M(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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 M(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),r=(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=r?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,r){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){this._expect_skip(1);let r=Number(this._vint());return[r,this._buffer.read_aligned_bytes(Math.floor((r+7)/8))]}_blob(e){this._expect_skip(2);let r=Number(this._vint());return this._buffer.read_aligned_bytes(r)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,r){this._expect_skip(3);let n=Number(this._vint());if(!(n in r))return this._skip_instance(),{};let i=r[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 r={},n=Number(this._vint());for(let i=0;i<n;i++){let s=Number(this._vint()),o=e.find(a=>a[2]===s);if(o)if(o[0]==="__parent"){let a=this.instance(o[1]);typeof a=="object"&&a!==null?r={...r,...a}:e.length===1?r=a:r[o[0]]=a}else r[o[0]]=this.instance(o[1]);else this._skip_instance()}return r}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let r=Number(this._vint());for(let n=0;n<r;n++)this._skip_instance()}else if(e===1){let r=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((r+7)/8))}else if(e===2){let r=Number(this._vint());this._buffer.read_aligned_bytes(r)}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 r=Number(this._vint());for(let n=0;n<r;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 V from"fs";import H from"path";function We(){let t=[];try{t.push(H.resolve(__dirname,"..","protocols")),t.push(H.resolve(__dirname,"..","..","protocols"))}catch{}t.push(H.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==H.dirname(e);){let r=H.join(e,"node_modules","@astefanski","storm-parser","protocols");t.push(r),e=H.dirname(e)}for(let r of t)if(V.existsSync(r)&&V.readdirSync(r).some(n=>n.endsWith(".json")))return r;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: `+t.join(", "))}var re=new Map,ne=null;function Re(){return ne||(ne=We()),ne}function J(t){if(re.has(t))return re.get(t);let e=Re(),r=H.join(e,`protocol${t}.json`);if(!V.existsSync(r))return null;let n=JSON.parse(V.readFileSync(r,"utf-8"));return re.set(t,n),n}function Be(){let t=Re();return V.readdirSync(t).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,r)=>e-r)}var W=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new Z(e,!1)}init(){let e=J(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 r=this.mpq.header.userDataHeader;if(!r||!r.content)throw new Error("Replay does not have a user data header");let n=new L(r.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 r=J(e);if(!r){let n=Be(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);r=J(i)}if(!r)throw new Error(`No protocol found for build ${e}`);return r}*decodeEventStream(e,r,n,i){if(!this.protocol)throw new Error("Protocol not loaded");let s=0;for(;!e.done();){let o=e.used_bits(),a=e.instance(this.protocol.svaruint32_typeid),f=Object.keys(a)[0],l=a[f];s+=l;let c=i?e.instance(this.protocol.replay_userid_typeid):void 0,u=Number(e.instance(r)),b=n[u];if(!b)throw new Error(`Unknown eventid(${u})`);let h=b[0],m=b[1],p=e.instance(h);p._event=m,p._eventid=u,p._gameloop=s,i&&(p._userid=c),e.byte_align(),p._bits=e.used_bits()-o,yield p}}getDetails(){if(!this.protocol)throw new Error("Protocol not loaded");let e=this.mpq.readFile("replay.details");return e?new L(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 $(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 r=new L(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 r=new $(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 De={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},qe={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function O(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}function v(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?r.m_value:void 0}function ee(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?O(r.m_value):void 0}function w(t,e){if(!t)return;let r=t.find(n=>O(n.m_key)===e);return r!==void 0?r.m_value:void 0}function ie(t,e){return`${t}-${e}`}function P(t,e){return(t-e)/16}function Ce(t,e,r){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};r.levelTimes={0:{},1:{}},r.takedowns=[],r.structures={},r.XPBreakdown=[],r.mercs={captures:[],units:{}},r.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:r.map||""};for(let i of t)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":Ye(i,n,e,r);break;case"NNet.Replay.Tracker.SUnitBornEvent":et(i,n,r);break;case"NNet.Replay.Tracker.SUnitDiedEvent":tt(i,n,r);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":rt(i,n);break}return r.loopGameStart=n.loopGameStart,r.loopLength=n.loopGameEnd,r.length=(n.loopGameEnd-n.loopGameStart)/16,nt(n,e),{playerIDMap:n.playerIDMap}}function Ye(t,e,r,n){let i=O(t.m_eventName),s=t.m_intData,o=t.m_stringData,a=t.m_fixedData;switch(i){case"PlayerInit":{let f=v(s,"PlayerID"),l=ee(o,"ToonHandle");f!==void 0&&l&&r[l]&&(e.playerIDMap[f]=l);break}case"GatesOpen":e.loopGameStart=t._gameloop;break;case"LevelUp":{let f=v(s,"PlayerID"),l=v(s,"Level");if(f===void 0||l===void 0)break;let c;if(f>=1&&f<=5)c="0";else if(f>=6&&f<=10)c="1";else break;n.levelTimes[c][String(l)]||(n.levelTimes[c][String(l)]={loop:t._gameloop,level:l,team:c,time:P(t._gameloop,e.loopGameStart)});break}case"TalentChosen":{let f=v(s,"PlayerID"),l=ee(o,"PurchaseName");if(f===void 0||!l)break;let c=e.playerIDMap[f];if(!c||!r[c])break;let u=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let b of u)if(!r[c].talents[b]){r[c].talents[b]=l;break}break}case"PlayerDeath":{let f=v(s,"PlayerID"),l=v(s,"KillingPlayer"),c=w(a,"PositionX")??0,u=w(a,"PositionY")??0;if(f===void 0)break;let b=e.playerIDMap[f];if(!b)break;let h={player:b,hero:r[b]?.hero||""},m=[],p=r[b]?.team;if(l&&l>0){let _=e.playerIDMap[l];_&&r[_]&&m.push({player:_,hero:r[_].hero})}for(let[_,y]of Object.entries(e.playerIDMap)){let x=r[y];!x||x.team===p||parseInt(_)===l||m.push({player:y,hero:x.hero})}let d={loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),x:c,y:u,killers:m,victim:h};n.takedowns.push(d),r[b]&&r[b].deaths.push(d);for(let _ of m)r[_.player]&&r[_.player].takedowns.push(d);break}case"PeriodicXPBreakdown":{let f=v(s,"Team");if(f===void 0)break;let l={GameTime:v(s,"GameTime")??0,PreviousGameTime:v(s,"PreviousGameTime")??0,MinionXP:w(a,"MinionXP")??0,CreepXP:w(a,"CreepXP")??0,StructureXP:w(a,"StructureXP")??0,HeroXP:w(a,"HeroXP")??0,TrickleXP:w(a,"TrickleXP")??0};n.XPBreakdown.push({loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),team:f,teamLevel:v(s,"TeamLevel")??0,breakdown:l,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let f=v(s,"Team");if(f===void 0)break;n.XPBreakdown.push({loop:t._gameloop,time:P(t._gameloop,e.loopGameStart),team:f,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:w(a,"MinionXP")??0,CreepXP:w(a,"CreepXP")??0,StructureXP:w(a,"StructureXP")??0,HeroXP:w(a,"HeroXP")??0,TrickleXP:w(a,"TrickleXP")??0}});break}case"JungleCampCapture":{let f=v(s,"CampTeam")??v(s,"Team")??0;n.mercs.captures.push({loop:t._gameloop,type:ee(o,"CampType")??ee(o,"Result")??"Unknown Camp",team:f,time:P(t._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let f=v(s,"PlayerID");if(f===void 0)break;let l=e.playerIDMap[f];if(!l||!r[l])break;let c={},u=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(o)for(let b=0;b<o.length&&b<u.length;b++){let h=O(o[b].m_value);h&&(c[u[b]]=h)}r[l].talents=c;break}default:Je(i,t,e,n,s,a);break}}var Ze=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function Je(t,e,r,n,i,s){if(!Ze.has(t)||!i)return;let o=v(i,"Team")??v(i,"Event")??0,a=o===0||o===1?o:0,f={team:o,loop:e._gameloop,time:P(e._gameloop,r.loopGameStart),score:v(i,"Score"),duration:w(s,"Duration")};n.objective[a].events.push(f),n.objective[a].count=n.objective[a].events.length}function et(t,e,r){let n=O(t.m_unitTypeName),i=t.m_unitTagIndex,s=t.m_unitTagRecycle,o=ie(i,s),a=t.m_controlPlayerId??t.m_upkeepPlayerId,f=t.m_x??0,l=t.m_y??0,c;if(a>=1&&a<=5?c=0:a>=6&&a<=10?c=1:a===11?c=0:a===12?c=1:c=a<=5?0:1,e.unitIndex[o]={type:n,playerId:a,team:c,x:f,y:l,bornLoop:t._gameloop},n.startsWith("Town")&&De[n]&&(r.structures[o]={type:n,name:De[n],tag:i,rtag:s,x:f,y:l,team:c}),n.startsWith("Hero")&&a>=1&&a<=10){e.heroUnits[o]=a,e.heroLives[a]||(e.heroLives[a]=[]);let u=P(t._gameloop,e.loopGameStart);e.heroLives[a].push({born:u,locations:[{x:f,y:l,time:u}],duration:0})}if(qe[n]){let b=r.mercs.captures[r.mercs.captures.length-1]?.loop??t._gameloop;r.mercs.units[o]={loop:b,team:c,type:n,locations:[{x:f,y:l}],time:P(b,e.loopGameStart),duration:0}}}function tt(t,e,r){let n=ie(t.m_unitTagIndex,t.m_unitTagRecycle),i=P(t._gameloop,e.loopGameStart);r.structures[n]&&(r.structures[n].destroyedLoop=t._gameloop,r.structures[n].destroyed=i),r.mercs.units[n]&&(r.mercs.units[n].duration=i-r.mercs.units[n].time);let s=e.heroUnits[n];if(s!==void 0&&e.heroLives[s]){let o=e.heroLives[s],a=o[o.length-1];a&&a.died===void 0&&(a.died=i,a.duration=i-a.born)}}function rt(t,e){let r=ie(t.m_unitTagIndex,t.m_unitTagRecycle),n=t.m_controlPlayerId??t.m_upkeepPlayerId;e.unitIndex[r]&&(e.unitIndex[r].playerId=n,n>=1&&n<=5?e.unitIndex[r].team=0:n>=6&&n<=10&&(e.unitIndex[r].team=1))}function nt(t,e){for(let[r,n]of Object.entries(t.heroLives)){let i=parseInt(r,10),s=t.playerIDMap[i];if(!s||!e[s])continue;for(let a of n)if(a.died===void 0){let f=a.locations[a.locations.length-1];a.duration=f?f.time-a.born:0}let o="";for(let[a,f]of Object.entries(t.heroUnits))if(f===i){o=a;break}o&&(e[s].units[o]={lives:n})}}var it=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function st(){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 ot(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:st(),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 Ae(t,e){at(t,e),ct(t,e),lt(t),ut(t),dt(t,e),bt(t)}function at(t,e){for(let r of["0","1"]){let n=t.teams[r];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=ot();for(let u of n.ids){let b=e[u];b&&(n.names.push(b.name),n.heroes.push(b.hero),n.tags.push(b.tag))}for(let u of n.ids){let b=e[u];if(b)for(let h of it){let m=b.gameStats[h];typeof m=="number"&&(n.stats.totals[h]+=m)}}let i=n.ids.reduce((u,b)=>u+(e[b]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),t.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(t.length*n.ids.length));let s=n.takedowns,o=i;n.stats.KDA=o>0?s/o:s,n.stats.PPK=s>0?t.takedowns.filter(u=>n.ids.includes(u.killers[0]?.player)).reduce((u,b)=>u+b.killers.length,0)/s:0;let a=t.levelTimes[r];a?.["10"]&&(n.stats.timeTo10=a[10].time);let f=parseInt(r,10),l=t.mercs.captures.filter(u=>u.team===f);n.stats.mercCaptures=l.length;let c=0;for(let u of Object.values(t.mercs.units))u.team===f&&u.duration>0&&(c+=u.duration);n.stats.mercUptime=c,n.stats.mercUptimePercent=t.length>0?c/t.length:0,ft(t,n,r)}}function ft(t,e,r){let n=parseInt(r,10),i=n===0?1:0,s={};for(let o of Object.values(t.structures)){let a=o.name;s[a]||(s[a]={lost:0,destroyed:0,first:t.length}),o.team===i&&o.destroyed!==void 0&&(s[a].destroyed++,s[a].first=Math.min(s[a].first,o.destroyed)),o.team===n&&o.destroyed!==void 0&&s[a].lost++}e.stats.structures=s}function ct(t,e){let r=t.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,o=i.TeamTakedowns??0;r>0&&(i.DPM=(i.HeroDamage??0)/r,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/r,i.XPM=(i.ExperienceContribution??0)/r),i.KDA=s>0?(i.Takedowns??0)/s:i.Takedowns??0,i.KillParticipation=o>0?(i.Takedowns??0)/o: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=t.length}}function lt(t){let e=0,r=0,n=new Set(t.teams[0]?.ids||[]);for(let i of t.takedowns)n.has(i.victim.player)?r++:e++;t.team0Takedowns=e,t.team1Takedowns=r}function ut(t){let e=t.levelTimes[0]||{},r=t.levelTimes[1]||{},n=[];for(let l of Object.values(e))n.push({time:l.time,team:0,level:l.level});for(let l of Object.values(r))n.push({time:l.time,team:1,level:l.level});if(n.sort((l,c)=>l.time-c.time),n.length===0){t.levelAdvTimeline=[];return}let i=[],s=0,o=0,a=n[0].time;for(let l of n){let c=s-o;l.time>a&&i.push({start:a,end:l.time,levelDiff:c,length:l.time-a}),l.team===0?s=l.level:o=l.level,a=l.time}let f=s-o;t.length>a&&i.push({start:a,end:t.length,levelDiff:f,length:t.length-a}),t.levelAdvTimeline=i;for(let l of["0","1"]){let c=t.teams[l];if(!c)continue;let u=l==="0"?1:-1,b=0,h=0,m=0,p=0;for(let d of i){let _=d.levelDiff*u;_>0&&(b+=d.length),h=Math.max(h,_),m+=_*d.length,p+=d.length}c.stats.levelAdvTime=b,c.stats.maxLevelAdv=h,c.stats.avgLevelAdv=p>0?m/p:0,c.stats.levelAdvPct=t.length>0?b/t.length:0}}function dt(t,e){for(let s of["0","1"]){let o=t.teams[s];if(!o)continue;let a=[];for(let d of o.ids){let _=e[d];if(_)for(let y of _.deaths){a.push({time:y.time,delta:-1});let x=y.time+mt(_.gameStats.Level??1,y.time,t.length);x<t.length&&a.push({time:x,delta:1})}}a.sort((d,_)=>d.time-_.time);let f=[{time:0,heroes:o.ids.length}],l=o.ids.length;for(let d of a)l+=d.delta,l=Math.max(0,Math.min(o.ids.length,l)),f.push({time:d.time,heroes:l});o.stats.uptime=f;let c={};for(let d=0;d<f.length;d++){let y=(d<f.length-1?f[d+1].time:t.length)-f[d].time,x=String(f[d].heroes);c[x]=(c[x]||0)+y}o.stats.uptimeHistogram=c;let u=0,b=0;for(let d=0;d<f.length;d++){let y=(d<f.length-1?f[d+1].time:t.length)-f[d].time;u+=f[d].heroes*y,b+=y}o.stats.avgHeroesAlive=b>0?u/b:o.ids.length,o.stats.wipes=f.filter(d=>d.heroes===0).length,o.stats.aces=0;let h=s==="0"?"1":"0",m=t.teams[h];m?.stats?.uptime&&(o.stats.aces=m.stats.uptime.filter(d=>d.heroes===0).length);let p=t.XPBreakdown.filter(d=>d.team===parseInt(s,10));if(p.length>0){let _=p[p.length-1].breakdown.TrickleXP;o.stats.passiveXPGain=_,o.stats.passiveXPRate=t.length>0?_/(t.length/60):0}}for(let s of["0","1"]){let o=t.teams[s],a=s==="0"?"1":"0",f=t.teams[a];if(!o||!f)continue;let l=o.stats.uptime,c=f.stats.uptime;if(!l.length||!c.length)continue;let u=new Set;for(let m of l)u.add(m.time);for(let m of c)u.add(m.time);let b=Array.from(u).sort((m,p)=>m-p),h=0;for(let m=0;m<b.length;m++){let p=b[m],_=(m<b.length-1?b[m+1]:t.length)-p,y=Ie(l,p),x=Ie(c,p);y>x&&(h+=_)}o.stats.timeWithHeroAdv=h,o.stats.pctWithHeroAdv=t.length>0?h/t.length:0}let r=t.teams[0]?.stats.passiveXPRate??0,n=t.teams[1]?.stats.passiveXPRate??0,i=(r+n)/2;t.teams[0]&&(t.teams[0].stats.passiveXPDiff=i>0?r/i:0),t.teams[1]&&(t.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function Ie(t,e){let r=0;for(let n of t)if(n.time<=e)r=n.heroes;else break;return r}function mt(t,e,r){return t<=1?15:t<=5?15+(t-1)*2:t<=10?23+(t-5)*3:t<=15?38+(t-10)*4:58+(t-15)*5}function bt(t){let e=1/0,r=-1,n=1/0,i=-1;for(let o of Object.values(t.structures))o.destroyed!==void 0&&(o.name==="Fort"&&o.destroyed<e&&(e=o.destroyed,r=o.team===0?1:0),o.name==="Keep"&&o.destroyed<n&&(n=o.destroyed,i=o.team===0?1:0));r>=0&&(t.firstFort=r,t.firstFortWin=r===t.winner),i>=0&&(t.firstKeep=i,t.firstKeepWin=i===t.winner);let s=[...t.objective[0].events.map(o=>({...o,assignedTeam:0})),...t.objective[1].events.map(o=>({...o,assignedTeam:1}))].sort((o,a)=>o.loop-a.loop);s.length>0&&(t.firstObjective=s[0].assignedTeam,t.firstObjectiveWin=s[0].assignedTeam===t.winner),t.firstPickWin=t.picks.first===t.winner}function D(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}var se=class{static async analyze(e){try{let r=new W(e);await r.init();let n=r.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=r.getTrackerEvents(),s=r.getInitData(),a=r.getHeader()?.m_version??{},f={m_flags:a.m_flags??0,m_major:a.m_major??0,m_minor:a.m_minor??0,m_revision:a.m_revision??0,m_build:a.m_baseBuild??r.getBuild(),m_baseBuild:a.m_baseBuild??r.getBuild()},l=n.m_playerList.find(h=>h?.m_toon)?.m_toon,c={version:f,map:D(n.m_title),date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:l?.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 u={};for(let h of n.m_playerList){if(!h?.m_toon)continue;let m=h.m_toon,p=D(m.m_programId),d=`${m.m_region}-${p}-${m.m_realm}-${m.m_id}`,_=D(h.m_hero);u[d]={hero:_,name:D(h.m_name),uuid:m.m_id,region:m.m_region,realm:m.m_realm,ToonHandle:d,tag:0,team:h.m_teamId,win:h.m_result===1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},c.playerIDs.push(d),c.heroes.push(_)}this.extractBattleTags(r,n,u),this.extractDraft(s,c,n);let{playerIDMap:b}=Ce(i,u,c);this.processScoreEvents(i,b,u);for(let[h,m]of Object.entries(u)){m.win&&(c.winner=m.team,c.winningPlayers.push(h));let p=c.teams[m.team.toString()];p&&(p.level=Math.max(p.level,m.gameStats.Level||0),p.takedowns+=m.gameStats.Takedowns||0,p.ids.push(h))}return Ae(c,u),{status:1,match:c,players:u}}catch(r){return console.error("ReplayAnalyzer Error:",r),{status:-2,error:String(r)}}}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,r,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"),o=i.toString("utf8").match(s);if(!o)return;let a=0;for(let f of r.m_playerList){if(!f?.m_toon)continue;let l=D(f.m_name);for(;a<o.length;){let u=o[a].split("#"),b=u[0],h=u[1].replace(/[zØ]/g,"");if(a++,b===l){let m=f.m_toon,p=`${m.m_region}-${D(m.m_programId)}-${m.m_realm}-${m.m_id}`;n[p]&&(n[p].tag=parseInt(h,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let o=[],a=[];for(let c of n.m_playerList){if(!c?.m_toon)continue;let u=D(c.m_hero);c.m_teamId===0?o.push(u):c.m_teamId===1&&a.push(u)}if(r.picks={0:o,1:a,first:0},typeof s.m_gameMode=="number"&&(r.mode=s.m_gameMode),typeof s.m_gameType=="number"&&(r.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let c=s.m_firstPickTeam??0;r.picks.first=c}}catch{}}static processScoreEvents(e,r,n){for(let i of e){if(i._event!=="NNet.Replay.Tracker.SScoreResultEvent")continue;let s=i.m_instanceList;for(let o of s){let a=D(o.m_name),f=o.m_values,l=a.startsWith("EndOfMatchAward"),c=0;for(let u of f)if(u&&u.length>0&&u[0]!==void 0){let b=c+1,h=r[b];if(h&&n[h]){let m=typeof u[0]=="object"&&u[0]!==null&&"m_value"in u[0]?u[0].m_value:u[0];m!=null&&(l?m===1&&n[h].awards.push(a):n[h].gameStats[a]=m)}c++}else u&&u.length===0&&c++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};export{se as ReplayAnalyzer,W as ReplayParser};
1
+ var X=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var de=X((ht,le)=>{"use strict";var fe=[0,1,3,7,15,31,63,127,255],F=function(t){this.stream=t,this.bitOffset=0,this.curByte=0,this.hasByte=!1};F.prototype._ensureByte=function(){this.hasByte||(this.curByte=this.stream.readByte(),this.hasByte=!0)};F.prototype.read=function(t){for(var e=0;t>0;){this._ensureByte();var r=8-this.bitOffset;if(t>=r)e<<=r,e|=fe[r]&this.curByte,this.hasByte=!1,this.bitOffset=0,t-=r;else{e<<=t;var n=r-t;e|=(this.curByte&fe[t]<<n)>>n,this.bitOffset+=t,t=0}}return e};F.prototype.seek=function(t){var e=t%8,r=(t-e)/8;this.bitOffset=e,this.stream.seek(r),this.hasByte=!1};F.prototype.pi=function(){var t=new Buffer(6),e;for(e=0;e<t.length;e++)t[e]=this.read(8);return t.toString("hex")};le.exports=F});var me=X((_t,ue)=>{"use strict";var A=function(){};A.prototype.readByte=function(){throw new Error("abstract method readByte() not implemented")};A.prototype.read=function(t,e,r){for(var n=0;n<r;){var i=this.readByte();if(i<0)return n===0?-1:n;t[e++]=i,n++}return n};A.prototype.seek=function(t){throw new Error("abstract method seek() not implemented")};A.prototype.writeByte=function(t){throw new Error("abstract method readByte() not implemented")};A.prototype.write=function(t,e,r){var n;for(n=0;n<r;n++)this.writeByte(t[e++]);return r};A.prototype.flush=function(){};ue.exports=A});var pe=X((gt,be)=>{"use strict";be.exports=(function(){var t=new Uint32Array([0,79764919,159529838,222504665,319059676,398814059,445009330,507990021,638119352,583659535,797628118,726387553,890018660,835552979,1015980042,944750013,1276238704,1221641927,1167319070,1095957929,1595256236,1540665371,1452775106,1381403509,1780037320,1859660671,1671105958,1733955601,2031960084,2111593891,1889500026,1952343757,2552477408,2632100695,2443283854,2506133561,2334638140,2414271883,2191915858,2254759653,3190512472,3135915759,3081330742,3009969537,2905550212,2850959411,2762807018,2691435357,3560074640,3505614887,3719321342,3648080713,3342211916,3287746299,3467911202,3396681109,4063920168,4143685023,4223187782,4286162673,3779000052,3858754371,3904687514,3967668269,881225847,809987520,1023691545,969234094,662832811,591600412,771767749,717299826,311336399,374308984,453813921,533576470,25881363,88864420,134795389,214552010,2023205639,2086057648,1897238633,1976864222,1804852699,1867694188,1645340341,1724971778,1587496639,1516133128,1461550545,1406951526,1302016099,1230646740,1142491917,1087903418,2896545431,2825181984,2770861561,2716262478,3215044683,3143675388,3055782693,3001194130,2326604591,2389456536,2200899649,2280525302,2578013683,2640855108,2418763421,2498394922,3769900519,3832873040,3912640137,3992402750,4088425275,4151408268,4197601365,4277358050,3334271071,3263032808,3476998961,3422541446,3585640067,3514407732,3694837229,3640369242,1762451694,1842216281,1619975040,1682949687,2047383090,2127137669,1938468188,2001449195,1325665622,1271206113,1183200824,1111960463,1543535498,1489069629,1434599652,1363369299,622672798,568075817,748617968,677256519,907627842,853037301,1067152940,995781531,51762726,131386257,177728840,240578815,269590778,349224269,429104020,491947555,4046411278,4126034873,4172115296,4234965207,3794477266,3874110821,3953728444,4016571915,3609705398,3555108353,3735388376,3664026991,3290680682,3236090077,3449943556,3378572211,3174993278,3120533705,3032266256,2961025959,2923101090,2868635157,2813903052,2742672763,2604032198,2683796849,2461293480,2524268063,2284983834,2364738477,2175806836,2238787779,1569362073,1498123566,1409854455,1355396672,1317987909,1246755826,1192025387,1137557660,2072149281,2135122070,1912620623,1992383480,1753615357,1816598090,1627664531,1707420964,295390185,358241886,404320391,483945776,43990325,106832002,186451547,266083308,932423249,861060070,1041341759,986742920,613929101,542559546,756411363,701822548,3316196985,3244833742,3425377559,3370778784,3601682597,3530312978,3744426955,3689838204,3819031489,3881883254,3928223919,4007849240,4037393693,4100235434,4180117107,4259748804,2310601993,2373574846,2151335527,2231098320,2596047829,2659030626,2470359227,2550115596,2947551409,2876312838,2788305887,2733848168,3165939309,3094707162,3040238851,2985771188]),e=function(){var r=4294967295;this.getCRC=function(){return~r>>>0},this.updateCRC=function(n){r=r<<8^t[(r>>>24^n)&255]},this.updateCRCRun=function(n,i){for(;i-- >0;)r=r<<8^t[(r>>>24^n)&255]}};return e})()});var he=X((yt,Me)=>{Me.exports={name:"seek-bzip",version:"2.0.0",contributors:["C. Scott Ananian (http://cscott.net)","Eli Skeggs","Kevin Kwok","Rob Landley (http://landley.net)"],description:"a pure-JavaScript Node.JS module for random-access decoding bzip2 data",main:"./lib/index.js",repository:{type:"git",url:"https://github.com/cscott/seek-bzip.git"},license:"MIT",bin:{"seek-bunzip":"./bin/seek-bunzip","seek-table":"./bin/seek-bzip-table"},directories:{test:"test"},dependencies:{commander:"^6.0.0"},devDependencies:{fibers:"^5.0.0",mocha:"^8.1.0"},scripts:{test:"mocha"}}});var ke=X((xt,Se)=>{"use strict";var He=de(),j=me(),xe=pe(),ve=he(),Y=20,_e=258,ge=0,Le=1,Oe=2,Ue=6,Ne=50,ze="314159265359",Xe="177245385090",ye=function(t,e){var r=t[e],n;for(n=e;n>0;n--)t[n]=t[n-1];return t[0]=r,r},g={OK:0,LAST_BLOCK:-1,NOT_BZIP_DATA:-2,UNEXPECTED_INPUT_EOF:-3,UNEXPECTED_OUTPUT_EOF:-4,DATA_ERROR:-5,OUT_OF_MEMORY:-6,OBSOLETE_INPUT:-7,END_OF_BLOCK:-8},B={};B[g.LAST_BLOCK]="Bad file checksum";B[g.NOT_BZIP_DATA]="Not bzip data";B[g.UNEXPECTED_INPUT_EOF]="Unexpected input EOF";B[g.UNEXPECTED_OUTPUT_EOF]="Unexpected output EOF";B[g.DATA_ERROR]="Data error";B[g.OUT_OF_MEMORY]="Out of memory";B[g.OBSOLETE_INPUT]="Obsolete (pre 0.9.5) bzip format not supported.";var T=function(t,e){var r=B[t]||"unknown error";e&&(r+=": "+e);var n=new TypeError(r);throw n.errorCode=t,n},k=function(t,e){this.writePos=this.writeCurrent=this.writeCount=0,this._start_bunzip(t,e)};k.prototype._init_block=function(){var t=this._get_next_block();return t?(this.blockCRC=new xe,!0):(this.writeCount=-1,!1)};k.prototype._start_bunzip=function(t,e){var r=new Buffer(4);(t.read(r,0,4)!==4||String.fromCharCode(r[0],r[1],r[2])!=="BZh")&&T(g.NOT_BZIP_DATA,"bad magic");var n=r[3]-48;(n<1||n>9)&&T(g.NOT_BZIP_DATA,"level out of range"),this.reader=new He(t),this.dbufSize=1e5*n,this.nextoutput=0,this.outputStream=e,this.streamCRC=0};k.prototype._get_next_block=function(){var t,e,r,n=this.reader,i=n.pi();if(i===Xe)return!1;i!==ze&&T(g.NOT_BZIP_DATA),this.targetBlockCRC=n.read(32)>>>0,this.streamCRC=(this.targetBlockCRC^(this.streamCRC<<1|this.streamCRC>>>31))>>>0,n.read(1)&&T(g.OBSOLETE_INPUT);var s=n.read(24);s>this.dbufSize&&T(g.DATA_ERROR,"initial position out of bounds");var a=n.read(16),o=new Buffer(256),c=0;for(t=0;t<16;t++)if(a&1<<15-t){var f=t*16;for(r=n.read(16),e=0;e<16;e++)r&1<<15-e&&(o[c++]=f+e)}var l=n.read(3);(l<Oe||l>Ue)&&T(g.DATA_ERROR);var d=n.read(15);d===0&&T(g.DATA_ERROR);var u=new Buffer(256);for(t=0;t<l;t++)u[t]=t;var m=new Buffer(d);for(t=0;t<d;t++){for(e=0;n.read(1);e++)e>=l&&T(g.DATA_ERROR);m[t]=ye(u,e)}var b=c+2,h=[],p;for(e=0;e<l;e++){var _=new Buffer(b),y=new Uint16Array(Y+1);for(a=n.read(5),t=0;t<b;t++){for(;(a<1||a>Y)&&T(g.DATA_ERROR),!!n.read(1);)n.read(1)?a--:a++;_[t]=a}var x,P;for(x=P=_[0],t=1;t<b;t++)_[t]>P?P=_[t]:_[t]<x&&(x=_[t]);p={},h.push(p),p.permute=new Uint16Array(_e),p.limit=new Uint32Array(Y+2),p.base=new Uint32Array(Y+1),p.minLen=x,p.maxLen=P;var I=0;for(t=x;t<=P;t++)for(y[t]=p.limit[t]=0,a=0;a<b;a++)_[a]===t&&(p.permute[I++]=a);for(t=0;t<b;t++)y[_[t]]++;for(I=a=0,t=x;t<P;t++)I+=y[t],p.limit[t]=I-1,I<<=1,a+=y[t],p.base[t+1]=I-a;p.limit[P+1]=Number.MAX_VALUE,p.limit[P]=I+y[P]-1,p.base[x]=0}var O=new Uint32Array(256);for(t=0;t<256;t++)u[t]=t;var C=0,R=0,ae=0,E,N=this.dbuf=new Uint32Array(this.dbufSize);for(b=0;;){for(b--||(b=Ne-1,ae>=d&&T(g.DATA_ERROR),p=h[m[ae++]]),t=p.minLen,e=n.read(t);t>p.maxLen&&T(g.DATA_ERROR),!(e<=p.limit[t]);t++)e=e<<1|n.read(1);e-=p.base[t],(e<0||e>=_e)&&T(g.DATA_ERROR);var z=p.permute[e];if(z===ge||z===Le){C||(C=1,a=0),z===ge?a+=C:a+=2*C,C<<=1;continue}if(C)for(C=0,R+a>this.dbufSize&&T(g.DATA_ERROR),E=o[u[0]],O[E]+=a;a--;)N[R++]=E;if(z>c)break;R>=this.dbufSize&&T(g.DATA_ERROR),t=z-1,E=ye(u,t),E=o[E],O[E]++,N[R++]=E}for((s<0||s>=R)&&T(g.DATA_ERROR),e=0,t=0;t<256;t++)r=e+O[t],O[t]=e,e=r;for(t=0;t<R;t++)E=N[t]&255,N[O[E]]|=t<<8,O[E]++;var q=0,oe=0,ce=0;return R&&(q=N[s],oe=q&255,q>>=8,ce=-1),this.writePos=q,this.writeCurrent=oe,this.writeCount=R,this.writeRun=ce,!0};k.prototype._read_bunzip=function(t,e){var r,n,i;if(this.writeCount<0)return 0;for(var s=0,a=this.dbuf,o=this.writePos,c=this.writeCurrent,f=this.writeCount,l=this.outputsize,d=this.writeRun;f;){for(f--,n=c,o=a[o],c=o&255,o>>=8,d++===3?(r=c,i=n,c=-1):(r=1,i=c),this.blockCRC.updateCRCRun(i,r);r--;)this.outputStream.writeByte(i),this.nextoutput++;c!=n&&(d=0)}return this.writeCount=f,this.blockCRC.getCRC()!==this.targetBlockCRC&&T(g.DATA_ERROR,"Bad block CRC (got "+this.blockCRC.getCRC().toString(16)+" expected "+this.targetBlockCRC.toString(16)+")"),this.nextoutput};var te=function(t){if("readByte"in t)return t;var e=new j;return e.pos=0,e.readByte=function(){return t[this.pos++]},e.seek=function(r){this.pos=r},e.eof=function(){return this.pos>=t.length},e},Te=function(t){var e=new j,r=!0;if(t)if(typeof t=="number")e.buffer=new Buffer(t),r=!1;else{if("writeByte"in t)return t;e.buffer=t,r=!1}else e.buffer=new Buffer(16384);return e.pos=0,e.writeByte=function(n){if(r&&this.pos>=this.buffer.length){var i=new Buffer(this.buffer.length*2);this.buffer.copy(i),this.buffer=i}this.buffer[this.pos++]=n},e.getBuffer=function(){if(this.pos!==this.buffer.length){if(!r)throw new TypeError("outputsize does not match decoded input");var n=new Buffer(this.pos);this.buffer.copy(n,0,0,this.pos),this.buffer=n}return this.buffer},e._coerced=!0,e};k.Err=g;k.decode=function(t,e,r){for(var n=te(t),i=Te(e),s=new k(n,i);!("eof"in n&&n.eof());)if(s._init_block())s._read_bunzip();else{var a=s.reader.read(32)>>>0;if(a!==s.streamCRC&&T(g.DATA_ERROR,"Bad stream CRC (got "+s.streamCRC.toString(16)+" expected "+a.toString(16)+")"),r&&"eof"in n&&!n.eof())s._start_bunzip(n,i);else break}if("getBuffer"in i)return i.getBuffer()};k.decodeBlock=function(t,e,r){var n=te(t),i=Te(r),s=new k(n,i);s.reader.seek(e);var a=s._get_next_block();if(a&&(s.blockCRC=new xe,s.writeCopies=0,s._read_bunzip()),"getBuffer"in i)return i.getBuffer()};k.table=function(t,e,r){var n=new j;n.delegate=te(t),n.pos=0,n.readByte=function(){return this.pos++,this.delegate.readByte()},n.delegate.eof&&(n.eof=n.delegate.eof.bind(n.delegate));var i=new j;i.pos=0,i.writeByte=function(){this.pos++};for(var s=new k(n,i),a=s.dbufSize;!("eof"in n&&n.eof());){var o=n.pos*8+s.reader.bitOffset;if(s.reader.hasByte&&(o-=8),s._init_block()){var c=i.pos;s._read_bunzip(),e(o,i.pos-c)}else{var f=s.reader.read(32);if(r&&"eof"in n&&!n.eof())s._start_bunzip(n,i),console.assert(s.dbufSize===a,"shouldn't change block size within multistream file");else break}}};k.Stream=j;k.version=ve.version;k.license=ve.license;Se.exports=k});import*as Pe from"fs";import*as K from"zlib";var Fe=ke(),we=512,je=65536,Ke=16777216,Ge=67108864,Qe=2147483648,$e={TABLE_OFFSET:0,HASH_A:1,HASH_B:2,TABLE:3};function Ve(){let t=1048577,e=new Uint32Array(256*5);for(let r=0;r<256;r++){let n=r;for(let i=0;i<5;i++){t=(t*125+3)%2796203;let s=(t&65535)<<16;t=(t*125+3)%2796203;let a=t&65535;e[n]=(s|a)>>>0,n+=256}}return e}var Ee=Ve(),Z=class{file;header;hashTable;blockTable;files;constructor(e,r=!0){if(Buffer.isBuffer(e)?this.file=e:this.file=Pe.readFileSync(e),this.header=this.readHeader(),this.hashTable=this.readTable("hash"),this.blockTable=this.readTable("block"),r){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),r;if(e==="MPQ")r=this.readMPQHeader(),r.offset=0;else if(e==="MPQ\x1B"){let n=this.readMPQUserDataHeader();r=this.readMPQHeader(n.mpqHeaderOffset),r.offset=n.mpqHeaderOffset,r.userDataHeader=n}else throw new Error("Invalid MPQ file header");return r}readMPQHeader(e=0){let r=this.file.subarray(e,e+32),n={magic:r.toString("utf8",0,4),headerSize:r.readUInt32LE(4),archiveSize:r.readUInt32LE(8),formatVersion:r.readUInt16LE(12),sectorSizeShift:r.readUInt16LE(14),hashTableOffset:r.readUInt32LE(16),blockTableOffset:r.readUInt32LE(20),hashTableEntries:r.readUInt32LE(24),blockTableEntries:r.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),r={magic:e.toString("utf8",0,4),userDataSize:e.readUInt32LE(4),mpqHeaderOffset:e.readUInt32LE(8),userDataHeaderSize:e.readUInt32LE(12)};return r.content=this.file.subarray(16,16+r.userDataHeaderSize),r}readTable(e){let r=e==="hash"?"hashTableOffset":"blockTableOffset",n=e==="hash"?"hashTableEntries":"blockTableEntries",i=this.header[r],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 c=[];for(let f=0;f<s;f++){let l=o.subarray(f*16,f*16+16);e==="hash"?c.push({hashA:l.readUInt32LE(0),hashB:l.readUInt32LE(4),locale:l.readUInt16LE(8),platform:l.readUInt16LE(10),blockTableIndex:l.readUInt32LE(12)}):c.push({offset:l.readUInt32LE(0),archivedSize:l.readUInt32LE(4),size:l.readUInt32LE(8),flags:l.readUInt32LE(12)})}return c}getHashTableEntry(e){let r=this.hash(e,"HASH_A"),n=this.hash(e,"HASH_B");for(let i of this.hashTable)if(i.hashA===r&&i.hashB===n)return i}readFile(e,r=!1){function n(c){let f=c[0];if(f===0)return c;if(f===2)return K.inflateSync(c.subarray(1));if(f===16)return Fe.decode(c.subarray(1));try{return K.inflateSync(c.subarray(1))}catch{return K.inflateRawSync(c.subarray(1))}}let i=this.getHashTableEntry(e);if(!i)return null;let s=this.blockTable[i.blockTableIndex];if(!s||!(s.flags&Qe))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&je)throw new Error("Encryption is not supported");if(s.flags&Ke)s.flags&we&&(r||s.size>s.archivedSize)&&(o=n(o));else{let c=512<<this.header.sectorSizeShift,f=Math.trunc(s.size/c)+1,l=!1;s.flags&Ge&&(l=!0,f+=1);let d=[];for(let h=0;h<f+1;h++)d.push(o.readUInt32LE(4*h));let u=d.length-(l?2:1),m=[],b=s.size;for(let h=0;h<u;h++){let p=o.subarray(d[h],d[h+1]);s.flags&we&&(r||b>p.length)&&(p=n(p)),b-=p.length,m.push(p)}o=Buffer.concat(m)}return o}hash(e,r){let n=2146271213,i=4008636142;for(let s=0;s<e.length;s++){let a=e.toUpperCase().charCodeAt(s);n=(Ee[($e[r]<<8)+a]^n+i)>>>0,i=a+n+i+(i<<5)+3>>>0}return n}decrypt(e,r){let n=r>>>0,i=4008636142,s=Buffer.alloc(e.length),a=e.length/4;for(let o=0;o<a;o++){i=i+Ee[1024+(n&255)]>>>0;let c=e.readUInt32LE(o*4);c=(c^n+i)>>>0,n=((~n<<21)+286331153|n>>>11)>>>0,i=c+i+(i<<5)+3>>>0,s.writeUInt32LE(c,o*4)}return s}};var G=class extends Error{constructor(e="Truncated Buffer"){super(e),this.name="TruncatedError"}},M=class extends Error{constructor(e="Corrupted Buffer"){super(e),this.name="CorruptedError"}},Q=class{_data;_used;_next;_nextbits;_bigendian;constructor(e,r="big"){this._data=e,this._used=0,this._next=0,this._nextbits=0,this._bigendian=r==="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 G;let r=this._data.subarray(this._used,this._used+e);return this._used+=e,r}read_bits(e){let r=0,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new G;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?r+=s*Math.pow(2,e-n-i):r+=s*Math.pow(2,n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_bits_bigint(e){let r=0n,n=0;for(;n!==e;){if(this._nextbits===0){if(this.done())throw new G;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?r|=s<<BigInt(e-n-i):r|=s<<BigInt(n),this._next>>=i,this._nextbits-=i,n+=i}return r}read_unaligned_bytes(e){let r=Buffer.alloc(e);for(let n=0;n<e;n++)r[n]=this.read_bits(8);return r}};var $=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new Q(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new M(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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,r){let n=this._int(e),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){let r=this._int(e);return[r,this._buffer.read_bits(r)]}_blob(e){let r=this._int(e);return this._buffer.read_aligned_bytes(r)}_bool(){return this._int([0,1])!==0}_choice(e,r){let n=this._int(e);if(!(n in r))throw new M(`Choice tag ${n} not found`);let i=r[n];return{[i[0]]:this.instance(i[1])}}_fourcc(){let e=this._buffer.read_bits(32),r=Buffer.alloc(4);return r.writeUInt32BE(e,0),r.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 r={};for(let n of e)if(n[0]==="__parent"){let i=this.instance(n[1]);if(typeof i=="object"&&i!==null&&!Array.isArray(i)&&!Buffer.isBuffer(i))r={...r,...i};else{if(e.length===1)return i;r[n[0]]=i}}else r[n[0]]=this.instance(n[1]);return r}},U=class{_buffer;_typeinfos;constructor(e,r){this._buffer=new Q(e),this._typeinfos=r}instance(e){if(e>=this._typeinfos.length)throw new M(`Invalid typeid ${e}`);let r=this._typeinfos[e],n=r[0],i=r[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 M(`Expected skip ${e}`)}_vint(){let e=this._buffer.read_bits(8),r=(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=r?-n:n;return s>=BigInt(Number.MIN_SAFE_INTEGER)&&s<=BigInt(Number.MAX_SAFE_INTEGER)?Number(s):s}_array(e,r){this._expect_skip(0);let n=Number(this._vint()),i=new Array(n);for(let s=0;s<n;s++)i[s]=this.instance(r);return i}_bitarray(e){this._expect_skip(1);let r=Number(this._vint());return[r,this._buffer.read_aligned_bytes(Math.floor((r+7)/8))]}_blob(e){this._expect_skip(2);let r=Number(this._vint());return this._buffer.read_aligned_bytes(r)}_bool(){return this._expect_skip(6),this._buffer.read_bits(8)!==0}_choice(e,r){this._expect_skip(3);let n=Number(this._vint());if(!(n in r))return this._skip_instance(),{};let i=r[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 r={},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&&!Array.isArray(o)&&!Buffer.isBuffer(o)?r={...r,...o}:e.length===1?r=o:r[a[0]]=o}else r[a[0]]=this.instance(a[1]);else this._skip_instance()}return r}_skip_instance(){let e=this._buffer.read_bits(8);if(e===0){let r=Number(this._vint());for(let n=0;n<r;n++)this._skip_instance()}else if(e===1){let r=Number(this._vint());this._buffer.read_aligned_bytes(Math.floor((r+7)/8))}else if(e===2){let r=Number(this._vint());this._buffer.read_aligned_bytes(r)}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 r=Number(this._vint());for(let n=0;n<r;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 V from"fs";import H from"path";function We(){let t=[];try{t.push(H.resolve(__dirname,"..","protocols")),t.push(H.resolve(__dirname,"..","..","protocols"))}catch{}t.push(H.resolve(process.cwd(),"protocols"));let e=process.cwd();for(;e!==H.dirname(e);){let r=H.join(e,"node_modules","@astefanski","storm-parser","protocols");t.push(r),e=H.dirname(e)}for(let r of t)if(V.existsSync(r)&&V.readdirSync(r).some(n=>n.endsWith(".json")))return r;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: `+t.join(", "))}var re=new Map,ne=null;function De(){return ne||(ne=We()),ne}function J(t){if(re.has(t))return re.get(t);let e=De(),r=H.join(e,`protocol${t}.json`);if(!V.existsSync(r))return null;let n=JSON.parse(V.readFileSync(r,"utf-8"));return re.set(t,n),n}function Re(){let t=De();return V.readdirSync(t).filter(e=>/^protocol\d+\.json$/.test(e)).map(e=>parseInt(e.match(/\d+/)[0],10)).sort((e,r)=>e-r)}var W=class{mpq;header;build=0;protocol;baseProtocol;constructor(e){this.mpq=new Z(e,!1)}init(){let e=J(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 r=this.mpq.header.userDataHeader;if(!r||!r.content)throw new Error("Replay does not have a user data header");let n=new U(r.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 r=J(e);if(!r){let n=Re(),i=n[0];for(let s of n)s<=e&&s>i&&(i=s);r=J(i)}if(!r)throw new Error(`No protocol found for build ${e}`);return r}*decodeEventStream(e,r,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),c=Object.keys(o)[0],f=o[c];s+=f;let l=i?e.instance(this.protocol.replay_userid_typeid):void 0,d=Number(e.instance(r)),u=n[d];if(!u)throw new Error(`Unknown eventid(${d})`);let m=u[0],b=u[1],h=e.instance(m);h._event=b,h._eventid=d,h._gameloop=s,i&&(h._userid=l),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 U(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 $(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 r=new U(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 r=new $(e,this.protocol.typeinfos);return Array.from(this.decodeEventStream(r,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 Be={TownCannonTowerL2:"Fort Tower",TownCannonTowerL3:"Keep Tower",TownTownHallL2:"Fort",TownTownHallL3:"Keep",TownMoonwellL2:"Fort Well",TownMoonwellL3:"Keep Well"},qe={MercLanerMeleeKnight:"Bruiser Camp",MercLanerRangedMage:"Bruiser Camp",MercLanerSiegeGiant:"Siege Camp",MercLanerRangedMinion:"Siege Camp"};function L(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}function v(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?r.m_value:void 0}function ee(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?L(r.m_value):void 0}function w(t,e){if(!t)return;let r=t.find(n=>L(n.m_key)===e);return r!==void 0?r.m_value:void 0}function ie(t,e){return`${t}-${e}`}function D(t,e){return(t-e)/16}function Ie(t,e,r){let n={playerIDMap:{},loopGameStart:0,loopGameEnd:0,unitIndex:{},heroUnits:{},heroLives:{}};r.levelTimes={0:{},1:{}},r.takedowns=[],r.structures={},r.XPBreakdown=[],r.mercs={captures:[],units:{}},r.objective={0:{count:0,events:[]},1:{count:0,events:[]},type:r.map||""};for(let i of t)switch(n.loopGameEnd=Math.max(n.loopGameEnd,i._gameloop),i._event){case"NNet.Replay.Tracker.SStatGameEvent":Ye(i,n,e,r);break;case"NNet.Replay.Tracker.SUnitBornEvent":et(i,n,r);break;case"NNet.Replay.Tracker.SUnitDiedEvent":tt(i,n,r);break;case"NNet.Replay.Tracker.SUnitOwnerChangeEvent":rt(i,n);break}return r.loopGameStart=n.loopGameStart,r.loopLength=n.loopGameEnd,r.length=(n.loopGameEnd-n.loopGameStart)/16,nt(n,e),{playerIDMap:n.playerIDMap}}function Ye(t,e,r,n){let i=L(t.m_eventName),s=t.m_intData,a=t.m_stringData,o=t.m_fixedData;switch(i){case"PlayerInit":{let c=v(s,"PlayerID"),f=ee(a,"ToonHandle");c!==void 0&&f&&r[f]&&(e.playerIDMap[c]=f);break}case"GatesOpen":e.loopGameStart=t._gameloop;break;case"LevelUp":{let c=v(s,"PlayerID"),f=v(s,"Level");if(c===void 0||f===void 0)break;let l;if(c>=1&&c<=5)l="0";else if(c>=6&&c<=10)l="1";else break;n.levelTimes[l][String(f)]||(n.levelTimes[l][String(f)]={loop:t._gameloop,level:f,team:l,time:D(t._gameloop,e.loopGameStart)});break}case"TalentChosen":{let c=v(s,"PlayerID"),f=ee(a,"PurchaseName");if(c===void 0||!f)break;let l=e.playerIDMap[c];if(!l||!r[l])break;let d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];for(let u of d)if(!r[l].talents[u]){r[l].talents[u]=f;break}break}case"PlayerDeath":{let c=v(s,"PlayerID"),f=v(s,"KillingPlayer"),l=w(o,"PositionX")??0,d=w(o,"PositionY")??0;if(c===void 0)break;let u=e.playerIDMap[c];if(!u)break;let m={player:u,hero:r[u]?.hero||""},b=[],h=r[u]?.team;if(f&&f>0){let _=e.playerIDMap[f];_&&r[_]&&b.push({player:_,hero:r[_].hero})}for(let[_,y]of Object.entries(e.playerIDMap)){let x=r[y];!x||x.team===h||parseInt(_)===f||b.push({player:y,hero:x.hero})}let p={loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),x:l,y:d,killers:b,victim:m};n.takedowns.push(p),r[u]&&r[u].deaths.push(p);for(let _ of b)r[_.player]&&r[_.player].takedowns.push(p);break}case"PeriodicXPBreakdown":{let c=v(s,"Team");if(c===void 0)break;let f={GameTime:v(s,"GameTime")??0,PreviousGameTime:v(s,"PreviousGameTime")??0,MinionXP:w(o,"MinionXP")??0,CreepXP:w(o,"CreepXP")??0,StructureXP:w(o,"StructureXP")??0,HeroXP:w(o,"HeroXP")??0,TrickleXP:w(o,"TrickleXP")??0};n.XPBreakdown.push({loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),team:c,teamLevel:v(s,"TeamLevel")??0,breakdown:f,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0});break}case"EndOfGameXPBreakdown":{let c=v(s,"Team");if(c===void 0)break;n.XPBreakdown.push({loop:t._gameloop,time:D(t._gameloop,e.loopGameStart),team:c,theoreticalMinionXP:v(s,"TheoreticalMinionXP")??0,breakdown:{GameTime:0,PreviousGameTime:0,MinionXP:w(o,"MinionXP")??0,CreepXP:w(o,"CreepXP")??0,StructureXP:w(o,"StructureXP")??0,HeroXP:w(o,"HeroXP")??0,TrickleXP:w(o,"TrickleXP")??0}});break}case"JungleCampCapture":{let c=v(s,"CampTeam")??v(s,"Team")??0;n.mercs.captures.push({loop:t._gameloop,type:ee(a,"CampType")??ee(a,"Result")??"Unknown Camp",team:c,time:D(t._gameloop,e.loopGameStart)});break}case"EndOfGameTalentChoices":{let c=v(s,"PlayerID");if(c===void 0)break;let f=e.playerIDMap[c];if(!f||!r[f])break;let l={},d=["Tier1Choice","Tier2Choice","Tier3Choice","Tier4Choice","Tier5Choice","Tier6Choice","Tier7Choice"];if(a)for(let u=0;u<a.length&&u<d.length;u++){let m=L(a[u].m_value);m&&(l[d[u]]=m)}r[f].talents=l;break}default:Je(i,t,e,n,s,o);break}}var Ze=new Set(["SoulEatersSpawned","TributeCollected","RavenCurseActivated","AltarCaptured","SkyTempleShotsFired","DragonKnightActivated","GardenTerrorActivated","InfernalShrineCaptured","PunisherKilled","VolskayaVehicleCapture","BraxisWaveStart","ImmortalDefeated","NukeExploded","PayloadDelivered","AlteracCavalryCharge","AlteracCavalry"]);function Je(t,e,r,n,i,s){if(!Ze.has(t)||!i)return;let a=v(i,"Team")??v(i,"Event")??0,o=a===0||a===1?a:0,c={team:a,loop:e._gameloop,time:D(e._gameloop,r.loopGameStart),score:v(i,"Score"),duration:w(s,"Duration")};n.objective[o].events.push(c),n.objective[o].count=n.objective[o].events.length}function et(t,e,r){let n=L(t.m_unitTypeName),i=t.m_unitTagIndex,s=t.m_unitTagRecycle,a=ie(i,s),o=t.m_controlPlayerId??t.m_upkeepPlayerId,c=t.m_x??0,f=t.m_y??0,l;if(o>=1&&o<=5?l=0:o>=6&&o<=10?l=1:o===11?l=0:o===12?l=1:l=o<=5?0:1,e.unitIndex[a]={type:n,playerId:o,team:l,x:c,y:f,bornLoop:t._gameloop},n.startsWith("Town")&&Be[n]&&(r.structures[a]={type:n,name:Be[n],tag:i,rtag:s,x:c,y:f,team:l}),n.startsWith("Hero")&&o>=1&&o<=10){e.heroUnits[a]=o,e.heroLives[o]||(e.heroLives[o]=[]);let d=D(t._gameloop,e.loopGameStart);e.heroLives[o].push({born:d,locations:[{x:c,y:f,time:d}],duration:0})}if(qe[n]){let u=r.mercs.captures[r.mercs.captures.length-1]?.loop??t._gameloop;r.mercs.units[a]={loop:u,team:l,type:n,locations:[{x:c,y:f}],time:D(u,e.loopGameStart),duration:0}}}function tt(t,e,r){let n=ie(t.m_unitTagIndex,t.m_unitTagRecycle),i=D(t._gameloop,e.loopGameStart);r.structures[n]&&(r.structures[n].destroyedLoop=t._gameloop,r.structures[n].destroyed=i),r.mercs.units[n]&&(r.mercs.units[n].duration=i-r.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 rt(t,e){let r=ie(t.m_unitTagIndex,t.m_unitTagRecycle),n=t.m_controlPlayerId??t.m_upkeepPlayerId;e.unitIndex[r]&&(e.unitIndex[r].playerId=n,n>=1&&n<=5?e.unitIndex[r].team=0:n>=6&&n<=10&&(e.unitIndex[r].team=1))}function nt(t,e){for(let[r,n]of Object.entries(t.heroLives)){let i=parseInt(r,10),s=t.playerIDMap[i];if(!s||!e[s])continue;for(let o of n)if(o.died===void 0){let c=o.locations[o.locations.length-1];o.duration=c?c.time-o.born:0}let a="";for(let[o,c]of Object.entries(t.heroUnits))if(c===i){a=o;break}a&&(e[s].units[a]={lives:n})}}var it=["DamageTaken","CreepDamage","Healing","HeroDamage","MinionDamage","SelfHealing","SiegeDamage","ProtectionGivenToAllies","TeamfightDamageTaken","TeamfightHealingDone","TeamfightHeroDamage","TimeCCdEnemyHeroes","TimeRootingEnemyHeroes","TimeSpentDead","TimeStunningEnemyHeroes","TimeSilencingEnemyHeroes"];function st(){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 at(){return{mercCaptures:0,mercUptime:0,mercUptimePercent:0,structures:{},KDA:0,PPK:0,timeTo10:0,totals:st(),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 Ae(t,e){ot(t,e),ft(t,e),lt(t),dt(t),ut(t,e),bt(t)}function ot(t,e){for(let r of["0","1"]){let n=t.teams[r];if(!n)continue;n.names=[],n.heroes=[],n.tags=[],n.stats=at();for(let d of n.ids){let u=e[d];u&&(n.names.push(u.name),n.heroes.push(u.hero),n.tags.push(u.tag))}for(let d of n.ids){let u=e[d];if(u)for(let m of it){let b=u.gameStats[m];typeof b=="number"&&(n.stats.totals[m]+=b)}}let i=n.ids.reduce((d,u)=>d+(e[u]?.gameStats.Deaths??0),0);i>0&&(n.stats.totals.avgTimeSpentDead=n.stats.totals.TimeSpentDead/i),t.length>0&&(n.stats.totals.timeDeadPct=n.stats.totals.TimeSpentDead/(t.length*n.ids.length));let s=n.takedowns,a=i;n.stats.KDA=a>0?s/a:s,n.stats.PPK=s>0?t.takedowns.filter(d=>n.ids.includes(d.killers[0]?.player)).reduce((d,u)=>d+u.killers.length,0)/s:0;let o=t.levelTimes[r];o?.["10"]&&(n.stats.timeTo10=o[10].time);let c=parseInt(r,10),f=t.mercs.captures.filter(d=>d.team===c);n.stats.mercCaptures=f.length;let l=0;for(let d of Object.values(t.mercs.units))d.team===c&&d.duration>0&&(l+=d.duration);n.stats.mercUptime=l,n.stats.mercUptimePercent=t.length>0?l/t.length:0,ct(t,n,r)}}function ct(t,e,r){let n=parseInt(r,10),i=n===0?1:0,s={};for(let a of Object.values(t.structures)){let o=a.name;s[o]||(s[o]={lost:0,destroyed:0,first:t.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 ft(t,e){let r=t.length/60;for(let n of Object.values(e)){let i=n.gameStats,s=i.Deaths??0,a=i.TeamTakedowns??0;r>0&&(i.DPM=(i.HeroDamage??0)/r,i.HPM=((i.Healing??0)+(i.SelfHealing??0))/r,i.XPM=(i.ExperienceContribution??0)/r),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=t.length}}function lt(t){let e=0,r=0,n=new Set(t.teams[0]?.ids||[]);for(let i of t.takedowns)n.has(i.victim.player)?r++:e++;t.team0Takedowns=e,t.team1Takedowns=r}function dt(t){let e=t.levelTimes[0]||{},r=t.levelTimes[1]||{},n=[];for(let f of Object.values(e))n.push({time:f.time,team:0,level:f.level});for(let f of Object.values(r))n.push({time:f.time,team:1,level:f.level});if(n.sort((f,l)=>f.time-l.time),n.length===0){t.levelAdvTimeline=[];return}let i=[],s=0,a=0,o=n[0].time;for(let f of n){let l=s-a;f.time>o&&i.push({start:o,end:f.time,levelDiff:l,length:f.time-o}),f.team===0?s=f.level:a=f.level,o=f.time}let c=s-a;t.length>o&&i.push({start:o,end:t.length,levelDiff:c,length:t.length-o}),t.levelAdvTimeline=i;for(let f of["0","1"]){let l=t.teams[f];if(!l)continue;let d=f==="0"?1:-1,u=0,m=0,b=0,h=0;for(let p of i){let _=p.levelDiff*d;_>0&&(u+=p.length),m=Math.max(m,_),b+=_*p.length,h+=p.length}l.stats.levelAdvTime=u,l.stats.maxLevelAdv=m,l.stats.avgLevelAdv=h>0?b/h:0,l.stats.levelAdvPct=t.length>0?u/t.length:0}}function ut(t,e){for(let s of["0","1"]){let a=t.teams[s];if(!a)continue;let o=[];for(let p of a.ids){let _=e[p];if(_)for(let y of _.deaths){o.push({time:y.time,delta:-1});let x=y.time+mt(_.gameStats.Level??1,y.time,t.length);x<t.length&&o.push({time:x,delta:1})}}o.sort((p,_)=>p.time-_.time);let c=[{time:0,heroes:a.ids.length}],f=a.ids.length;for(let p of o)f+=p.delta,f=Math.max(0,Math.min(a.ids.length,f)),c.push({time:p.time,heroes:f});a.stats.uptime=c;let l={};for(let p=0;p<c.length;p++){let y=(p<c.length-1?c[p+1].time:t.length)-c[p].time,x=String(c[p].heroes);l[x]=(l[x]||0)+y}a.stats.uptimeHistogram=l;let d=0,u=0;for(let p=0;p<c.length;p++){let y=(p<c.length-1?c[p+1].time:t.length)-c[p].time;d+=c[p].heroes*y,u+=y}a.stats.avgHeroesAlive=u>0?d/u:a.ids.length,a.stats.wipes=c.filter(p=>p.heroes===0).length,a.stats.aces=0;let m=s==="0"?"1":"0",b=t.teams[m];b?.stats?.uptime&&(a.stats.aces=b.stats.uptime.filter(p=>p.heroes===0).length);let h=t.XPBreakdown.filter(p=>p.team===parseInt(s,10));if(h.length>0){let _=h[h.length-1].breakdown.TrickleXP;a.stats.passiveXPGain=_,a.stats.passiveXPRate=t.length>0?_/(t.length/60):0}}for(let s of["0","1"]){let a=t.teams[s],o=s==="0"?"1":"0",c=t.teams[o];if(!a||!c)continue;let f=a.stats.uptime,l=c.stats.uptime;if(!f.length||!l.length)continue;let d=new Set;for(let b of f)d.add(b.time);for(let b of l)d.add(b.time);let u=Array.from(d).sort((b,h)=>b-h),m=0;for(let b=0;b<u.length;b++){let h=u[b],_=(b<u.length-1?u[b+1]:t.length)-h,y=Ce(f,h),x=Ce(l,h);y>x&&(m+=_)}a.stats.timeWithHeroAdv=m,a.stats.pctWithHeroAdv=t.length>0?m/t.length:0}let r=t.teams[0]?.stats.passiveXPRate??0,n=t.teams[1]?.stats.passiveXPRate??0,i=(r+n)/2;t.teams[0]&&(t.teams[0].stats.passiveXPDiff=i>0?r/i:0),t.teams[1]&&(t.teams[1].stats.passiveXPDiff=i>0?n/i:0)}function Ce(t,e){let r=0;for(let n of t)if(n.time<=e)r=n.heroes;else break;return r}function mt(t,e,r){return t<=1?15:t<=5?15+(t-1)*2:t<=10?23+(t-5)*3:t<=15?38+(t-10)*4:58+(t-15)*5}function bt(t){let e=1/0,r=-1,n=1/0,i=-1;for(let a of Object.values(t.structures))a.destroyed!==void 0&&(a.name==="Fort"&&a.destroyed<e&&(e=a.destroyed,r=a.team===0?1:0),a.name==="Keep"&&a.destroyed<n&&(n=a.destroyed,i=a.team===0?1:0));r>=0&&(t.firstFort=r,t.firstFortWin=r===t.winner),i>=0&&(t.firstKeep=i,t.firstKeepWin=i===t.winner);let s=[...t.objective[0].events.map(a=>({...a,assignedTeam:0})),...t.objective[1].events.map(a=>({...a,assignedTeam:1}))].sort((a,o)=>a.loop-o.loop);s.length>0&&(t.firstObjective=s[0].assignedTeam,t.firstObjectiveWin=s[0].assignedTeam===t.winner),t.firstPickWin=t.picks.first===t.winner}function S(t){return Buffer.isBuffer(t)?t.toString("utf8"):typeof t=="string"?t:String(t??"")}var se=class{static async analyze(e){try{let r=new W(e);await r.init();let n=r.getDetails();if(!n)throw new Error("Missing replay.details from parsed MPQ archive");let i=r.getTrackerEvents(),s=r.getInitData(),o=r.getHeader()?.m_version??{},c={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??r.getBuild(),m_baseBuild:o.m_baseBuild??r.getBuild()},f=n.m_playerList.find(m=>m?.m_toon)?.m_toon,l={version:c,map:S(n.m_title),isBlizzardMap:n.m_isBlizzardMap,timeLocalOffset:Number(n.m_timeLocalOffset),gameSpeed:n.m_gameSpeed,date:this.fileTimeToDate(n.m_timeUTC).toISOString(),rawDate:Number(n.m_timeUTC),length:0,winner:-1,region:f?.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};l.objective.type=l.map||"";let d={};for(let m of n.m_playerList){if(!m?.m_toon)continue;let b=m.m_toon,h=S(b.m_programId),p=`${b.m_region}-${h}-${b.m_realm}-${b.m_id}`,_=S(m.m_hero);d[p]={hero:_,name:S(m.m_name),uuid:b.m_id,region:b.m_region,realm:b.m_realm,ToonHandle:p,tag:0,team:m.m_teamId,win:m.m_result===1,heroLevel:0,skin:"",mount:"",banner:"",spray:"",clanTag:"",highestLeague:0,combinedRaceLevels:0,randomSeed:0,announcer:"",silenced:!1,voiceSilenced:!1,gameStats:{},awards:[],talents:{},takedowns:[],deaths:[],units:{}},l.playerIDs.push(p),l.heroes.push(_)}this.extractBattleTags(r,n,d),this.extractDraft(s,l,n),this.extractCosmetics(s,n,d);let{playerIDMap:u}=Ie(i,d,l);this.processScoreEvents(i,u,d);for(let[m,b]of Object.entries(d)){b.heroLevel=b.gameStats.Level||0,b.win&&(l.winner=b.team,l.winningPlayers.push(m));let h=l.teams[b.team.toString()];h&&(h.level=Math.max(h.level,b.gameStats.Level||0),h.takedowns+=b.gameStats.Takedowns||0,h.ids.push(m))}return Ae(l,d),{status:1,match:l,players:d}}catch(r){return console.error("ReplayAnalyzer Error:",r),{status:-2,error:String(r)}}}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,r,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 c of r.m_playerList){if(!c?.m_toon)continue;let f=S(c.m_name);for(;o<a.length;){let d=a[o].split("#"),u=d[0],m=d[1].replace(/[zØ]/g,"");if(o++,u===f){let b=c.m_toon,h=`${b.m_region}-${S(b.m_programId)}-${b.m_realm}-${b.m_id}`;n[h]&&(n[h].tag=parseInt(m,10)||0);break}}}}catch(s){console.error("BattleTag regex error:",s)}}static extractDraft(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState,a=i.m_gameDescription;if(!s)return;a&&(r.randomValue=a.m_randomValue,r.gameOptions=a.m_gameOptions);let o=[],c=[];for(let m of n.m_playerList){if(!m?.m_toon)continue;let b=S(m.m_hero);m.m_teamId===0?o.push(b):m.m_teamId===1&&c.push(b)}r.picks={0:o,1:c,first:0};let f,l=a?.m_gameOptions;if(l&&typeof l.m_ammId=="number"){let m=l.m_ammId;m===50001?f="Quick Match":m===50031?f="ARAM":m===50041?f="Unranked Draft":m===50051?f="Custom":m===50061?f="Hero League":m===50071?f="Team League":m===50091&&(f="Storm League")}if(!f&&typeof s.m_gameMode=="number"){let m=s.m_gameMode;m===3?f="Quick Match":m===4?f="Custom":m===5?f="Hero League":m===6?f="Team League":m===7?f="Unranked Draft":m===8&&(f="ARAM")}if(r.mode=f,typeof s.m_gameType=="number"&&(r.type=s.m_gameType),!s.m_slots)return;if(s.m_pickedMapTag!==void 0){let m=s.m_firstPickTeam??0;r.picks.first=m}}catch{}}static extractCosmetics(e,r,n){if(e)try{let i=e.m_syncLobbyState;if(!i)return;let s=i.m_lobbyState;if(!s)return;let a=s.m_slots,o=i.m_userInitialData;if(!a)return;let c=0;for(let f of r.m_playerList){if(!f?.m_toon)continue;let l=f.m_toon,d=`${l.m_region}-${S(l.m_programId)}-${l.m_realm}-${l.m_id}`,u=n[d];for(;c<a.length;){let m=a[c];c++;let b=m.m_hero;if(!(!b||Buffer.isBuffer(b)&&b.length===0)){if(u&&(u.skin=S(m.m_skin)||"",u.mount=S(m.m_mount)||"",u.announcer=S(m.m_announcerPack)||"",u.banner=S(m.m_banner)||"",u.spray=S(m.m_spray)||"",u.silenced=!!m.m_hasSilencePenalty,u.voiceSilenced=!!m.m_hasVoiceSilencePenalty,o)){let h=o.find(p=>S(p.m_name)===u.name);h&&(u.clanTag=S(h.m_clanTag),u.highestLeague=h.m_highestLeague,u.combinedRaceLevels=h.m_combinedRaceLevels,u.randomSeed=h.m_randomSeed)}break}}}}catch{}}static processScoreEvents(e,r,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),c=a.m_values,f=o.startsWith("EndOfMatchAward"),l=0;for(let d of c)if(d&&d.length>0&&d[0]!==void 0){let u=l+1,m=r[u];if(m&&n[m]){let b=typeof d[0]=="object"&&d[0]!==null&&"m_value"in d[0]?d[0].m_value:d[0];b!=null&&(f?b===1&&n[m].awards.push(o):n[m].gameStats[o]=b)}l++}else d&&d.length===0&&l++}}}static fileTimeToDate(e){return new Date(Number(e)/1e4-116444736e5)}};export{se as ReplayAnalyzer,W as ReplayParser};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astefanski/storm-parser",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Storm Parser is a tool for parsing Heroes of the Storm replays.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",