# GridFight extractor (Node ESM) Faithful port of the sibling extractor. Node 22+ and built-in modules only; validated with Node 26.12.0. Native reader proofs require GNU `objdump`. Linux installers use `flock`. XXH64 is implemented locally: no FFI, libxxhash, npm install, metadata dumper, network fetch or game launch is required. Run from the repository root: ```sh node tools/gridfight-extract-node/extract.mjs \ --client /path/to/client \ --dump /path/to/config/dump.cs \ --output /tmp/gridfight-core \ --traits --season-id 2 \ --evidence-dir /tmp/gridfight-core/evidence node --test tools/gridfight-extract-node ``` The dump must contain `RPG.GameCore.Config.dll`; traits also need `RPG.GameCore.Foundation.dll` and `RPG.GameCore.FixedMath.dll`. Use the matching `config/dump.cs`, rather than assuming the top-level dump contains those rows. Treat client files as read-only; place outputs and evidence outside the client. `extract.mjs` retains `--language` (default `en`, choices `en cn cht jp kr de es fr id pt ru th vi`), `--traits`, `--season-id`, and `--evidence-dir`. It publishes roles, shop odds, traits/layers, season/divisions, offerings, schemas and provenance with the same filenames and JSON contracts as the reference. Season selection is explicit; it does not establish a live calendar. Native mask tests and metadata establish field order. Every native indexed collection must decode completely and re-encode to its exact original bytes. Unknown types, absent/ambiguous assets, conflicting text and invalid references fail. Required data documents publish only after the requested validations succeed. Evidence may remain after failure. Each replacement is atomic; use a staging directory for bundle-level installation. Large integers retain all 64 bits in JSON. Arrays, numeric dictionary ordering, indentation and Unicode escaping retain the reference output representation. Other entrypoints preserve their original flags (`.mjs` replaces `.py`): | Entrypoint | Required flags | Optional flags/defaults | |---|---|---| | `stages.mjs` | `--client --dump --identity --output` | None | | `items.mjs` | `--client --dump --identity --output` | None | | `petpal.mjs` | `--client --dump --identity --output` | None | | `rounds.mjs` | `--client --dump --literals --identity --output` | `--language en` | | `trait_effects.mjs` | `--client` | `--output resources/grid_fight/trait_effects.json` | | `trailblaze.mjs` | `--client` | `--output resources/grid_fight/trailblaze.json` | | `jade.mjs` | `--client` | `--output resources/grid_fight/jade.json` | | `equipment.mjs` | None | `--items`, `--catalog`, `--season 2`, `--output` | | `portals.mjs` | None | `--items`, `--catalog`, `--season 2`, `--output`, `--augments` | | `score.mjs` | None | `--catalog`, `--output` | | `ui_names.mjs` | `--client --dump --resources --english --thai --output --evidence` | None | | `native_slice.mjs` | `--client --dump --class --output` | Repeat `--class` | | `audit_coverage.mjs` | `--dump --output` | `--source` defaults to repository root | | `audit_battle_entry.mjs` | `--client --dump --assembly --roles --evidence` | None | | `install_stages.mjs` | `--source --launcher --stage --report` | `--package stage` or `petpal` | | `install_rounds.mjs` | `--server-root --launcher-root --stage` | None | | `run_tests.mjs` | `--output` | `--zig zig` (requires 0.14.1 and cached protobuf) | Default derived-table inputs and outputs remain repository-relative: `resources/grid_fight/items.raw.json`, the matching basename's JSON, and `protocol/evidence/cw-mechanics/catalog.json`. Portals also writes `resources/grid_fight/augments.json`. Pass scratch output paths for comparison. For example: ```sh node tools/gridfight-extract-node/items.mjs \ --client /path/to/client --dump /path/to/config/dump.cs \ --identity /tmp/gridfight-core/provenance.json --output /tmp/gridfight-items node tools/gridfight-extract-node/equipment.mjs \ --items /tmp/gridfight-items/items.raw.json --season 2 \ --catalog /path/to/matching/catalog.json --output /tmp/gridfight-equipment.json ``` `stages`, `items` and `petpal` consume the core extractor's identity manifest. `rounds` consumes the client-table metadata identity (`client` and `metadata_sha256`) and matching string literals. Its underscore raw filenames, Supply `null`, and tagged DynamicValue contract intentionally differ from `stages` (dot raw filenames, Supply `shop`, and `wire_tag`). Neither guesses monster groups or tutorial selector meanings. Installers retain identity/hash checks, a nonblocking runtime-refresh lock, verified backups, atomic replacements, manifest refresh, protected-file checks and rollback. They use a local Node port of the runtime verification helpers. `install_rounds` stores its audit evidence in this new directory, preserving the sibling reference directory. No installer was run against real resources; installation was exercised only in disposable fixtures. Full season-2 staging, including a fresh native catalog for equipment/score and a file-by-file installed-data comparison: ```sh node tools/gridfight-extract-node/stage_all.mjs \ --client /path/to/client --dump /path/to/config/dump.cs \ --literals /path/to/stringliterals.json \ --english /path/to/active/en/TextMap.bytes \ --thai /path/to/active/th/TextMap.bytes \ --resources /path/to/server/resources \ --baseline /path/to/server/resources/grid_fight \ --output /tmp/cw-stage ``` The output must be outside the client and installed resources. All flags are required; no derived entrypoint falls back to the older repository catalog in this workflow. The active language blobs must match the client. Logs, native proofs, CHANGES.md, comparison.json and a hashed bundle manifest accompany the runtime JSON. Root rounds use the existing stages contract; the alternate rounds extractor's distinct contract is retained under rounds-alternative/. Server-authored node_profiles/orb_rewards are carried byte-identically, marked as third-party observations rather than freshly extracted client data. Old backup files are compared in the report but excluded from the runtime bundle. AvatarConfig/AeonCatalog provide existing UI identity/name lookup hints; UI text is resolved from the supplied client TextMaps. Staging does not install data. Validation on OSBETAWin4.6.53, season 2: - 51 tests pass, including native 4.6.53 dispatch mutation rejection, relocated 4.6.51 layout, nested nulls, signed 64-bit limits, NaN payloads, dictionary doubles and the Phainon schema's stack-write regression. - DynamicValue FromByteBinary resolves from metadata (4.6.53 RVA 0x1e745df0; 4.6.51 RVA 0x1e58e120). Both use case offsets 0x85/0xc5/0x102/0x14f/0x18a/0x1cf and dispatch offset 0x2b0. Typed case operations and dispatch bindings must match before evidence is published. The wire layout is unchanged; both projection modes support null tag 6. Integer and float bytes stay lossless. - Stages: all 14 tables, including 29,715 StageConfig rows, re-encode exactly. Fresh supplemental catalog: all 20 additional tables re-encode exactly, including all nine Phainon fields (stack addresses are excluded from native field candidates). Both stage and alternate-rounds extraction complete. - 141 roles, 11 shop levels, 67 traits, 304 season-2 augments, 74 season-2 portals, 147 season-2 equipment entries, 493 routes, 12 partners. Trial-avatar audit: 5,200 rows re-encoded exactly and all 141 role identities verified. - ui_names also exports item/augment/portal/trait hash maps for ID comparisons; the current run resolves 1,816 English and Thai texts. - No client data, installed resources, reference Python code, archives or live processes are changed. No gameplay-correctness claim is made. Use `node --test tools/gridfight-extract-node`. On hosts whose default test isolation reports only a file-level result, add `--test-isolation=none` to see all 51 individual results. Native proof fixtures are static objdump excerpts from the matching beta DLL; method offsets come from config/dump.cs.