The responder reads OPENFUT_ADVERTISE/OPENFUT_BIND at import time and several live payloads embed the advertised address (Blaze redirect target, RS4/POW/ roster URLs). Without pinning, regenerating on a machine that exports OPENFUT_ADVERTISE produces different bytes and --check goes red for a reason that has nothing to do with the codec. Pinned to 127.0.0.1 as a placeholder so no real LAN address is baked into a committed fixture. Not a claim about deployment: remote mode still requires an explicit advertised address and has no loopback fallback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
openfut-protocol-blaze
Game-independent EA Blaze wire protocol: Fire2 framing and the Heat2/TDF codec.
This is the first Rust component of the Python → Rust migration. It was chosen first because it is the lowest layer that is genuinely game-independent, it has an executable oracle, and both existing Rust implementations of it are wrong.
Scope
| In | Out |
|---|---|
| Fire2 16-byte frame header, frame/stream splitting | Component and command name tables |
| Heat2 tag packing, varints, the 11 value types | Notification IDs, response schemas |
| A message = frame + decoded body, routed by number | Login sequencing, session identity |
| Diagnostics dumps | Anything FIFA-17-specific |
The boundary test: could FIFA 18 or FIFA 23 use this without importing FIFA
17's command tables? This crate knows 0x0009/0x0007 is component 9, command
7. That it means Util::preAuth is a FIFA 17 fact, and belongs in a FIFA 17
adapter.
Provenance
Ported from fifa17-recon/tools/heat2.py (TDF codec) and
fifa17-recon/tools/blaze_responder_v3b.py (Fire2 framing) — the Python
implementation that drove a retail FIFA 17 client from Origin login to an
opened FUT pack. That implementation is the project's behavioural oracle, and
this crate does not claim parity with it, it tests parity against it.
Two superseded framing implementations
Both other Fire2 implementations in this repository are wrong for FIFA 17, and this crate exists partly to replace them:
fifa-blaze/crates/blaze-proto/src/frame.rs— a 12-byte header with au16length, nibble-packed type/options, anerrorfield, and a JUMBO-frame escape flag. Its own doc comment says FIFA 23's variant "is UNKNOWN" and that Fire2 was picked because it "is the most likely candidate". It predates the FIFA 17 recon and nothing has since validated it.fifa17-recon/tools/heat2.py::build_fire2_frame— packs>IHHHHB3s, putting au16 msgIdat[10:12]andmsgTypeat[12]. Dead code: the responder carries a comment telling callers not to use it, and it is not on any live path. Its module docstring still describes the old layout.
The proven layout is 16 bytes, with a u24 msgNum at [10:13] and
(msgType << 5) | userIndex in byte 13. There is no error field (that is
Fire v1) and no jumbo flag (the length is already a u32). The
payload_over_64kib_needs_no_jumbo_flag test is the direct refutation.
Confidence is not uniform
int, string, blob and struct are validated byte-exact against captured
FIFA 17 traffic. list, map, union, varlist, objtype, objid and
float are marked UNVERIFIED in the oracle — they are absent from every capture
we hold. TypeId::is_verified() reports which is which, and a test asserts the
unverified ones stay flagged, so "Rust and Python agree" can never be quietly
read as "this is how EA does it".
Testing
cargo test -p openfut-protocol-blaze # 43 unit + 10 differential + 1 doc
./check-parity.sh # regenerate fixtures, then re-verify
Differential vectors live in fixtures/, generated by fixtures/generate.py
from the Python oracle. 25 of the TDF vectors and 6 of the Fire2 vectors are
origin: "live" — real payloads from the responder's own builders, including
the preAuth reply (~11.8 KB) that is the first RPC FIFA 17 sends.
Comparison is byte-for-byte. FIFA 17's deserialisers hard-freeze on an
unexpected shape, so "semantically equivalent" is not a useful category at this
layer. (It is the right call one layer up at UTAS/JSON — see
fifa17-recon/tools/test_fut_contract.py.)
Regenerate fixtures after any change to the Python oracle:
python3 fixtures/generate.py # rewrite
python3 fixtures/generate.py --check # assert committed files are current
No runtime dependencies
Deliberate. This is a byte-level codec whose oracle is stdlib-only Python; there
is nothing here a third-party crate can do that std cannot. In particular the
crates.io tdf crate (already a fifa-blaze dependency) is not used: it
targets a generic BlazeSDK 15.x dialect, and adopting it would substitute
someone else's reading of the format for our own captured evidence. It remains
useful as an independent cross-check, not as the implementation.
serde_json is a dev-dependency only, for reading fixture files.
Not yet done
- No TLS, no sockets, no async. Framing and codec only; transport belongs to whatever hosts this.
- The Blaze redirector (HTTPS + XML
getServerInstance) is a different protocol and is not here. - Nothing consumes this crate yet. The Python backend remains the live runtime, untouched.