w3grs 0.1.0

A Rust port of w3gjs for parsing Warcraft III replay files.
Documentation
# w3grs

`w3grs` is a Rust port of [`w3gjs`](https://github.com/PBug90/w3gjs), intended to be a clean library for parsing Warcraft III replay files in other Rust projects.

The upstream TypeScript source is tracked as a Git submodule in `upstream/w3gjs` for parity tests, benchmarks, and future maintenance. The published crate contains the Rust library and a small fixture subset, not the full upstream replay corpus.

## Installation

`w3grs` requires Rust 1.85 or newer.

```sh
cargo add w3grs
```

## Development Checkout

Clone with submodules when you want to run the repository's `w3gjs` parity and speed comparison scripts:

```sh
git clone --recurse-submodules git@github.com:wakamex/w3grs.git
```

For an existing checkout:

```sh
git submodule update --init --recursive
```

## Usage

```rust
use w3grs::W3GReplay;

fn main() -> w3grs::Result<()> {
    let mut parser = W3GReplay::new();
    let replay = parser.parse_file("replay.w3g")?;

    println!("{} on {}", replay.matchup, replay.map.file);
    println!("players: {}", replay.players.len());

    Ok(())
}
```

Lower-level parser layers are also public:

- `RawParser` for replay headers and compressed data blocks
- `MetadataParser` for lobby/map/player setup data
- `GameDataParser` and `ActionParser` for timeslots, chat, leave events, and player actions
- `ReplayParser` for the combined low-level parse output

Consumers that need both the high-level summary and the raw action stream can parse once and use the timed action helper:

```rust
use w3grs::{W3GReplay, action::format_fourcc_or_hex};

fn main() -> w3grs::Result<()> {
    let mut parser = W3GReplay::new();
    let parsed = parser.parse_file_detailed("replay.w3g")?;

    println!("{} on {}", parsed.summary.matchup, parsed.summary.map.file);
    for timed in parsed.low_level.timed_actions() {
        println!(
            "frame={} player={} action=0x{:02x}",
            timed.frame,
            timed.player_id,
            timed.action.id()
        );
    }

    let order = format_fourcc_or_hex(*b"trah");
    println!("formatted order id: {order}");

    Ok(())
}
```

## Benchmark

From a development checkout with the submodule initialized, compare local `w3gjs` and `w3grs` parsing speed on the same replay:

```sh
node scripts/benchmark.mjs --prepare upstream/w3gjs/test/replays/132/reforged1.w3g
```

Useful options:

```sh
node scripts/benchmark.mjs replay.w3g --iterations 100 --warmup 10
node scripts/benchmark.mjs replay.w3g --json
```

The benchmark reads the replay once per parser process, warms both parsers, then reports timed in-process parses. `--prepare` runs the local `w3gjs` install/build and builds the Rust benchmark binary in release mode.

Example smoke result on this repo's `reforged1.w3g` fixture with 2 timed iterations and 1 warmup:

```text
Replay: upstream/w3gjs/test/replays/132/reforged1.w3g
Iterations: 2 timed, 1 warmup

Parser   total ms   mean ms   min ms    max ms    players
w3gjs       61.179    30.589    30.201    30.977        2
w3grs        1.863     0.932     0.849     1.014        2

w3grs mean speedup vs w3gjs: 32.84x
```

Benchmark results vary by machine, replay, iteration count, and current CPU load. Use larger iteration counts for less noisy comparisons:

```sh
node scripts/benchmark.mjs upstream/w3gjs/test/replays/132/reforged1.w3g --iterations 100 --warmup 10
```

## Upstream Parity And Speed Sweep

From a development checkout, check every replay fixture in `upstream/w3gjs/test/replays` for output parity while timing both parsers:

```sh
node scripts/compare-all.mjs --prepare --iterations 3 --warmup 1
```

The sweep reports:

- exact canonical JSON byte parity, including `parseTime`
- normalized canonical JSON parity with `parseTime` removed
- `w3gjs` and `w3grs` mean parse time per replay
- aggregate mean/min/max speedup

`parseTime` is expected to break exact parity because it measures each parser's runtime. Normalized parity is the useful output-equivalence signal.

Useful options:

```sh
node scripts/compare-all.mjs --iterations 10 --warmup 2
node scripts/compare-all.mjs --json
node scripts/compare-all.mjs --fail-on-mismatch --write-mismatches tmp/parity
```

Recent local smoke result on the upstream submodule replay fixtures with 1 timed parse and no warmup:

```text
Replays: 50
Exact byte parity: 0/50 (mismatches include expected parseTime differences)
Normalized parity without parseTime: 50/50
Speedup mean/min/max: 17.99x / 2.81x / 78.62x
```