Skip to content

Commit d55a667

Browse files
committed
Handle other sections besides headers (no parsing code for them yet though, we just skip them after noting their positions), adjust WASM API to better serve the 'parse on demand' design.
1 parent 1743313 commit d55a667

File tree

7 files changed

+709
-228
lines changed

7 files changed

+709
-228
lines changed

Cargo.lock

Lines changed: 50 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

broodrep-wasm/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ crate-type = ["cdylib"]
1212

1313
[dependencies]
1414
broodrep = { path = "../broodrep" }
15-
wasm-bindgen = "0.2"
15+
chrono = { version = "0.4", features = ["serde", "wasm-bindgen"] }
16+
console_error_panic_hook = { version = "0.1", optional = true }
1617
js-sys = "0.3"
1718
serde = { version = "1.0", features = ["derive"] }
1819
serde-wasm-bindgen = "0.6"
19-
chrono = { version = "0.4", features = ["serde", "wasm-bindgen"] }
20-
console_error_panic_hook = { version = "0.1", optional = true }
20+
tsify = { version = "0.5", default-features = false, features = ["js"] }
21+
wasm-bindgen = "0.2"
2122

2223
[dependencies.web-sys]
2324
version = "0.3"

broodrep-wasm/README.md

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ fileInput.addEventListener('change', async event => {
2626
const uint8Array = new Uint8Array(arrayBuffer)
2727

2828
try {
29-
const replayInfo = parseReplay(uint8Array)
30-
console.log('Replay parsed:', replayInfo)
31-
console.log('Game title:', replayInfo.gameTitle)
32-
console.log('Map name:', replayInfo.mapName)
33-
console.log('Players:', replayInfo.activePlayers)
29+
const replay = parseReplay(uint8Array)
30+
const header = replay.header
31+
console.log('Replay parsed:', replay)
32+
console.log('Game title:', header.title)
33+
console.log('Map name:', header.mapName)
34+
console.log('Players:', replay.players().filter(p => !p.isEmpty && !p.isObserver))
3435
} catch (error) {
3536
console.error('Failed to parse replay:', error)
3637
}
@@ -48,73 +49,86 @@ const replayData = fs.readFileSync('example.rep')
4849
const uint8Array = new Uint8Array(replayData)
4950

5051
try {
51-
const replayInfo = parseReplay(uint8Array)
52-
console.log('Game:', replayInfo.gameTitle)
53-
console.log('Map:', replayInfo.mapName)
54-
console.log('Format:', replayInfo.format)
55-
console.log('Engine:', replayInfo.engine)
56-
console.log('Players:', replayInfo.activePlayers.length)
52+
const replay = parseReplay(uint8Array)
53+
const header = replay.header
54+
console.log('Game:', header.title)
55+
console.log('Map:', header.mapName)
56+
console.log('Format:', replay.format)
57+
console.log('Engine:', header.engine)
58+
console.log('Players:', replay.players().filter(p => !p.isEmpty && !p.isObserver).length)
5759
} catch (error) {
5860
console.error('Failed to parse replay:', error)
5961
}
6062
```
6163

6264
## API Reference
6365

64-
### `parseReplay(data: Uint8Array, options?: DecompressionOptions): ReplayInfo`
66+
### `parseReplay(data: Uint8Array, options?: DecompressionConfig): Replay`
6567

66-
Parses a StarCraft replay file and returns detailed information about the game.
68+
Parses a StarCraft replay file and returns a Replay object for retrieving game information.
6769

6870
**Parameters:**
6971

7072
- `data`: A `Uint8Array` containing the replay file bytes
7173
- `options`: Optional decompression configuration to customize security limits
7274

73-
**Returns:** A `ReplayInfo` object containing:
75+
**Returns:** A `Replay` object with the following interface:
7476

7577
```typescript
76-
interface ReplayInfo {
77-
format: string // "Legacy (pre-1.18)", "Modern (1.18-1.21)", or "Modern (1.21+)"
78-
engine: string // "StarCraft" or "Brood War"
78+
class Replay {
79+
readonly format: ReplayFormat // "legacy", "modern", or "modern121"
80+
readonly header: ReplayHeader // Game header information
81+
82+
// Methods for retrieving player information
83+
players(): Player[] // All player slots (including empty)
84+
observers(): Player[] // Only observers
85+
slots(): Player[] // All slots
86+
hostPlayer(): Player | undefined // The host player if identifiable
87+
88+
// Methods for retrieving raw section data
89+
getRawSection(section: ReplaySection): Uint8Array | undefined
90+
getRawCustomSection(section_id: number): Uint8Array | undefined
91+
}
92+
93+
interface ReplayHeader {
94+
engine: Engine // "starCraft", "broodWar", or "unknown"
7995
frames: number // Number of game frames
80-
startTime: number | null // Unix timestamp of game start (or null if invalid)
81-
gameTitle: string // Game title
82-
mapName: string // Map name
96+
startTime: number // Unix timestamp of game start
97+
title: string // Game title
8398
mapWidth: number // Map width in tiles
8499
mapHeight: number // Map height in tiles
85-
gameSpeed: string // Game speed setting
86-
gameType: string // Game type (e.g., "Melee", "Free For All")
100+
availableSlots: number // Number of available player slots
101+
speed: GameSpeed // Game speed setting
102+
gameType: GameType // Game type (e.g., "melee", "freeForAll")
87103
gameSubType: number // Game sub-type value
88104
hostName: string // Name of the game host
89-
players: PlayerInfo[] // All player slots (including empty)
90-
activePlayers: PlayerInfo[] // Only active players (non-empty, non-observers)
91-
observers: PlayerInfo[] // Only observers
105+
mapName: string // Map name
92106
}
93107

94-
interface PlayerInfo {
95-
slotId: number // Map slot ID
96-
networkId: number // Network ID
97-
playerType: string // "Human", "Computer", etc.
98-
race: string // "Terran", "Protoss", "Zerg", "Random"
108+
interface Player {
109+
slotId: number // Map slot ID (post-randomization)
110+
networkId: number // Network ID (255 for computer, 128-131 for observers)
111+
playerType: PlayerType // "inactive", "computer", "human", etc.
112+
race: Race // "zerg", "terran", "protoss", "random"
99113
team: number // Team number
100114
name: string // Player name
101115
isEmpty: boolean // Whether this is an empty slot
102116
isObserver: boolean // Whether this is an observer
103117
}
104118
```
105119

106-
### `DecompressionOptions`
120+
### `DecompressionConfig`
107121

108-
Configuration class for customizing security limits during replay parsing.
122+
Configuration object for customizing security limits during replay parsing.
109123

110124
```javascript
111-
import { DecompressionOptions } from './pkg/broodrep_wasm.js'
112-
113-
const options = new DecompressionOptions()
114-
options.maxDecompressedSize = 200 * 1024 * 1024 // 200MB
115-
options.maxCompressionRatio = 1000.0 // Allow 1000:1 compression ratio
125+
// Create decompression config object
126+
const options = {
127+
maxDecompressedSize: 200 * 1024 * 1024, // 200MB
128+
maxCompressionRatio: 1000.0 // Allow 1000:1 compression ratio
129+
}
116130

117-
const replayInfo = parseReplay(replayData, options)
131+
const replay = parseReplay(replayData, options)
118132
```
119133

120134
**Properties:**

broodrep-wasm/examples/index.html

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -261,40 +261,45 @@ <h3>Observers</h3>
261261
}
262262

263263
function displayReplayInfo(replay) {
264+
// Cache header for efficiency
265+
const header = replay.header
266+
const activePlayers = replay.players().filter(p => !p.isEmpty && !p.isObserver)
267+
const observers = replay.observers()
268+
264269
// Game Information
265270
const gameInfo = document.getElementById('gameInfo')
266271
gameInfo.innerHTML = `
267272
<div class="info-item">
268273
<div class="info-label">Title</div>
269-
<div class="info-value">${replay.gameTitle || 'Untitled'}</div>
274+
<div class="info-value">${header.title || 'Untitled'}</div>
270275
</div>
271276
<div class="info-item">
272277
<div class="info-label">Format</div>
273278
<div class="info-value">${replay.format}</div>
274279
</div>
275280
<div class="info-item">
276281
<div class="info-label">Engine</div>
277-
<div class="info-value">${replay.engine}</div>
282+
<div class="info-value">${header.engine}</div>
278283
</div>
279284
<div class="info-item">
280285
<div class="info-label">Game Type</div>
281-
<div class="info-value">${replay.gameType}</div>
286+
<div class="info-value">${header.gameType}</div>
282287
</div>
283288
<div class="info-item">
284289
<div class="info-label">Speed</div>
285-
<div class="info-value">${replay.gameSpeed}</div>
290+
<div class="info-value">${header.speed}</div>
286291
</div>
287292
<div class="info-item">
288293
<div class="info-label">Frames</div>
289-
<div class="info-value">${replay.frames.toLocaleString()}</div>
294+
<div class="info-value">${header.frames.toLocaleString()}</div>
290295
</div>
291296
<div class="info-item">
292297
<div class="info-label">Started</div>
293-
<div class="info-value">${formatDate(replay.startTime)}</div>
298+
<div class="info-value">${formatDate(header.startTime)}</div>
294299
</div>
295300
<div class="info-item">
296301
<div class="info-label">Host</div>
297-
<div class="info-value">${replay.hostName || 'Unknown'}</div>
302+
<div class="info-value">${header.hostName || 'Unknown'}</div>
298303
</div>
299304
`
300305

@@ -303,17 +308,17 @@ <h3>Observers</h3>
303308
mapInfo.innerHTML = `
304309
<div class="info-item">
305310
<div class="info-label">Map Name</div>
306-
<div class="info-value">${replay.mapName || 'Unknown'}</div>
311+
<div class="info-value">${header.mapName || 'Unknown'}</div>
307312
</div>
308313
<div class="info-item">
309314
<div class="info-label">Dimensions</div>
310-
<div class="info-value">${replay.mapWidth} × ${replay.mapHeight}</div>
315+
<div class="info-value">${header.mapWidth} × ${header.mapHeight}</div>
311316
</div>
312317
`
313318

314319
// Players
315320
const playersTableBody = document.getElementById('playersTableBody')
316-
playersTableBody.innerHTML = replay.activePlayers
321+
playersTableBody.innerHTML = activePlayers
317322
.map(
318323
player => `
319324
<tr>
@@ -331,9 +336,9 @@ <h3>Observers</h3>
331336
const observersSection = document.getElementById('observersSection')
332337
const observersTableBody = document.getElementById('observersTableBody')
333338

334-
if (replay.observers.length > 0) {
339+
if (observers.length > 0) {
335340
observersSection.style.display = 'block'
336-
observersTableBody.innerHTML = replay.observers
341+
observersTableBody.innerHTML = observers
337342
.map(
338343
observer => `
339344
<tr class="observer">

0 commit comments

Comments
 (0)