Dated source reference. Current release status: handoff state. Full original relative paths are in the developer archive.
Live game data: findings and extension contract
Storage cleanup72: bulky raw captures are now verified lossless archives, with an index and explicit restore helper. Historical capture contents and verification scope are unchanged.
Earlier API checkpoint (October15,2026):
0.2.0.dev36, HTTP v1, snapshot ABI14/event ABI4, college profile4/court definition3. Use the current contract, verification ledger and remaining scope. Research milestones below retain their original versions, results and uncertainties; their former next-step statements are historical. Latest natural college OT/export and18 both-end release-contact cases passed; rare live exceptions remain unverified. No public release or extra live smoke is required for this documentation pass.Current scope (October13,2026): Windows-first deep editing and a public extension API; Mac/Linux compatibility follows later. Earlier native-port goals and next-step proposals in this evidence history are superseded. Technical observations and verification limits remain as recorded.
Follow-up correction: Event semantics and reliability supersedes this survey for event meaning. Fast-break points are +0x2A, not +0x1A. Historical decoded files and validation describe the earlier decoder; raw captures remain intact.
Research date: October 12, 2026 America/Chicago (captures continue into October 13 UTC). We can already extract a useful named live box score and clocks without reconstructing the whole engine. A dependable external dynasty/career system also needs explicit match lifecycle, ordered events and authoritative rule decisions. Those are narrower reverse-engineering targets than a full decompilation, but they are not solved by collecting more arbitrary memory addresses.
This investigation used a CPU-versus-CPU Spurs–Heat Quick Game on the Windows reference machine. Miami was home. The supported original x86 executable SHA-256 is 7cb5fdaa30586558a3146dcf9023880b1cceeaabfe0f92f967f461c4644f3704; the existing external-mode/portable-saves v0.2 adapter remained installed. No community executable or new injected mod was run. The collector only opens the process for query/read and calls ReadProcessMemory. The original NBA2K14-extracted corpus was preserved.
The final observed checkpoint is Q2 3:14, Miami 18–San Antonio 14. This is a running Windows reference game, not a reconstructed native engine. The live reader has not been validated under Wine/Proton or on macOS/Linux. No full-match, overtime, restart, save/load or result-export guarantee is established.
Evidence and confidence
- Capability inventory: 238 quantities/capabilities, each tagged with its evidence, extraction approach and limitations. This count includes unmapped fields, derived metrics and systems still to build; it is not 238 working API fields.
- Validation: 172 checks covering capture integrity, independent screenshot values, player/team reconciliation, selected events, code bytes and bounded predicate verification.
- Three-point predicate comparison: 1,266 comparisons against exact captured x86 code, with the point-extraction helper replaced by controlled inputs.
- Evidence manifest: sources, versions, hashes and capture artifacts.
- Read-only collector, decoder, research validation script.
“Observed” means a value was reconciled with a visible game screen or a specific recorded transition. “Corroborated” means sources, code and observations agree, but independent coverage is incomplete. “Candidate” is a lead, not a supported API promise. Zero-only matches do not establish semantics. An event inferred from two snapshots is not an authoritative engine callback.
What is accessible now
| Area | Evidence obtained | Remaining work |
|---|---|---|
| Score and time | Home/away score, period, fractional game clock and shot clock; Q1→Q2 transition | Match-validity, pause/replay state, overtime and clock-display rules |
| Team box score | Points, two-/three-point attempts and makes, FT, OR/DR, AST/STL/BLK/TO/PF; paint and fast-break points; possession time | Other modes, team-only credits, corrections and full-match consistency |
| Individual players | 24 named roster entries, including bench players; common stat layout and playing-time values | Stable external roster IDs; independently check every player column and lifecycle |
| Lineups | Ten active actors join to the 24-player roster; substitutions observed | Ordered substitution events and assigned position/matchups |
| Spatial data | Actor positions and team directions from community code, moving values in our capture | Calibrate coordinate origin/units, render vs simulation pose, velocity, camera projection |
| Possession | Byte tracks home/away across observed possessions and rebounds | Separate physical control, entitlement, last touch and loose-ball state |
| Shot/FT state | Shot-active and FT-count leads, and-one/FT sequence captured; shot decision callsites match live code | Actual release/basket award/miss events; interruption and foul semantics |
| Court rules | Exact four-point three-point classification predicate, parameter banks and downstream score-value use | Rule selector labels, foul/AI/shot-chart callers, rendered line alignment |
| Historical scoring | GameTrack visibly retains scorer, period, time, shot type and resulting score | Recover its backing records and retention/reset behavior |
| Legal ball state | Timer/ball/shot leads inspected | Live-ball/dead-ball classification remains unmapped |
The game UI also exposes timeouts, biggest lead, bench points, second-chance points, points off turnovers and dunks. Their authoritative addresses are not yet established. A useful distinction: at our Q2 checkpoint the pause summary shows two San Antonio points on the current bench, while Team Stats shows three bench points from a player who began on the bench. Summing whoever is currently off court does not reproduce that statistic.
Exact current field map
All addresses below are virtual addresses in this one supported build, not portable addresses or file offsets. The game is x86. Treat pointers as 32-bit and fields with their actual widths. Community tables sometimes declare byte/word sizes for values native code accesses as DWORDs.
| Quantity | Address/type | Status and caveat |
|---|---|---|
| Period | 0x1DEE5EC, uint32 |
1 and 2 observed |
| Remaining game time | 0x1DEE638, float32 seconds |
UI 1:28 = 87.6581497; UI 3:14 = 193.5305176 |
| Remaining shot time | 0x1DEE654, float32 seconds |
Countdowns/resets observed |
| Rounded game/shot seconds | 0x1DEE5F0 / 0x1DEE5F4, uint32 |
Clock-display intermediates, not configured limits |
| Backcourt timer candidate | 0x1DEE670, float32 |
Community lead; violation boundary not tested |
| Timer flags | 0x1DEE640 / 0x1DEE65C, uint32 |
Insufficient to classify live/dead/pause |
| Team-possession candidate | 0x14F5655, byte |
Earlier 0 home / 255 away mapping fails after halftime; relationship reverses in Q3/Q4. See EVENT-SEMANTICS.md; do not use as fixed team identity. |
| Shot-active candidate | 0x1DEE8F8, uint32 |
Do not confuse with “ball is legally live” |
| FT remaining candidate | 0x1DEE8DC, uint32 |
Reaches zero before the final attempt is credited |
| Current shot point value | 0x1DEE45C, uint32 |
Cached/contextual value; changes outside actual releases |
| Shot distance/chance leads | 0x1E16290 / 0x1E16298, float32 |
Source leads; probability calibration absent |
| Native make-decision lead | 0x1E165C8, byte |
Prediction/decision, not final awarded basket |
| Fixed team statistics | home 0x1DE2188, away 0x1DE260C |
Stride 0x484 |
| Current context team pointers | [0x1DEE620], [0x1DEE624] |
Swap. Not fixed home/away roots and not always equal to possession byte |
| Primary ball pointer | [0x1CE716C] |
Bound actor at +0x120; flags at +0x184 |
| Three-point selector | 0x1E85DAC, uint32 |
Zero observed; labels for alternate mode unproven |
Common team/player statistics record
Native helper 0x6B5810 selects player statistics at actor + 0xC7C; reset logic at 0x884100 corroborates the layout. The following record offsets apply to our observations:
| Offset | Type | Meaning |
|---|---|---|
+0x00 |
uint32 | Points |
+0x04, +0x06 |
uint16 | FT made / attempted |
+0x08, +0x0A |
uint16 | Two-pointers made / attempted |
+0x0C, +0x0E |
uint16 | Three-pointers made / attempted |
+0x18, +0x2A |
uint16 | Paint points / fast-break points; corrected by final UI and scoring helper 0x8835C0 (see EVENT-SEMANTICS.md). Earlier +0x1A label was wrong; that field correlates with points off turnovers. |
+0x228, +0x22A |
uint16 | Offensive / defensive rebounds |
+0x240, +0x242, +0x244 |
uint16 | Steals / blocks / personal fouls |
+0x248, +0x24C |
uint16 | Assists / turnovers |
+0x258 |
int16 | Player plus/minus; team slot sums player values, not score margin |
+0x33C |
float32 | Player seconds played; first-quarter starters agree with elapsed time |
+0x358 |
float32 | Team possession seconds; matches UI truncation to whole seconds |
Total FGM = two-point makes + three-point makes; FGA similarly. Our first positive three-pointer exposed this distinction. At Q2 3:14, San Antonio's raw +8/+A was 5/12, its threes were 1/1, and the UI correctly showed 6/13. Miami's 8/13 plus 0/1 similarly produced 8/14. Earlier zero-three checkpoints alone could not distinguish these interpretations.
Offsets +0x246 and +0x24A appear to track points received from assisted scores and points generated by assists, respectively; they remain candidates. +0x246 is not fast-break points: it read four when the UI showed two. Separate per-period foul slots beginning around +0x3B8 need an explicit bonus/counter audit.
Scoring subrecords use a 0x5C stride: slot 0 total, slots 1/2 Q1/Q2 observed. The Q2 checkpoint matched home [18,12,6], away [14,12,2]. Getter 0x8845C0 clamps indices at 5; do not promise independently addressable unlimited overtime periods from this structure. Other per-period field layouts remain candidates.
Players, identities and positions
Roster roots are 0x1F1F730 and 0x1F1FA28, with count byte at +0x7D and pointer slots at root + slot×4. Our bounded probe caps count at 20. Each node has UTF-16 last/first name pointers at +0/+4, actor pointer at +0x30, and an appearance-ID candidate at +0x3C. Appearance IDs are not guaranteed unique roster IDs.
The active actor list starts at [0x1D95200], with sentinel 0x1D951D0; the collector bounds traversal and detects cycles. The active set stayed at ten during the established game and joined uniquely to roster entries before and after substitutions. Pregame/loading produced zero, one and two actors, so “actor list exists” is not sufficient session validation.
GreenMeter's code reads actor +0x20 as a pose pointer and float XYZ at pose +0x30/+0x34/+0x38; actor +0x4C points to team data. Observed vertical values are body-height-like, not guaranteed ground/foot positions. A basket coordinate of approximately 1274.445 and the community's distance conversions suggest centimeter units, but retain game units until calibrated. Nearest-defender distance is derivable; it is not the engine's defensive-contest probability. Camera/world-to-screen, skeleton labels, native velocity, fatigue, injury, matchup and substitution queues remain unmapped.
Offensive rebounds, events and race conditions
The recorder caught Udonis Haslem's offensive rebound at Q1 ~3:35. Between game-03 sequences 903 and 904, his OR count and Miami's OR count went 0→1, while the possession byte remained home (0). That answers the concrete question: we can identify an offensive rebound and credit it to a named player from live data.
However, the shot clock had already reset to 24 before that stat increment; at the recorded rebound credit it was about 23.1. A rule implementation that waits for the OR counter to change acts after the original reset decision. This is useful for a provisional event feed, but insufficient for authoritative college/league reset rules.
Other recorded examples include Bosh's defensive rebound, James/Haslem assists, Wade's steal with Leonard's turnover, Bosh's block, Duncan's and-one and made FT, and Ginobili's made three. game-04 sequence 443 records the three, making it 10–10 at 73.8667 seconds; GameTrack independently shows Ginobili's three at 1:13. At sequence 1197 the period changes to 2 while clock is still zero and a final three-point attempt is recorded: reads at transitions need explicit coherence rules.
What polling cannot establish reliably:
- Exact event ordering or a stable event ID; several counters can update between reads.
- Which shot a rebound belongs to, or whether a rebound was a team/dead-ball credit.
- Why a reset occurred: rim contact, rebound, change of possession, foul, violation or period initialization.
- Foul type, fouled player, shooter continuation, goaltending or annulled basket.
- Final authoritative result, abandoned match, correction or replay-only activity.
The FT counter reached zero before the made FT was awarded. The ball object's bound actor also persisted as Chalmers after his made basket when San Antonio was entitled to inbound. Neither “FT count is zero” nor “ball actor pointer exists” is a sufficient semantic event.
The team-context roots also disagree with the possession byte in 338/1,627 samples of game-04, including transition/presentation context. They are useful code inputs, but should not be published as a validated current-possession API.
Live ball, dead ball and game phase
This is the most important unresolved category. Clock flags are real timer state; native users test masks including 6. They are not a complete basketball state machine. A paused game can retain the same timer flags as active gameplay because a parent timer is paused; a free throw can have a stopped clock while the ball is legally live. Replays retain stats and position data. A zero clock occurs during period transitions without establishing game over.
The API should expose separate, nullable concepts:
- Session phase: loading, pregame, gameplay, period break, timeout, replay, paused, final, aborted.
- Legal ball state: live, dead, unknown.
- Ball motion/control: held, dribbling, airborne, loose, out of bounds, unknown.
- Team possession, inbound entitlement, current controller, last touch and last shooter.
- Timer running/paused and the reason for each reset.
Unknown values must remain unknown. Publishing a guessed liveBall: false from a stopped clock would corrupt rules and analytics.
Three-point line: recovered decision code
We recovered the 244-byte predicate at 0x89FF10, its transform helper 0x6DC460, the binding/classification caller 0x8C7C80 and the score-value selection path around 0x8A3771.
The predicate tests four transformed body points, not just the player's center or a shot-distance field. All four must lie outside the boundary. Body/bone names are not established. Direction comes from the current team-context object, allowing both court directions.
| Parameter | Selector zero (observed default) | Selector nonzero |
|---|---|---|
| Arc radius, game units | 723.899963 | 675.0 |
| Corner half-width | 670.559998 | 660.0 |
| Corner transition along oriented Z | 1005.840027 | 1132.935181 |
| Basket oriented Z | 1274.444946 | 1274.444946 |
For each point, let z = direction × point.z. If z exceeds the transition, abs(x) must be greater than the corner half-width. Otherwise, x² + (z − basketZ)² must exceed radius². Equality is inside. Preserve float32 operation order when reproducing boundary behavior.
The caller first queries rule ID 0x15. If enabled and the predicate succeeds, it sets bit 0x400 in ball flags +0x184; otherwise it clears the bit. A downstream path uses the flag to select two-/three-point award values, unless in its FT branch. This is evidence connecting geometry to scoring, not a search hit for a familiar number.
The comparison harness executed the exact captured x86 predicate in Unicorn 2.1.4, replacing only the four-point extraction helper with supplied inputs. All 1,266 cases passed: 66 boundary cases and 1,200 seeded random cases, both directions and selector values 0/1/7. It did not validate the skeleton extraction in a live boundary-shot scenario. We have not changed a rule, identified the alternate selector's UI name, or proven that shooting-foul awards, AI spacing and shot-chart logic all use this predicate.
A complete custom court/ruleset therefore needs aligned geometry, visible court artwork, shot classification, shooting-foul FT counts, AI shot selection/spacing and presentation. Elan's own README reports that its shortened-three modification can still award only two free throws on a shooting foul. One multiplier or rendered line is not the whole rule.
Community sources and useful follow-up targets
Elan 2K Mod, elanjcoop/Elan-2K-Mod, archived commit f77f0faeee55a1c077f52c79d2b4e8e5db0d47f6: main.py, Elan's Engine.CT, Shot Chance Research.CT, README. The 199-entry address inventory preserves original labels and source hashes without blessing them as correct. Its “QuarterLength” at 0x1DEE5F0 is rounded remaining time; configured period length has a different pointer-chain lead. Elan documents overly frequent OR resets and shortened-three foul inconsistencies.
GreenMeter Autostart 1.7.0, user-supplied package, MIT notice “Copyright (c) 2026 Green Meter contributors”: statically extracted and decompiled with ILSpy 11.1.0.9782 / .NET 10.0.12. The extracted executable hash is 95e26d6f18b7d871e264b50340f9c6375f5bf75f4b970ac911cb27cb66d1fcab. Decompiled GameConnection.cs, StubTemplates.cs, EngineProfile.cs, ShotDistance.cs and MeterDifficulty.cs supply traversal, shot-action and guarded-callsite leads. Their use is evidence of that author's implementation, not evidence our whole build matches its fingerprint or that every claim works. The package was not executed.
Five candidate shot/finish CALL sites were independently checked against live code: 0x84291A→0x8CA230, 0x80D42D→0x8CA230, 0x9F0192→0x9EFCD0, 0x82B379→0x8A8DF0, 0x82B387→0x8A9430. Verify exact targets from the machine-readable validation before writing any hook. These are decision/finish leads, not final basket-credit events. Action-object offsets for release/ideal release, subtypes and timing windows remain candidates; CPU controllers have index −1 in the captured match, whereas the community meter filters user-controlled players.
Blaze framework 0.5.1 SDK exposes generic memory/plugin services. It does not itself supply the semantic live-game API we need. Sigil provides useful roster/stat vocabulary and disk structures, which must not be confused with live actor layouts. Older NLSC cheat tables include MyCareer globals, stale heap addresses and slider caveats; the Stats Tracker is not proof of automatic live extraction.
Native stats callback lead 0x883E00 iterates callbacks in the 0x1DE1D3C table; 0x884990 registers up to three. Nearby stat-update functions are promising, but callback ownership, calling convention, ordering and coverage remain unverified. Do not register blindly or call this a universal gameplay event bus.
GameTrack backing records are a high-priority next target. The captured screen independently shows period, scorer, game time, layup/jumpshot/FT classification, two-/three-point classification and resulting score. Recovering that retained history may provide reconciliation after a missed snapshot or companion reconnect. Its memory layout, retention length and non-scoring-event coverage are not known yet.
Recommended external API design
Use a small, version-guarded x86 adapter at the original game boundary and a portable companion for storage, modes, analytics and presentation. Do not embed HTTP server work or database writes in the simulation/render thread. Publish coherent data to a bounded queue/shared buffer; let the companion serve a local API. This can support Windows and compatibility-based Mac/Linux reference runs while keeping replacement logic portable for future native engine work.
Three contracts should stay separate:
- Snapshots: match instance, roster mapping, period/clocks, score, stat totals, on-court lineup, positions and phase. Each has sequence/time, executable/adapter version, validity and field capability status. Cached/unknown fields are explicit. Snapshots recover current state after event loss.
- Events: ordered sequence, event type, period/simulation time, players/teams, shot/possession linkage and score/clock before/after. Surface overflow/gaps; support reconciliation and corrections. Never claim exactly-once delivery merely because polling is frequent.
- Rules metadata and commands: versioned league settings, court geometry and supported enforcement features. Apply mutations at known safe simulation boundaries with validation; acknowledge the applied state. Reading a timer is separate from safely changing a timer limit or reset policy.
External IDs belong to our match/roster mapping, not names, cyberface IDs or heap addresses. A custom roster can reuse names/appearance assets. A new match/restart/load must create an epoch so old pointers and counter decreases do not masquerade as events. Completion should be an explicit, verified final-results boundary before committing a dynasty/career result.
This foundation can feed custom scoreboards, stat cards, play-by-play, shot charts, lineup overlays and external mode results. Derived metrics can include shooting efficiency, scoring runs, lineup plus/minus, spacing, shot distance and defender proximity. More ambitious touches, pass networks, screen assists, deflections and rebound chances require additional event/position semantics and declared definitions. An estimated possession formula is not an actual possession counter; a nearest defender is not necessarily the shot's assigned contesting defender.
Reproduction and next milestone
The saved scripts capture-game-01..04.ps1 and paused/proof scripts are the exact probe variants used. Receipts record process, EXE/adapter hashes, UTC bounds, sample count, size and SHA-256. The four long captures total 8,670 samples across 960 seconds of wall time; they overlap/contain pauses and are not 16 minutes of continuous game time. Latest probes requested 100 ms sampling and achieved roughly nine samples/second. Reads are sequential and lack a simulation-frame lock.
From the project root:
python3 tools/analyze_live_data.py analysis/live-data/game-04.jsonl --output analysis/live-data/game-04-decoded.jsonl
.venv/bin/python analysis/live-data/verify_three_point.py
python3 analysis/live-data/validate_research.py
python3 analysis/live-data/build_catalog.py
python3 tools/index_openhoops.py
verify_three_point.py uses the project .venv and its installed Unicorn 2.1.4; system python3 lacks that package. Broad linear disassembly and xref snippets include unaligned starts/data and are discovery aids; the exact bounded functions and live byte comparisons carry the stronger claims. Raw decoded fields retaining _candidate labels are intentionally conservative. Early partial/decode files are superseded by canonical captures and should not be treated as final schema evidence.
The next useful implementation is a read-only named snapshot feed using this verified core, accompanied by targeted recovery of GameTrack storage, match phase and final stat-credit boundaries. Then validate a whole match, replay, timeout, free throws, substitution, period break, restart, reconnect and final-result commit. Only after that should external career/dynasty results be treated as reliable. For custom college rules, recover the authoritative reset/foul/period decisions before exposing write commands.
The reference game is left paused for follow-up. No recurring automation was enabled. Full native reconstruction remains a separate, unfinished goal; this research establishes a practical extension boundary without claiming that the original executable is now portable source code.