ZERO-LAUNCH FIXES from the multi-agent pass over 0x18013af30 and 0x1801683f0.
FUT_STORE_FIELDS IS DELETED, AND IT WAS A FREEZE, NOT A REGRESSION. It sent
"actionType": 0 and "firstPartyStoreId": 0 as JSON INTEGERS. Both atoms (0x8, 0x127)
are read with the STRING getter 0x1801c7aa0. That is the exact type-desync class this
project exists to avoid. So "the corrections stopped packs opening" was never bad luck
or an unrelated field: two of them were the documented freeze mechanism, shipped by a
change whose own comment claimed it was correct about what the parser reads. Knowing
WHICH atoms a parser reads tells you nothing about which TYPES it demands. Read the
getter, every time. (Two other fields in that block were no-ops anyway: useDefaultImage
0x36a stores inverted, and visible 0x37d never reads its value at all.)
FUT_STORE_GROUPS IS DELETED and its freeze is now traced end to end. It sent
displayGroup as an ARRAY of pack-shaped objects on the belief that the key was parsed
recursively by the same element parser. It is not recursive at all: case 0xd9 never
re-enters 0x18013af30. It is a FLAT OBJECT with exactly two members. An array desyncs
the reader, the parser runs off the end of the document, and the tokenizer returns the
same token forever with nothing consumed, spinning inside FUN_1801c7f10 whose body
contains the observed PC 0x1801c7f1a.
Both were being kept as togglable "maybe nearly right" experiments. Each is a loaded
gun; neither survives contact with the decompile. Deleted rather than left switched off.
NEW FUT_STORE_DISPLAYGROUP (default 0): send the one key that actually names a tile,
displayGroup = {"value": "<pack name>"}. `value` (0x377, STRING) writes record offset
+0x00, the same slot whose constructor default is the literal "unknown" (the only such
literal in the DLL, 0x180223108). The tiles say "unknown" because nobody ever sent the
field. Distinct values per pack so grouping stays 1:1. priority/displayGroupAssetId/
displayGroupUseDefaultImage all omitted as second variables.
Default OFF for a reason the token-balance proof does not cover: this may be the first
field we have sent that selects a RENDER PATH rather than a value, and that code is
packed.
SEASON ROOT SHAPE CORRECTED. season_list() and its docstring were both wrong in the
same way: the deserializer is 0x1801683f0 (object root, one key seasons=0x2ad, array
inside), not 0x180167740, which is the per-ELEMENT parser. Someone read the element
parser and served its key set at the document root, so a bare array populated nothing.
Three of the keys served (eligibilityKey/Slot/Value) are inner members of elgReq and
inert even at element level. Also recorded: `type` must precede `divisionId` because
the divisionId branch reads the parsed type at elem+0x1b4.
Still behind FUT_MODES and still pointless to serve: across 486 real client requests
the game has NEVER asked for /season. Every /season line in our log is our own curl.
NEW FUT_CLUB_PAGE (default 0): an experiment, not a fix. The MY CLUB counter's
renderer is not in cardsdll (no two-number formatter of any spelling exists) and
FutStickerBookSearch has no count atom, so there is no field we can send that IS the
number. What is still testable is whether the counter is a Flash-side count over the
returned list, which a pure length change discriminates. A NULL RESULT IS THE VALUABLE
ONE: if the counter does not move, there is no server-side lever for this symptom and
the right outcome is to prove that and stop.
392 + 61 checks green, defaults byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUT92pz6RWKih9dSr8ZpxW
OpenFUT
Offline Ultimate Team — like SPT, but for FIFA 23.
OpenFUT replaces EA's retired FUT servers with a fully offline, single-player backend. You own FIFA 23 legitimately. You just want to keep playing after EA shut down the servers.
Repositories
| Repo | Purpose |
|---|---|
openfut-core |
Game-independent offline FUT backend |
openfut-bridge |
FIFA 23 integration layer + reverse-engineering proxy |
Architecture
FIFA 23 client
│
▼
┌─────────────────┐
│ openfut-bridge │ ← intercepts FUT traffic, maps routes, logs unknown
└────────┬────────┘
│
▼
┌─────────────────┐
│ openfut-core │ ← offline FUT backend: profiles, packs, squads, SBCs
└─────────────────┘
│
▼
SQLite database
Core is game-independent. It speaks a clean REST API and knows nothing about FIFA 23 internals.
Bridge is FIFA-specific. It speaks FIFA 23's wire protocol and translates it into Core API calls. Bridge is where all reverse engineering work happens.
Current Status
| Feature | Status |
|---|---|
| Local profiles + clubs | ✅ Working |
| Coin system | ✅ Working |
| Pack generator | ✅ Working |
| Card collection | ✅ Working |
| Squad builder | ✅ Working |
| Objectives engine | ✅ Working |
| SBC engine | ✅ Working |
| Match rewards | ✅ Working |
| NPC transfer market | ✅ Working |
| Statistics | ✅ Working |
| FIFA 23 integration | 🔴 Reverse engineering in progress |
| Chemistry calculation | 🟡 In progress |
| Full Draft mode | 🟡 In progress |
| Squad Battles AI generator | 🟡 In progress |
| Objectives claim flow | 🟡 In progress |
Running
# Start the offline backend
cd openfut-core
cargo run
# Start the proxy (for traffic capture / FIFA integration)
cd openfut-bridge
cargo run
Core listens on http://127.0.0.1:8080 by default.
Bridge listens on http://127.0.0.1:8443 by default.
Design Principles
- Offline-first. No EA account required. No internet connection needed.
- Single-player only. This is not a multiplayer server emulator.
- Data-driven. All cards, packs, SBCs, and objectives are JSON files. Everything is moddable.
- Clean architecture. Core has zero knowledge of FIFA 23. Bridge has zero game logic.
- No copyrighted assets. No real player images, no EA trademarks in data files.
Disclaimer
This project is not affiliated with EA Sports. You must own FIFA 23 legitimately to use this software. This project does not bypass any ownership verification and is intended only to restore offline playability of a game whose online services have been retired.