Compare commits

2 Commits

Author SHA1 Message Date
MrARM
8c6547acbd J Sync 7/21/26 2026-07-21 10:45:59 -05:00
MrARM
32751448c6 Document VPX emulator status and recovery plan 2026-07-14 23:10:10 -05:00
11 changed files with 1232 additions and 23 deletions

View File

@@ -26,7 +26,10 @@ Static triage of the extracted Raspberry Pi based root filesystem in this worksp
- [Analysis Evidence Map](analysis-evidence-map.md) - compact map from `analysis/` datasets to findings, confidence, and follow-up work. - [Analysis Evidence Map](analysis-evidence-map.md) - compact map from `analysis/` datasets to findings, confidence, and follow-up work.
- [Artifact Index](artifact-index.md) - hashes and generated artifact inventory. - [Artifact Index](artifact-index.md) - hashes and generated artifact inventory.
- [Next Analysis Targets](next-analysis-targets.md) - prioritized open work. - [Next Analysis Targets](next-analysis-targets.md) - prioritized open work.
- [Docker/QEMU Emulation](docker-qemu-emulation.md) - what currently boots, what is visible, and why the game is not fully playable. - [Docker/QEMU Emulation](docker-qemu-emulation.md) - Docker/VNC analysis path plus the verified native HVF/Cocoa game-display baseline.
- [Process-Level Game Emulation](process-level-emulation.md) - userspace capsule, launcher, software cabinet bridge, debugging, and validation boundary.
- [VPX Table Recreation](vpx-table-recreation.md) - clean-room Pokemon Pro table sources, evidence basis, build/runtime validation, and remaining physical-accuracy boundary.
- [Native macOS Playable Table](native-playable-table.md) - QEMU rules/display/audio joined to VPX physics through the supervised playfield bridge.
## System and Software ## System and Software
@@ -41,10 +44,14 @@ Static triage of the extracted Raspberry Pi based root filesystem in this worksp
- [Main Game Behavior Notes](main-game-behavior.md) - [Main Game Behavior Notes](main-game-behavior.md)
- [Conagent Reverse Engineering](conagent-re.md) - [Conagent Reverse Engineering](conagent-re.md)
- [QR Code Analysis](qr-code-analysis.md)
- [Conagent Machine Emulator](conagent-machine-emulator.md) - [Conagent Machine Emulator](conagent-machine-emulator.md)
- [Netbridge Reverse Engineering and Emulator](netbridge-re.md) - [Netbridge Reverse Engineering and Emulator](netbridge-re.md)
- [Magikarp Topper Serial Key and Emulator](topper-serial-key-emulator.md) - [Magikarp Topper Serial Key and Emulator](topper-serial-key-emulator.md)
- [Docker/QEMU Emulation](docker-qemu-emulation.md) - [Docker/QEMU Emulation](docker-qemu-emulation.md)
- [Process-Level Game Emulation](process-level-emulation.md)
- [VPX Table Recreation](vpx-table-recreation.md)
- [Native macOS Playable Table](native-playable-table.md)
## Assets and Radium Format ## Assets and Radium Format

View File

@@ -1,5 +1,128 @@
# Artifact Index # Artifact Index
## Target Firmware Evidence
These files are unmodified target-root artifacts used for static protocol
recovery.
| SHA-256 | Target path | Notes |
| --- | --- | --- |
| `f8eb96998a32ff86008663f7f07f2df76c32a9c86200f690c215dc36917391a9` | `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex` | 32,799-byte Intel HEX image; command `0x15` poll and unsolicited sequence-0 cabinet-input report paths are documented in `netbridge-re.md`. |
## Process Emulator Source Artifacts
These are clean-room source files. Their generated capsule, run logs, copied
Mesa runtime, framebuffers, audio, and traced packs live under
`emulation/work/` and are intentionally excluded from Git.
| Path | Notes |
| --- | --- |
| `spike3-emu` | Unified `prepare`, `run`, `debug`, `trace`, `shell`, `pack`, and safe legacy-clean entry point. |
| `emulation/process/spike3_emu.py` | Read-only process capsule builder/runner, Docker/native/qemu-user selection, display/audio sidecars, trace packer, and GDB helper generator. |
| `emulation/process/config.default.json` | Versioned software-only runtime defaults. |
| `emulation/process/Dockerfile` | Host-architecture runtime with ARM64 Mesa libraries, tracing/debug tools, Xvfb, x11vnc, and noVNC. |
| `emulation/process/test_spike3_emu.py` | Launcher, isolation, preload-order, framebuffer-loop, trace, and debugger unit tests. |
| `emulation/stubs/spike3machine_client.c` | AArch64 preload bridge from target SPI/I2C/GPIO calls to the versioned machine broker. |
| `emulation/stubs/spike3emu_display_contract.h` | Shared DMA-BUF attribute and software-decoder contract helpers for the display shim tests. |
| `emulation/tests/spike3emu_display_contract_test.c` | Host/AArch64 contract tests for DMA-BUF attribute parsing and the V4L2-to-software decoder alias. |
| `emulation/tests/mesa_fbo_smoke.c` | Standalone surfaceless Mesa GLES test requiring a complete texture-backed FBO, nonzero readback, and no GL error; source compilation is verified, ARM64 runtime execution remains pending. |
| `analysis/spike-machine-emulator/` | Versioned broker, persistent EEPROM/amplifier models, SPI/GPIO/UART/nodebus cabinet model, replay/fault support, client, and tests. |
| `analysis/spike-machine-emulator/profiles/pokemon-pro.json` | Configurable provisional Pokémon switch/output mapping. |
| `emulation/vpx-plugin/` | Pinned ARM64 macOS VPX ControllerPlugin source, package builder, configuration, and table-side VBS adapter. |
Generated capsule manifests record hashes for `/games/pokemon_pro/game`, both
Radium `image.bin` files, Godot `main.pck`, the menu/display executables, the
extracted loader, both generated preload DSOs, and the copied Mesa overlay.
## VPX Clean Rebuild and Historical Recreation Artifacts
The active clean-room implementation is under `emulation/vpx-rebuild/`. Its
maintained and generated artifacts are:
| Path | Artifact role | Validation and handling caveat |
| --- | --- | --- |
| `emulation/playfield-contract/pokemon-pro-hardware.json` | Canonical geometry-free contract for 45 switches, 11 sensors, 9 cabinet controls, 13 drivers, and 113 logical lamps. | Static contract/consumer validation is recorded in `emulation/playfield-contract/VALIDATION.md`; it is not live-game proof. |
| `emulation/vpx-rebuild/design/geometry.json` | Clean-room Pro XY/Z, mechanism, collision, and physics source of truth. | Top-down placement is registered to official evidence; installed Z, force, friction, clearance, and timing values still need physical-machine measurement. |
| `emulation/vpx-rebuild/design/lights.json` | Independent 113-lamp trace and confidence records. | Exact-address mapping is statically validated; rendered response still needs final integrated capture. |
| `emulation/vpx-rebuild/design/REFERENCE.md` | Edition scope, official/secondary source URLs, and accuracy boundary. | Official Stern evidence wins over secondary sources. |
| `emulation/vpx-rebuild/assets/playfield/pokemon-pro-official-registered.png` | Deterministically registered 952x2115 private-use base image. | Official photograph contains baked-in hardware/lighting; not a licensed bare-playfield scan and not redistributable by this assessment. |
| `emulation/vpx-rebuild/assets/models/` | Original procedural VPX mechanism and toy meshes. | Simulation models, not production CAD or direct physical scans. |
| `emulation/vpx-rebuild/assets/sounds/` | Original procedural CC0 bank containing 69 mechanical/physics source events. | Contains no copied game samples; QEMU remains the source of licensed music, speech, and game effects. |
| `emulation/vpx-rebuild/script/PokemonPro_Rebuild.vbs.in` | Clean standalone/SPIKE3 table behavior and integration template. | Maintained input; generated `source/script.vbs` and distribution sidecar must not be hand-edited. |
| `emulation/vpx-rebuild/tools/generate_table.py` | Deterministic unpacked-VPX source generator and contract binder. | Generator/source checks do not establish live gameplay. |
| `emulation/vpx-rebuild/source/` | Generated diff-friendly VPX source. | Rebuilt from maintained inputs; do not hand-edit. |
| `emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx` | Active private-use VPX 10.8.1 assembled table selected by the default launcher; 5,902,336 bytes, SHA-256 `ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`. | Generated output; exact current hash also appears in `qa/rebuild-validation.json` and `qa/runtime-smoke.log`. |
| `emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vbs` | Sidecar matching the active assembled table. | Generated output; round-trip script equality is checked by `build.sh`. |
| `emulation/vpx-rebuild/qa/rebuild-validation.json` | Machine-readable current 359-item build, asset, contract, geometry, lamp, and round-trip report. | A `pass` is static/generated-source evidence, not physical-machine calibration or a complete game. |
| `emulation/vpx-rebuild/qa/runtime-smoke.log` | Exact-current-table VPX 10.8.1 runtime-load record: table/VBS load, script compile/start, Metal initialization, two frames, clean close, and exit code 0. | Uses bounded `CaptureAttract`, not interactive `-Play`; it proves runtime/render startup but not a shot or full mechanism sequence. |
| `emulation/vpx-rebuild/qa/vpx-playfield-capture.png` | 1024x1024 frame from the exact-current-table runtime smoke; SHA-256 `5c99e798e467c8475b2c1aa48a4134881d9293d915aafde4e998ed6ebea5871c`. | Render-review evidence; photographed base art still contains baked-in hardware and lighting. |
The registered photograph, service manual, Pokemon/Stern imagery, and assembled
table must remain within the authorized private-use boundary. The table does
not embed target machine code or the game's scoring rules. See
`vpx-table-recreation.md` for the separated static, VPX-runtime, integrated,
and hardware validation states.
The following SHA-256 table is the preserved earlier VPX/bridge milestone.
Entries under `emulation/vpx/` describe the deprecated greybox/manual workflow;
they are retained for audit and are not inputs or defaults for the clean
rebuild. Shared plugin, launcher, guest-helper, and emulator paths may still be
active, but the hashes below are milestone hashes rather than an assertion
that every shared file is unchanged today.
| SHA-256 | Path | Notes |
| --- | --- | --- |
| `fd64431a28fb08a880acff091dc752301b785a2c93fe3985f9fb316db01026a0` | `emulation/vpx/PokemonPro_Greybox.vpx` | Deprecated historical VPX 10.8.1 greybox milestone hash; the current working copy may differ. The clean rebuild does not inspect or write it, and it is not an input or runtime default. |
| `652b59cbfa3d38bae9194e096a9a15d6e9e7ea73b077707aced2827a774b8a84` | `emulation/vpx/layout.json` | Historical greybox geometry/mapping manifest; superseded for active electrical mapping by `emulation/playfield-contract/pokemon-pro-hardware.json`. |
| `0e5047593cc1b4f6e53fb9324165c08adf8b1c6d1c7ad430281fd581c1278eeb` | `emulation/vpx-plugin/tests/test_bridge.cpp` | VPX bridge protocol fixture with the live-verified Service Back, Up, Down, and Enter CPU-SPI addresses; exercised by the plugin mapping, reconnect, and 10,000-pulse tests. |
| `18028e0065d4260799264ec255999a8e4ced4c7e4213484379752d360e1f4768` | `emulation/vpx/PokemonPro_Greybox.vbs` | Deprecated historical greybox controller/QA sidecar; not the active clean-table script. |
| `127c653b08aa275afd86141bef1ad7d44a48525f034d1bdd1a0188671d61bd04` | `emulation/vpx/manual/PokemonPro_Manual.vpx` | Historical editor-owned derivative of the greybox workflow; not the active clean-table source of truth. |
| `560f8108ed1224945a5597f8d960e266fea5feea74f813a17b3a6a7ea2233ad8` | `emulation/vpx/prepare-manual-table.sh` | Creates the protected manual table, refreshes only its generated controller sidecar, and backs up before an explicit reset. |
| `1c9826c3c302b24d66ccce780eb65b75ead20be59dcd062667d921e9effb6f11` | `emulation/vpx/edit-manual-macos.sh` | Opens the manual table in standalone VPX with an isolated preferences path and prints Live Editor save guidance. |
| `ebb09dfb3b708f2b1e363c49f59ca78b7e64067ab50a9d598b4d2d5c07dfa972` | `emulation/vpx/test-manual-macos.sh` | Fast standalone physics launcher for the editor-owned table. |
| `95819aec421da92aa2a6305c0b5e9bebb56ad18f86400212e85d6b42badaedc7` | `emulation/scripts/run-playable-manual-macos.sh` | Full native QEMU/HVF and VPX integration launcher using the editor-owned table. |
| `2777d9a83a42a2ef076e6956bd8c15bfc54e1cff99eddbd012385fd5db90db5b` | `emulation/scripts/run-playable-macos.sh` | Native cabinet supervisor with matching VPX/QEMU-focus control instructions and automatic generated-rootfs guest-helper refresh. |
| `b7a864e250907952e83239a6b3dd69923bca86fed0816a3c5a99a026b92edcf5` | `emulation/scripts/run-macos.sh` | Native QEMU/HVF supervisor with playfield bridge and explicit optional/required conagent backend collision handling. |
| `d08a4f577ed481371d16f734e360553ea19f49c8afab83d2c4d528af51abc270` | `emulation/guest-tools/keyboard-switch-relay.c` | AArch64 guest relay using the cabinet map when QEMU Cocoa has focus: Shift flippers, `1` Start, `5` coin, Return/Space Action, and `7`/`8`/`9`/`0` service controls. Installed binary SHA-256 `55054003950873900b5ce203292846f158b861060dbdd2e04b5be4a8afa1869c` was verified from the rebuilt ext4 image. |
| `732437382c6bc88df080e3a9c1cf66803eb35b1e6f736a76896ab35d8f48c0e2` | `emulation/vpx/tools/generate_layout.py` | Deterministic hardware, geometry, physics, asset-registration, live-corrected service CPU-SPI, and power-distribution route manifest generator. |
| `cedb8d18f491d06c1dde2b5aa2fdee9f4786001ac0be029e672d25204e97f709` | `emulation/vpx/tools/generate_table.py` | Rounded collision paths, layered VPX source, controller integration, QA, and exact cabinet-route validation. |
| `836e26f693990680688780befa5d826c37d656d827f9448f68ececcede777230` | `emulation/VPX_EMULATOR_STATUS.md` | Current implementation inventory, repaired CPU-SPI/powerdist input paths, persistent-provisioning stop point, manual geometry workflow, and acceptance checklist for branch `vpx-emulator`. |
| `e13eb92e99a806ea86625de8cc426876929625190f763bfe93cd2e821efd3f88` | `emulation/vpx/tools/verify_collision_topology.py` | Static assertions for continuous collision/visual rail inventory and runtime/editor flags. |
| `829c929b4ed51d2d7544cfae1aeb0b8571b50e1d71de299bcfdc0535844372e1` | `emulation/vpx/qa/collision-topology.json` | Per-path polygon area, winding, vertex, and self-intersection report. |
| `e3ed8627e92c9175791c6aad0a2868df7dd66ac53602663431c6cd0c7a98a52a` | `emulation/vpx/qa/collision-validation.json` | Passing 13-collision/13-visual topology summary with zero legacy segments and intersections. |
| `526e2a4e5695496e6662fe44ddc8406b700a551b142a6da95bd74bb8adc8e00f` | `emulation/vpx/qa-mechanisms.sh` | Live VPX assertions for both outlanes, center drain, Town VUK, Poke Ball hold, and shooter serve. |
| `32b0c8fa4f5330cd9051d456c7209575bbd764f460c9e655d48ffcc89b23ddc1` | `emulation/vpx/qa-plunger.sh` | Full-pull VPX assertion requiring the served ball to clear the shooter mouth into the main playfield. |
| `d66b99d4029b08906549e44b2c0f7ab26153313d2eea1d168e824e4fe75a705e` | `emulation/vpx/layer-map.json` | Stable 16-layer VPX construction/collision/visual/electrical/QA editor map. |
| `254eb1167d1355003fe28fa1becc1209ff7fb1f53544cf696295b546c5c49e0e` | `emulation/vpx/assets/models/star_post.obj` | Blender-authored normalized ten-lobed post mesh. |
| `0c2adad60a3c0b3491f371630013106d18c3327dd2173b2bf238e23705e1c25e` | `emulation/vpx/assets/models/stepped_metal_post.obj` | Blender-authored normalized stepped guide-post mesh. |
| `d58c3f812fa0ba98461abf0220692a72ba3dfaec52b0fcaee0ca3d02ef70bc84` | `emulation/vpx/assets/models/pop_bumper_cap.obj` | Blender-authored normalized pop-bumper cap mesh. |
| `976a2c72f13a654541e8d558b69c2dd6974e33a3f787b5926d98067e34d89f8d` | `emulation/vpx/assets/cad-reference/Star_Post_PL-00172-OT.SLDPRT` | Unmodified Pinball Makers private-development star-post reference. |
| `bb313ef15bf3bcf25f6faa1e3d6a40e0a8d683b828b32572aaaf9752e1697326` | `emulation/vpx/assets/cad-reference/Post_Faceted_1_inch_C-11561-20.SLDPRT` | Unmodified Pinball Makers private-development faceted-post reference. |
| `cb78b416be8f3c946313517766ecd3077dd05fdecea8f6ec162f9c9b0d4d3413` | `emulation/vpx/assets/cad-reference/WPC_Pop_Bumper_Assy_B-9414.zip` | Unmodified Pinball Makers WPC pop-bumper CAD assembly reference. |
| `50a1906b5c609cd3a84970b6a6e9b0be839b4d0a588d8b27361936ab6537d6bb` | `emulation/vpx/reference/Pokemon_Pro_web.pdf` | Preserved 10,227,248-byte service manual. |
| `09477fe9bd50cfeba34bbff2eac5c272fc79eed6a6ed07fc40da636c5fde977b` | `emulation/vpx/reference/pokemon_playfield_pro.png` | Preserved 451x998 target-root playfield locator. |
| `b2804f794eb119712420ad575479fb8febcc39df0e505f9942c162614819819c` | `emulation/vpx/reference/photos/pokemon-pro-overhead.png` | Preserved user-supplied overhead machine photograph. |
| `6ba469672627125d13798d1e80d460f85a5ce4ab9d4798c9947715ebbe1cfe70` | `emulation/vpx/reference/photos/pokemon-pro-lower-detail.png` | Preserved user-supplied lower-playfield detail photograph. |
| `1a1af7fa16dde9c84deb2fbc19de44b9c860cd5ef4dc3a65d48a3cbfdbb214a3` | `emulation/vpx/reference/photos/pokemon-pro-player-view.png` | Preserved user-supplied player-view photograph. |
| `acb797ec6c2a03079c1a427e0350de0dfabb0c33688eb45e60b52824c04317eb` | `emulation/vpx/reference/photos/pokemon-pro-overhead-registered.png` | Reproducible 451x998 overhead-photo crop for locator comparison. |
| `5383b79d5a955b91059ca5d67806d719d89655ec293e4bd0cc84b8a5adede03f` | `emulation/vpx/qa/layout-overlay.png` | Actual buffered collision polygons, centerlines, ramps, mechanisms, and hardware over the locator. |
| `595faa3cc12de55ad10ef3324593140473a3eb3e4778471797845c39a31ae243` | `emulation/vpx/qa/photo-geometry-overlay.png` | The same buffered collision review over the registered overhead photograph. |
| `8084af0ed94f87c20487f0ba02a182015365a3c51c00d0358d679e4e5267d62f` | `emulation/vpx/qa/vpx-playfield-capture.png` | Inspected live desktop render of continuous rounded visual rails and initialized mechanisms. |
| `3351926b55d81ea7c77e3df016aa05167dbf786e3a1985537770a1d95da309b7` | `emulation/vpx/qa/vpx-cabinet-capture.png` | Inspected live fullscreen render rotated into physical portrait orientation. |
| `6b1a65cbbf9569fd720075f60ee3687b99fdf439313fc85bb887231afd44dfbe` | `emulation/vpx/qa/mechanism-runtime.log` | Passing continuous-collision evidence for both outlanes, three drains, both kickers, and shooter service. |
| `06cf7cd0b2a61579ec7bce7637565d1bccd9dcd803a18e0053cce94859005f3b` | `emulation/vpx/qa/plunger-runtime.log` | Passing continuous-shooter-rail main-playfield-entry evidence. |
Key source hashes for the 2026-07-11 process/VPX milestone:
| SHA-256 | Path |
| --- | --- |
| `502868bdf619ef66a2af612f1d70ac5c7d8811e8999a8b55039c10aff7d56f40` | `emulation/process/spike3_emu.py` |
| `31ca5928aefb1d17ec4153a35ec01f87cb84bf5e8e3059b82efd37bd1ae7f2ea` | `analysis/spike-machine-emulator/service.py` |
| `a6065c0b13beca83fb9c1a64702ba163b5e1664cdfb3bb64985a4b6b326cf91e` | `analysis/spike-machine-emulator/machine.py` |
| `57be1144bafd4dfe3ba852e36ba015487b548df1229dcb2869884abd67bf6101` | `emulation/dashboard/server.py` |
| `1da09b43b43e920f8f95183281cf3c3291f7d013a0da8e6ac60cc0f776b6dbee` | `emulation/vpx-plugin/Spike3Controller.cpp` |
| `5c8bb043378669334b587d0e443b7a6ded5530c886d87f9bd1e30fb7fe7cb12c` | `emulation/tests/mesa_fbo_smoke.c` |
## Main Game Artifacts ## Main Game Artifacts
| SHA-256 | Path | | SHA-256 | Path |
@@ -104,9 +227,9 @@ These files are workspace-local generated artifacts, not target-root files.
| `b5ddb933f125cf6b54b81c56f4e3430ded50d843c045a49f0727a1f7ebc5f66f` | `rootfs-triage-wiki/scripts/harvest_loose_openable_assets.py` | Static loose-asset copier for PNG, TTF, MP4, and MOV files. | | `b5ddb933f125cf6b54b81c56f4e3430ded50d843c045a49f0727a1f7ebc5f66f` | `rootfs-triage-wiki/scripts/harvest_loose_openable_assets.py` | Static loose-asset copier for PNG, TTF, MP4, and MOV files. |
| `257cb2d9ba653385aba63157a3bc143bb479c5188071ef529996d3f5da258b71` | `analysis/topper-emulator/topper_emulator.py` | MicroPython SPIKE node-board emulator for the Magikarp topper serial-key path; 6,452 bytes. | | `257cb2d9ba653385aba63157a3bc143bb479c5188071ef529996d3f5da258b71` | `analysis/topper-emulator/topper_emulator.py` | MicroPython SPIKE node-board emulator for the Magikarp topper serial-key path; 6,452 bytes. |
| `32263b75f49aefba04596c196f85cc4ae4477eb536f767fcdeac7c0d5fc40e9b` | `analysis/topper-emulator/README.md` | Emulator notes and recovered command summary; 5,820 bytes. | | `32263b75f49aefba04596c196f85cc4ae4477eb536f767fcdeac7c0d5fc40e9b` | `analysis/topper-emulator/README.md` | Emulator notes and recovered command summary; 5,820 bytes. |
| `5b510902f4027e364702a61338ff613bde2b77d8441ba2703b343e642cac0453` | `analysis/netbridge-emulator/netbridge_emu.py` | Python host-side emulator for the RP2040 netbridge UART protocol, bridge commands, CRC response, PTY/socket serving, and a stub nodebus device. | | `75c5c40ec0835140c3ee26474144777e69eb0947f57d428b72000470f0a77aaa` | `analysis/netbridge-emulator/netbridge_emu.py` | Live netbridge and `spike3-playfield/v1` service with manifest cabinet aliases, CPU-SPI routing, polled and unsolicited powerdist input reports, ordered output, and safe QEMU synchronization. |
| `542b9835e6b6d8107d68cc6d62bfdfe85995fe61029c36ffcdba3e0295f6d455` | `analysis/netbridge-emulator/test_netbridge_emu.py` | Unit tests for host frame escaping/checksum, version response, firmware CRC response, and node serial-key command. | | `7fcbb6b3752ddf7212d996660871d4b96b3f83666266e82fb027e4478bd46769` | `analysis/netbridge-emulator/test_netbridge_emu.py` | Sixty-eight protocol, routing, active-low wire-state, unsolicited UART, event-pump, output, synchronization, and recovery tests. |
| `b7a05a1f4d3cffc7d163e26afed6fa5402ee1a94df1687447dc1aefe067e03cd` | `analysis/netbridge-emulator/README.md` | Runbook and porting notes for the host-side netbridge emulator. | | `e4e8828d0a0bcf7c533f67d03d69e83fd9bab93278d6ac8f084a2699b12be8f0` | `analysis/netbridge-emulator/README.md` | Runbook and porting notes for the host-side netbridge emulator. |
| `a27f5e00f09a014391418f6822dc6c5481c224ef0cf42786b8b290ecd508d9c3` | `analysis/netbridge-pico-firmware/CMakeLists.txt` | Pico SDK and portable host-test build file for the RP2040 netbridge firmware recreation. | | `a27f5e00f09a014391418f6822dc6c5481c224ef0cf42786b8b290ecd508d9c3` | `analysis/netbridge-pico-firmware/CMakeLists.txt` | Pico SDK and portable host-test build file for the RP2040 netbridge firmware recreation. |
| `d01867dc66873799b966cf24b7574d9e389277f1556d88b92fd61342ea760222` | `analysis/netbridge-pico-firmware/include/netbridge_protocol.h` | Shared host-frame protocol and model API for the Pico firmware recreation. | | `d01867dc66873799b966cf24b7574d9e389277f1556d88b92fd61342ea760222` | `analysis/netbridge-pico-firmware/include/netbridge_protocol.h` | Shared host-frame protocol and model API for the Pico firmware recreation. |
| `918b434ad6c8b4a0455b4eea0bd7ed8c341fb4e709bc6012c0aa43b4c074601e` | `analysis/netbridge-pico-firmware/src/netbridge_protocol.c` | Portable STX/ETX/ESC frame encoder/decoder for the Pico firmware recreation. | | `918b434ad6c8b4a0455b4eea0bd7ed8c341fb4e709bc6012c0aa43b4c074601e` | `analysis/netbridge-pico-firmware/src/netbridge_protocol.c` | Portable STX/ETX/ESC frame encoder/decoder for the Pico firmware recreation. |
@@ -137,7 +260,7 @@ These files are workspace-local generated support artifacts, not target-root evi
| `61f62ec692e91cddcd1dd81d7edb32aaeb7951f80c82f64b472fa54f9009e217` | `emulation/README.md` | User-facing emulator runbook, optional netbridge mode, and current limitations. | | `61f62ec692e91cddcd1dd81d7edb32aaeb7951f80c82f64b472fa54f9009e217` | `emulation/README.md` | User-facing emulator runbook, optional netbridge mode, and current limitations. |
| `239ddcc7b6928582df66273cd557bf939dbc14f792f554c6ee2e6d4844ac00ca` | `rootfs-triage-wiki/docker-qemu-emulation.md` | Wiki page documenting implementation, verified progress, optional netbridge wiring, and the current display and hardware blockers. | | `239ddcc7b6928582df66273cd557bf939dbc14f792f554c6ee2e6d4844ac00ca` | `rootfs-triage-wiki/docker-qemu-emulation.md` | Wiki page documenting implementation, verified progress, optional netbridge wiring, and the current display and hardware blockers. |
| `62eddb1582076ca9e07b3910e71673e7d15e4cd3599d73ce51e3a4148e5b95ad` | `rootfs-triage-wiki/analysis-evidence-map.md` | Cross-reference from durable `analysis/` outputs to wiki findings, confidence boundaries, and open enrichment work. | | `62eddb1582076ca9e07b3910e71673e7d15e4cd3599d73ce51e3a4148e5b95ad` | `rootfs-triage-wiki/analysis-evidence-map.md` | Cross-reference from durable `analysis/` outputs to wiki findings, confidence boundaries, and open enrichment work. |
| `ac568dc027509f7c30475f444490771cdfa37e2510deeb32cda29367ee5ec78a` | `rootfs-triage-wiki/netbridge-re.md` | Wiki page documenting Ghidra-backed netbridge protocol recovery, emulator use, Docker wiring, and Pico firmware recreation. | | `3ec9bb35a9fc74f992f84fc71405b5dda9c8c096312263902dfc850597ff8a98` | `rootfs-triage-wiki/netbridge-re.md` | Wiki page documenting Ghidra-backed netbridge and power-distribution protocol recovery, emulator use, Docker wiring, and Pico firmware recreation. |
## Generated Static Triage Artifacts ## Generated Static Triage Artifacts

View File

@@ -17,7 +17,18 @@ Static-analysis boundary: this page documents the emulator scaffold and evidence
## Current Status ## Current Status
The emulator is a working analysis scaffold, not yet a playable machine emulator. It boots the generated AArch64 guest, exposes logs and a browser-visible framebuffer, and runs far enough to identify deterministic display and hardware dependencies. It does not yet produce a real game frame or model the complete SPIKE hardware environment. This page primarily documents the older Docker/VNC analysis scaffold. That
path still provides a useful browser framebuffer and diagnostics, but it is not
the gameplay path. The current playable implementation runs QEMU natively with
HVF, Cocoa, and CoreAudio and joins it to a native VPX table; see
`native-playable-table.md`.
The native path now produces a genuine 1920x1080 Pokémon game frame with the
llvmpipe compatibility stack. The generated guest and netbridge are connected
end to end: VPX contacts enter the same node model consumed by the real game,
and real lamp/driver writes return to VPX through the supervised playfield
protocol. This does not make the emulator an exact electrical model of every
SPIKE board.
| Layer | Status | Evidence / remaining issue | | Layer | Status | Evidence / remaining issue |
| --- | --- | --- | | --- | --- | --- |
@@ -25,17 +36,24 @@ The emulator is a working analysis scaffold, not yet a playable machine emulator
| Browser access | Working | noVNC and the dashboard respond, and `capture-vnc.py` records nonblack 1360x768 frames. | | Browser access | Working | noVNC and the dashboard respond, and `capture-vnc.py` records nonblack 1360x768 frames. |
| DRM device discovery | Working | `virtio_gpu` creates `/dev/dri/card0`, `/dev/dri/renderD128`, and `fb0`; the guest provides the target-compatible `card1` alias. | | DRM device discovery | Working | `virtio_gpu` creates `/dev/dri/card0`, `/dev/dri/renderD128`, and `fb0`; the guest provides the target-compatible `card1` alias. |
| Mesa EGL/GLES initialization | Working with compatibility overlay | The native probe reaches EGL and OpenGL ES 3.1 with Mesa softpipe; fbdev mode reaches a real surfaceless display and pbuffer. | | Mesa EGL/GLES initialization | Working with compatibility overlay | The native probe reaches EGL and OpenGL ES 3.1 with Mesa softpipe; fbdev mode reaches a real surfaceless display and pbuffer. |
| Target display output | Blocked | The game-created EGLImage/FBO remains incomplete. Mesa reports `GL_INVALID_FRAMEBUFFER_OPERATION`, so the captured image is the shim's fallback bands rather than a rendered game frame. | | Native target display | Working software-rendered baseline | HVF/Cocoa plus Debian llvmpipe produced a complete 1920x1080 game frame. This is CPU rendering, not Apple-GPU acceleration. |
| Audio startup | Partially bypassed | A null ALSA default clears the earlier missing-PCM abort; real amplifier behavior and audio output are not emulated. | | Native audio | Working baseline | QEMU HDA/CoreAudio carries the real game audio; VPX supplies only mechanical table sounds. |
| Netbridge/nodebus | Scaffolded, not end-to-end verified | A Python netbridge model and optional `/dev/ttyAMA5` wiring exist, but the full guest boot with that mode has not been validated and OpenOCD programming may block before UART exchange. | | Netbridge/nodebus | Working playable subset | The real game consumes VPX cabinet/playfield contacts and publishes mapped lamp/driver output. The model is not a complete electrical reproduction of every node. |
| Switch dashboard | Logging only | Events reach the guest log but are not translated into SPIKE nodebus state. | | Switch dashboard | Diagnostic only | The browser remains optional; native VPX is the authoritative gameplay source when `physics_authority=vpx`. |
| Video decode | Blocked after display setup | HEVC/V4L2 initialization still expects target hardware capabilities not supplied by the current QEMU machine. | | Video decode | Working compatibility path | The host VideoToolbox bridge is preferred on macOS, with the existing guest software decoder retained as fallback. |
### Why It Does Not Fully Work Yet ### Remaining fidelity boundary
The extracted software assumes a Raspberry Pi graphics and I/O stack, while QEMU exposes a generic `virtio-gpu` machine. The compatibility layer can satisfy discovery calls and create a real Mesa context, but it cannot yet reproduce the exact GBM/EGLImage-to-framebuffer path used by the game. That is the immediate reason no genuine game frame reaches noVNC. The extracted software assumes Raspberry Pi graphics and I/O while QEMU
exposes generic VirtIO devices. The working native baseline therefore retains
a narrow compatibility overlay and CPU renderer. Docker/VNC can still hit the
historical EGLImage/FBO fallback path even though Cocoa displays the real game.
After that display path is fixed, the game still expects several independent machine subsystems: OpenOCD-managed RP2040 netbridge firmware, SPIKE nodebus devices, TAS/MAX amplifier register behavior, physical switches/lamps/coils, and V4L2 HEVC decode. Those are separate emulation targets; success in one does not imply that the others are modeled. Several machine subsystems remain behavioral models rather than electrical
replicas: the RP2040 netbridge, SPIKE nodes, TAS/MAX amplifiers, cabinet buses,
and video decode devices. VPX supplies the missing physical playfield, but its
ramp elevations, forces, and materials remain evidence-tagged estimates where
the manual has no direct measurement.
## Evidence Basis ## Evidence Basis
@@ -48,7 +66,9 @@ Confirmed string/import evidence:
- `/games/pokemon_pro/spike3/spike_menu/game` has matching GBM/EGL/DRM strings at offsets including `0x382cd0` (`/sys/class/drm`), `0x383030` (`/dev/dri/card1`), `0x383058` (`/dev/dri/card0`), and `0x383730` (`drmModeSetCrtc fail`); see `analysis/static-triage/spike_menu_game.strings`. - `/games/pokemon_pro/spike3/spike_menu/game` has matching GBM/EGL/DRM strings at offsets including `0x382cd0` (`/sys/class/drm`), `0x383030` (`/dev/dri/card1`), `0x383058` (`/dev/dri/card0`), and `0x383730` (`drmModeSetCrtc fail`); see `analysis/static-triage/spike_menu_game.strings`.
- `/etc/init.d/S12lvds_panel` launches `/games/spike3/bin/boot_display` early, and `/etc/init.d/game_monitor` later stops `boot_display` before launching `/games/game`. - `/etc/init.d/S12lvds_panel` launches `/games/spike3/bin/boot_display` early, and `/etc/init.d/game_monitor` later stops `boot_display` before launching `/games/game`.
Implementation consequence: the first emulator path uses QEMU `virtio-gpu` and browser-visible VNC/noVNC, not X11 forwarding. Implementation consequence: both paths use QEMU `virtio-gpu`, not X11
forwarding. Docker exposes VNC/noVNC for analysis; playable macOS uses native
Cocoa directly.
## Implemented Files ## Implemented Files
@@ -176,6 +196,7 @@ Confirmed results:
- Rootfs cache recheck on 2026-07-07 after the final shim rebuild: a second one-shot prepare logged `rootfs cache hit; reusing /workspace/emulation/work/rootfs.ext4`. - Rootfs cache recheck on 2026-07-07 after the final shim rebuild: a second one-shot prepare logged `rootfs cache hit; reusing /workspace/emulation/work/rootfs.ext4`.
- Mesa dependency verification on 2026-07-07: adding missing ARM64 loader dependencies (`libX11-xcb`, `libwayland-client`, `libxcb-dri2`, `libxcb-present`, `libxcb-randr`, `libxcb-sync`, `libxcb-xfixes`, and `libxshmfence`) changed `display-probe` from `EGL_BAD_PARAMETER` to a successful native GBM/EGL initialization. The probe log reports `EGL_MESA_platform_gbm`, `EGL_MESA_platform_surfaceless`, successful `eglInitialize`, and Mesa DRI loading from `/usr/local/spike-emu/mesa-dri/kms_swrast_dri.so`. - Mesa dependency verification on 2026-07-07: adding missing ARM64 loader dependencies (`libX11-xcb`, `libwayland-client`, `libxcb-dri2`, `libxcb-present`, `libxcb-randr`, `libxcb-sync`, `libxcb-xfixes`, and `libxshmfence`) changed `display-probe` from `EGL_BAD_PARAMETER` to a successful native GBM/EGL initialization. The probe log reports `EGL_MESA_platform_gbm`, `EGL_MESA_platform_surfaceless`, successful `eglInitialize`, and Mesa DRI loading from `/usr/local/spike-emu/mesa-dri/kms_swrast_dri.so`.
- Native path verification on 2026-07-07: forcing `SPIKE3_DISPLAY_BACKEND=native` reaches `OpenGL ES 3.1 Mesa 22.3.6` / `renderer: softpipe`, but `/games/game` repeatedly logs `failed to make image from buffer object` and `failed to create framebuffer`; native is therefore not the default auto presentation path. - Native path verification on 2026-07-07: forcing `SPIKE3_DISPLAY_BACKEND=native` reaches `OpenGL ES 3.1 Mesa 22.3.6` / `renderer: softpipe`, but `/games/game` repeatedly logs `failed to make image from buffer object` and `failed to create framebuffer`; native is therefore not the default auto presentation path.
- Native llvmpipe verification on 2026-07-14: `SPIKE3_GRAPHICS_STACK=debian-llvmpipe SPIKE3_CAPTURE_TARGET_LOGS=1 emulation/scripts/run-macos.sh` booted the ARM64 guest with HVF and the Cocoa display. The target log reports `OpenGL API OpenGL ES 3.2 Mesa 22.3.6 - Compatibility - Using Device: Mesa/X.org - llvmpipe (LLVM 15.0.6, 128 bits)`. QEMU monitor `screendump` produced `emulation/work/llvmpipe-test.ppm` (1920x1080), converted to `emulation/work/llvmpipe-test.png`, and the capture contains a complete Pokémon game frame. A host `ps` snapshot 22 seconds after launch reported QEMU at 534% CPU and 6.1% memory. This confirms functional multithreaded CPU rendering; it is not Apple-GPU acceleration. Repeated `failed to queue page flip: Invalid argument` messages remain in the serial log but did not prevent the captured frame. Because live animation was smooth, `run-macos.sh` now selects `debian-llvmpipe` by default; `SPIKE3_GRAPHICS_STACK=buildroot-software` retains the former behavior when explicitly requested.
- fbdev/surfaceless verification on 2026-07-07: forcing `SPIKE3_DISPLAY_BACKEND=fbdev` uses the real Mesa surfaceless display and an implicit pbuffer (`eglMakeCurrent ... draw=<pbuffer> read=<pbuffer>`). This clears the `eglChooseConfig num=0` retry path, but the target-created framebuffer remains incomplete. The serial log shows `failed framebuffer check for created target buffer`, `failed to create framebuffer`, and repeated `GL_INVALID_FRAMEBUFFER_OPERATION` from `glClear`, `glDrawArrays`, and `glDrawElements`. The capture `emulation/work/game-fbdev-implicit-pbuffer.ppm` is nonblank but still only shows the fallback color bands because no valid target swap/copy occurs. - fbdev/surfaceless verification on 2026-07-07: forcing `SPIKE3_DISPLAY_BACKEND=fbdev` uses the real Mesa surfaceless display and an implicit pbuffer (`eglMakeCurrent ... draw=<pbuffer> read=<pbuffer>`). This clears the `eglChooseConfig num=0` retry path, but the target-created framebuffer remains incomplete. The serial log shows `failed framebuffer check for created target buffer`, `failed to create framebuffer`, and repeated `GL_INVALID_FRAMEBUFFER_OPERATION` from `glClear`, `glDrawArrays`, and `glDrawElements`. The capture `emulation/work/game-fbdev-implicit-pbuffer.ppm` is nonblank but still only shows the fallback color bands because no valid target swap/copy occurs.
- Netbridge wiring verification on 2026-07-07: `python3 -m unittest discover -s analysis/netbridge-emulator`, Python bytecode compilation, and `bash -n` over the changed Docker/QEMU shell scripts all pass. The full `SPIKE3_NETBRIDGE_EMU=1` Docker boot has not yet been run, so guest acceptance of the emulated `/dev/ttyAMA5` path remains an open validation item. - Netbridge wiring verification on 2026-07-07: `python3 -m unittest discover -s analysis/netbridge-emulator`, Python bytecode compilation, and `bash -n` over the changed Docker/QEMU shell scripts all pass. The full `SPIKE3_NETBRIDGE_EMU=1` Docker boot has not yet been run, so guest acceptance of the emulated `/dev/ttyAMA5` path remains an open validation item.

254
native-playable-table.md Normal file
View File

@@ -0,0 +1,254 @@
# Native macOS Playable Table
## Status
The native playable-cabinet implementation lives under `emulation/`. It
combines two independently useful runtimes:
- QEMU 11 with HVF runs the generated ARM64 guest and genuine Pokémon Pro game
code. Its Cocoa window and CoreAudio output are the backbox, game display,
music, speech, and game effects.
- Visual Pinball X 10.8.1 owns the physical balls, collision geometry,
switches, trough inventory, and moving playfield mechanisms.
The default VPX frontend is now the clean-room table assembled at
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`. The supported first
release is deliberately two-window. It does not use VNC,
noVNC, browser capture, JPEG screendumps, or the dashboard for gameplay. The
dashboard remains an optional diagnostic monitor. This is an
evidence-calibrated playable development recreation; it is not a
measurement-validated replica of ramp heights, coil forces, or rebound curves.
The one-command Finder entry point is `emulation/run-playable.command`; the
terminal entry point is `emulation/scripts/run-playable-macos.sh`.
### Clean rebuild workflow
With no table override, `emulation/scripts/run-playable-macos.sh` runs
`emulation/vpx-rebuild/build.sh` and selects
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`. The maintained sources are
`design/geometry.json`, `design/lights.json`, the VBS template, procedural
assets, and generator under `emulation/vpx-rebuild/`; generated `source/` and
`dist/` files must not be hand-edited. `emulation/vpx-rebuild/run-standalone.sh`
provides the short no-QEMU physics/render path.
`SPIKE3_VPX_TABLE` still permits an explicit custom table. If that table needs
a preparation step, set `SPIKE3_VPX_BUILD`; setting the custom table without a
build override prevents the clean rebuild script from modifying it. The
historical `emulation/vpx/PokemonPro_Greybox.vpx` and
`emulation/vpx/manual/PokemonPro_Manual.vpx` are deprecated historical
artifacts. The clean workflow does not inspect or write either file, and
neither is the active geometry source or default.
### Current stop point (2026-07-20)
The bridge now ingests host events immediately and routes service controls over
active-low CPU-SPI. Start and Action use the installed power-distribution
command `0x15` path, including unsolicited input reports. The genuine display
has acknowledged service navigation, and protocol tests cover a Start change
without a preceding host poll. Those are transport results, not a completed
game.
The clean table and canonical hardware contract pass their static validators.
`emulation/vpx-rebuild/qa/runtime-smoke.log` records a bounded VPX 10.8.1
`CaptureAttract` pass on the exact current table SHA-256
`ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`,
with table/VBS load, script compile/start, Metal initialization, two rendered
frames, clean close, and exit code 0. In integrated modes,
the clean script publishes physical contacts, consumes exact-address lamp
components, preserves ordered driver pulses, establishes reconnect baselines,
and releases transients/mechanisms on disconnect. Cabinet edges move the
flippers locally while cabinet/EOS state is still sent to the genuine game.
The final clean table also completed the supervised native QEMU/VPX startup
gate on 2026-07-20. The accepted session reached `Playable cabinet connected`
with synchronized VPX physics, 392 lamp updates, 7 LED frames, 1 driver pulse,
stable captive-ball contacts, and QEMU-keyboard coin/Start press-release
records. The default hardware-output wait is 420 seconds because normal game
initialization can take about five minutes. A transient bridge blip is accepted
only after a later synchronized reconnect; unrecovered disconnects still fail
closed.
The remaining immediate cabinet gate is completing and preserving a genuine
normal game cycle. The generated QEMU disk is writable and persistent, but the
current conagent `data_store` model is memory-only. After that gate, the final clean table still needs a captured normal game,
multiball, full physical shot coverage, integrated RGB/driver/mechanism traces,
restart recovery, and a 60-minute inventory soak. Earlier greybox/manual
runtime logs remain useful historical bridge evidence, but they do not prove
the clean table's geometry or final trajectories.
## Runtime ownership
| Concern | Authority |
| --- | --- |
| Rules, scoring, player/ball progression | Genuine game process in QEMU |
| Display, music, speech, game effects | QEMU Cocoa/CoreAudio |
| Ball physics and collision geometry | Native VPX at a 1,000 Hz physics step |
| Physical switch state and pulses | VPX table script and controller plugin |
| Lamps and mechanism driver commands | Genuine game through netbridge |
| Mechanical ball/rubber/flipper/gate sounds | VPX only |
| Window placement and focus | Native macOS helper |
`emulation/playfield-contract/pokemon-pro-hardware.json` is the canonical
geometry-free mapping. It records 45 playfield switches, 11 sensors, 9 cabinet
controls, 13 drivers, and 113 logical lamps expanded to 176 component bindings
at 150 exact SPIKE addresses. The plugin mapping header is generated from this
contract by `emulation/vpx-plugin/generate-layout-mapping.py`; it is not a
second handwritten address map. Ball inventory and placement live separately
in the clean geometry/script rather than becoming transport fields.
## Playfield protocol and safe recovery
`analysis/netbridge-emulator/netbridge_emu.py` provides a persistent,
loopback-only JSONL endpoint at `127.0.0.1:8091` using
`spike3-playfield/v1`. VPX sends `hello`, `switch.set`, `switch.pulse`,
`cabinet.set`, `release_transient`, and `heartbeat`. The bridge sends complete
`snapshot` records, coalescible `lamp.delta` records, lossless ordered
`driver.pulse` records, and `status` records.
Every server record has a process-unique `bridge_session`, a monotonic launcher
`bridge_generation`, sequence, model revision, and monotonic timestamp. A new
connection is not considered ready after TCP connect or `hello`. The bridge
first sends a disconnected snapshot and `synchronizing`; the plugin releases
transients, restores stable switch/cabinet state, and sends a heartbeat as an
ordering barrier. The bridge reports `ready` and sends QEMU monitor `cont` only
after that barrier and after the guest UART is connected.
With `physics_authority=vpx`, the netbridge synthetic shooter, trough, outlane,
VUK, flipper-EOS, and ball-return shortcuts are disabled from process start.
On a VPX disconnect, transient contacts open, VPX mechanisms de-energize,
stable trough/held-ball contacts remain, and QEMU receives monitor `stop`. A
supervised bridge restart removes the old readiness record, restores only
stable contacts plus lamp/driver snapshot baselines, assigns a new session and
generation, and never replays cumulative driver counts as new pulses.
The implementation and synchronization tests are in
`analysis/netbridge-emulator/test_netbridge_emu.py`. The native supervisor is
`emulation/scripts/run-macos.sh`; the outer cabinet lifecycle and identity
checks are in `emulation/scripts/run-playable-macos.sh`.
## VPX plugin and table modes
`emulation/vpx-plugin/` is rebased to the installed VPX commit
`ca80d1604e6a525db2a3f6d1ec704fc651d573fa`. Its builder checks the exact Git
revision, host binary SHA-256, ARM64 architecture, and bundle version before
writing `emulation/work/vpx-plugin/Spike3Controller/vpx-host-attestation.txt`.
The playable launcher copies the attested app to a private workspace runtime,
installs the matching plugin there, and ad-hoc signs that private copy. It does
not modify the user's source VPX app.
Network work runs on a background thread. Bounded queues keep socket I/O away
from VPX's render/physics thread; lamp state is coalesced, while driver events
retain order and cumulative counters. Reconnect snapshots establish a driver
counter baseline and cannot become stale physical events.
The table has explicit modes:
- `standalone`: provides local serving, flipper/plunger control, and mechanism
behavior for no-QEMU physics/render checks.
- `spike3`: QEMU is authoritative for lamps and every driver-driven mechanism.
- `qa-spike3`: integrated handshake/input/output acceptance without falling
back to standalone behavior.
In integrated mode, a missing controller is fail-safe: the table remains in
SPIKE mode, disables standalone serving/GI, de-energizes driver-controlled
mechanisms, and logs the error. Flippers are the deliberate exception to game
driver ownership: cabinet buttons actuate them locally and publish their
cabinet/EOS states to the bridge. Slings, pops, trough eject, auto-launch,
posts, gate, Meowth motor, and ejects move only from mapped game driver pulses.
## Physics and private visual assets
`emulation/vpx-rebuild/design/geometry.json` is the active clean-room geometry
and physics source. It fixes a 952 by 2115 VPX plane, 6.5-degree pitch, and
standard 50-unit-diameter ball. XY references follow the full 451 by 998 Pro
locator registration; Z paths, clearances, restitution, friction, coil force,
and mechanism timing remain simulation values until measured on a physical Pro
machine. `design/lights.json` independently records lamp placement and
confidence rather than deriving visual coordinates from the transport map.
The deterministically registered official Pro photograph is the provisional
playfield texture; it retains photographed hardware, shadows, and lighting
under the separately rendered VPX geometry. Original procedural meshes provide
posts, hardware, toys, and mechanisms, and the CC0 mechanical bank supplies
physics audio without copying game samples. The table, official photograph,
Pokemon/Stern imagery, reference manual, and assembled output are private-use
artifacts and are excluded from any redistributable package.
## Controls and native windows
The launcher writes a private VPX input profile without overwriting existing
service bindings. Defaults are:
| Input | Function |
| --- | --- |
| Left/Right Shift or LB/RB | Flippers |
| Return, Space, A, or right trigger | Action/launch |
| `1` or Menu | Start |
| `5` or View | Coin |
| `7`/`8`/`9`/`0` or D-pad | Service back/down/up/enter |
The QEMU guest keyboard relay now implements the same map as VPX. Previously,
only Shift was a cabinet control when the Cocoa backbox had focus; Return and
`1` through `9` still used an obsolete diagnostic switch map. That produced the
observed failure where flippers navigated Guided Setup while Start, coin,
Action, and service keys did nothing useful. The corrected relay maps `1`,
`5`, Return/Space, and `7`/`8`/`9`/`0` to the named cabinet inputs above.
The 2026-07-15 generated rootfs was rebuilt with the correction. The installed
AArch64 `/usr/local/spike-emu/bin/keyboard-switch-relay` was extracted from the
ext4 image and matched an independent source build at SHA-256
`55054003950873900b5ce203292846f158b861060dbdd2e04b5be4a8afa1869c`.
A headless QEMU monitor test injected all ten keys and
`emulation/work/logs/keyboard-relay.log` recorded matching press/release pairs
for both flippers, Start, coin, Action twice, and all four service controls.
That event-name check alone was insufficient: CPU and powerdist contacts do
not pass through a node input-bank poll. The playfield bridge now runs a
bounded host-event pump which routes those edges to their real transports
immediately while leaving ordinary node-bus edges queued until the relevant
bank read. The cabinet aliases and routes are loaded from
`emulation/playfield-contract/pokemon-pro-hardware.json`.
VPX normally receives focus. On two displays the AppKit/Accessibility helper attempts a
fullscreen VPX playfield plus fullscreen QEMU backbox. On one display it uses
approximately 65% for VPX and 35% for a 16:9 backbox. If Accessibility access
is declined or a window does not expose fullscreen through AX, both processes
remain running and the launcher prints the saved placement log and manual
layout.
## Verification boundary
The reproducible checks are split by what they prove:
- `python3 -m unittest discover -s analysis/netbridge-emulator -v` verifies
recovered serial behavior, protocol ordering, output identity, safe pause,
snapshot restore, synchronization, and lossless driver delivery.
- `emulation/vpx-plugin/tests/run-tests.sh` verifies the generated mapping,
background client, ordered input edges, output records, driver baseline, and
reconnect behavior against a loopback server.
- `python3 emulation/playfield-contract/validate_contract.py --json` verifies
the geometry-free schema, channel counts/routes, manual evidence, consumer
loading, and generated-header parity.
- `emulation/vpx-rebuild/build.sh` regenerates assets/source, verifies the
assembled VPX, compares the round-trip VBS, and runs contract/object,
geometry, clearance, lamp-route, audio, and no-ball-teleport checks recorded
in `emulation/vpx-rebuild/qa/rebuild-validation.json`.
- `emulation/vpx-rebuild/qa/runtime-smoke.log` records the exact current table
loading, compiling/starting its script, rendering two frames, and closing
with exit code 0 under VPX 10.8.1 `CaptureAttract`. It does not claim
interactive ball or mechanism coverage.
- The accepted live cabinet startup proves the real QEMU game returned lamp,
LED, and driver output through the final table/plugin path and received
QEMU-keyboard coin/Start edges. It does not prove a complete normal game.
Do not infer a normal three-ball game, multiball, measured frame-rate percentile,
or 60-minute invariant soak from the static/unit suites. Those acceptance rows
must remain explicitly open until their corresponding native run logs exist.
## Optional composed backbox
QEMU D-Bus shared-memory scanout is a follow-up only. It must demonstrate
correct page flips, sustained 60 fps for ten minutes, and no more than one
added frame before being connected to a VPX Backglass/ScoreView texture. The
working two-window Cocoa path remains the fallback; VNC/browser/screendump
capture is not an accepted gameplay fallback.

View File

@@ -8,6 +8,7 @@ Target artifacts:
- `/games/pokemon_pro/spike3/netbridge/spike3-swd.cfg` - `/games/pokemon_pro/spike3/netbridge/spike3-swd.cfg`
- `/games/pokemon_pro/spike3/netbridge/rp2040.cfg` - `/games/pokemon_pro/spike3/netbridge/rp2040.cfg`
- `/games/pokemon_pro/spike3/netbridge/version.txt` - `/games/pokemon_pro/spike3/netbridge/version.txt`
- `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex`
Generated artifacts: Generated artifacts:
@@ -160,6 +161,35 @@ CRC32(0, &__logical_binary_start, 0x12a40)
For this ELF, `__logical_binary_start` maps to file offset `0x1100`. A Python `binascii.crc32` over `games/pokemon_pro/spike3/netbridge/netbridge.elf[0x1100:0x1100+0x12a40]` gives `0x357f5327`. For this ELF, `__logical_binary_start` maps to file offset `0x1100`. A Python `binascii.crc32` over `games/pokemon_pro/spike3/netbridge/netbridge.elf[0x1100:0x1100+0x12a40]` gives `0x357f5327`.
## Power-Distribution Cabinet Inputs
Confirmed static facts:
- SHA-256 of `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex` is `f8eb96998a32ff86008663f7f07f2df76c32a9c86200f690c215dc36917391a9`; it is a 32,799-byte Intel HEX file whose decoded address range starts at `0x3800`.
- In `netbridge.elf`, `HOST_Update` at `0x10001210` calls `POWERDIST_IsSpike3`. For a SPIKE 3 controller, commands `0x10..0x1f` are passed to `POWERDIST_SendCmd` at `0x100020b4`; the legacy non-SPIKE-3 command `0x15` fallback is the unrelated one-byte `0xff` response.
- `POWERDIST_Update` at `0x10002174` parses controller frames and forwards their original command, sequence, and payload to the Pi with `HOSTUART_SendMessage`. These frames are not restricted to replies to a Pi request.
- Disassembling the decoded power-distribution image as Thumb code at `0x3800` shows command `0x15` dispatch at `0x427c`. It calls `0x475c` and returns exactly one payload byte.
- Function `0x46b4` composes that byte from raw GPIO P0_8, P0_9, and P0_11 as bits 0, 1, and 2. The installed cabinet map assigns Action to bit 0 and Start to bit 1; bit 2 is the coin-door power interlock state.
- Most importantly, the controller's input-change path at `0x407a` calls the frame sender at `0x3f9c` with command `0x15`, sequence `0`, a one-byte payload, and length `1`. A separate command handler at `0x428a` provides the same state for explicit polls.
The last point explains the VPX failure introduced while adding the first
power-distribution model. The emulator answered explicit `0x15` polls but did
not reproduce the controller's unsolicited change report, so a Start edge
could exist inside the emulator without ever reaching game code. The repaired
UART service queues every changed Start/Action state as command `0x15`,
sequence `0`, and drains those frames independently of incoming host traffic.
An integration test uses a socket pair to prove that a Start transition emits
`15 00 01` at the decoded protocol level without first sending a poll.
Reproduction commands used for the static controller pass:
```sh
arm-none-eabi-objcopy -I ihex -O binary \
games/pokemon_pro/spike3/powerdist/powerdist_ext.hex /tmp/powerdist_ext.bin
arm-none-eabi-objdump -D -b binary -m arm -M force-thumb \
--adjust-vma=0x3800 /tmp/powerdist_ext.bin
```
## Nodebus Layer ## Nodebus Layer
`NODE_Init` configures a mixed PIO/hardware-UART nodebus: `NODE_Init` configures a mixed PIO/hardware-UART nodebus:
@@ -220,6 +250,8 @@ It currently provides:
- Firmware CRC response `0x357f5327`. - Firmware CRC response `0x357f5327`.
- PTY, serial-device, and Unix-socket serving modes for QEMU or host-side serial plumbing. - PTY, serial-device, and Unix-socket serving modes for QEMU or host-side serial plumbing.
- Existing-node stub at address `0x0a` with Magikarp/topper-oriented commands `0xf2`, `0xfc`, `0xfd`, `0xfe`, and `0xff`. - Existing-node stub at address `0x0a` with Magikarp/topper-oriented commands `0xf2`, `0xfc`, `0xfd`, `0xfe`, and `0xff`.
- Power-distribution command `0x15` polling plus unsolicited sequence-0
Start/Action change reports matching the installed controller firmware.
Run: Run:
@@ -243,8 +275,8 @@ Verification performed:
```text ```text
python3 -m unittest discover -s analysis/netbridge-emulator python3 -m unittest discover -s analysis/netbridge-emulator
.... ...................................................................
Ran 4 tests in 0.000s Ran 67 tests
OK OK
python3 -m py_compile analysis/netbridge-emulator/netbridge_emu.py analysis/netbridge-emulator/test_netbridge_emu.py python3 -m py_compile analysis/netbridge-emulator/netbridge_emu.py analysis/netbridge-emulator/test_netbridge_emu.py

View File

@@ -1,5 +1,72 @@
# Next Analysis Targets # Next Analysis Targets
## Priority 0: Native playable acceptance and calibration
The QEMU/HVF Cocoa game path, loopback `spike3-playfield/v1` bridge, safe
pause/restart synchronization, VPX 10.8.1 controller-plugin rebase, generated
hardware-contract mapping, clean-room dual-mode VPX table, private controller
profile, and native two-window launcher are implemented; see
`native-playable-table.md` and `vpx-table-recreation.md`. The default launcher
now builds and selects
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`. The deprecated
`emulation/vpx/PokemonPro_Greybox.vpx` is historical evidence that the clean
rebuild does not inspect or write; it is not a build input or runtime default.
The input transport blockers are now repaired and live-checked. The table uses
`VPXActionKey(25..28)`; power-distribution command `0x15` carries active-low
Action/Start/door state; CPU-SPI `0-SW-23` is correctly aligned; and service
Back/Up/Down/Enter use CPU-SPI `0-SW-8/9/10/11`. Immediate host-event ingestion
replaces the broken dependency on node-bus polling. The genuine display
accepted service Up/Down/Back, and the guest acknowledged the exact raw states.
The final clean table now passes the supervised native startup gate: the
2026-07-20 run reached `Playable cabinet connected`, returned 392 lamp updates,
7 LED frames, and 1 driver pulse, kept both captive optos stable, and delivered
QEMU-keyboard coin/Start edges. The launcher now allows 420 seconds because
normal game initialization can take about five minutes. The next blocking
integration target is a complete normal game and persistence check. If a fresh
image enters Guided Setup, complete Save & Exit through the repaired panel,
trace any subsequent invalid-machine decision, identify the minimum genuine
NVRAM or conagent-backed setup records, persist them across two reboots, and
prove the second boot reaches attract mode and accepts Start.
The clean table and geometry-free hardware contract pass their static
validators. `emulation/vpx-rebuild/qa/runtime-smoke.log` records a bounded VPX
10.8.1 `CaptureAttract` pass on 2026-07-20: it loaded the table and VBS,
compiled/started the script, rendered two frames, and closed with exit code 0.
The final smoke now matches the current distribution SHA-256
`ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`.
It is not interactive play or a substitute for the QEMU-connected acceptance
below.
After those blockers, continue acceptance and calibration:
- Complete and preserve a normal game plus a multiball sequence through the
genuine rules on the final clean table, without dashboard ball-motion
shortcuts.
- Exercise the shooter lane, both orbits, every ramp, both outlanes/returns,
pops, QEMU-authoritative slings, Pokedex captive chain, Town VUK, right-ramp
hold/Pikachu post, gates, spinner, targets, and Meowth movement with physical
VPX balls. Treat contact injection as transport coverage, not shot proof.
- Capture exact-address RGB lamp behavior, ordered driver pulses, retained-ball
ownership, disconnect fail-safe, and reconnect baselines in one integrated
run of the final table.
- Record input-to-visible-flipper and bridge-output-to-mechanism p95/p99 from
live native traces, plus rendered fps/1% low under simultaneous QEMU load.
- Preserve a full 60-minute inventory soak and explicit QEMU, bridge, and VPX
restart logs before declaring the long-run acceptance row complete.
- Compare `emulation/vpx-rebuild/design/geometry.json` with a physical Pro
machine: measure ramp Z/clearance, post and toy footprints, Pikachu post
direction/stroke, Meowth crank travel, coil timing, pitch, and ball/rubber
behavior. Replace estimated or simulation-tuned values only with recorded
evidence.
- Acquire a licensed stripped/bare-playfield scan to replace the official
registered photograph, whose baked-in hardware and lighting remain visible
beneath VPX geometry. Re-run visual and runtime QA after that asset change.
- Test QEMU D-Bus shared-memory scanout only after the two-window release is
stable. Retain Cocoa rather than adopting a VNC/browser capture fallback if
page-flip, 60 fps, or one-frame latency gates fail.
The current derived-data inventory and confidence boundaries are summarized in `analysis-evidence-map.md`. This page tracks only the work that remains or still needs stronger validation. The current derived-data inventory and confidence boundaries are summarized in `analysis-evidence-map.md`. This page tracks only the work that remains or still needs stronger validation.
## Priority 1: Main Game Binary ## Priority 1: Main Game Binary
@@ -26,7 +93,8 @@ Suggested first passes:
Follow-up passes: Follow-up passes:
- Continue from `emulation/`: QEMU now boots the generated rootfs, exposes noVNC/dashboard, uses cached rootfs preparation, loads `virtio_gpu`, creates `/dev/dri/card0` plus `/dev/dri/renderD128`, and reaches `boot-display`, `spike-menu`, and `/games/game`. The generated rootfs includes a display probe, `fbdev` fallback interposer, Mesa surfaceless/pbuffer support, synthetic GLES/GBM/EGLImage fallbacks, GPIO sysfs stubs, framebuffer diagnostic renderer, and null ALSA default. Verified captures include nonblank `emulation/work/boot-display-fbo-wrap.ppm`, `emulation/work/spike-menu-gpio-wrap.ppm`, `emulation/work/game-gl-limits.ppm`, `emulation/work/game-fbdev-real-egl.png`, and the earlier diagnostic screen in `emulation/work/game-diagnostic.png`. The prior black-screen, missing-ALSA-default, boot-display framebuffer retry, SPIKE-menu GPIO 25 spam, Mesa loader dependency, and zero-varying shader blockers are cleared. The current display blocker is the main game's target-created EGLImage/FBO path: `eglMakeCurrent` succeeds with a real pbuffer, but the framebuffer remains incomplete and Mesa reports `GL_INVALID_FRAMEBUFFER_OPERATION`, so VNC still shows fallback color bands instead of a game frame. Other current blockers are TAS/MAX amp register verify reads and bridge/netbridge startup failures (`OpenOCD init failed`, `netbridge image CRC mismatch`, `failed to init bridge thread`). The next pass should fix or fake the FBO attachment path, then decide which bridge/netbridge and audio paths need modeling versus inert stubs. - Exercise the new process-level lane documented in `process-level-emulation.md`. Static validation resolves all 51 target runtime libraries and matching `GLIBC_2.39`/`GLIBCXX_3.4.32` nodes; launcher/unit/syntax checks and the Apple Silicon ARM64 container build pass without executing the target. The initial `prepare` run exposed a nested `/games/spike3` alias bug which is now fixed and regression-tested; the post-fix run was blocked by the local Codex Docker approval/usage gate. The next acceptance sequence is `prepare`, `boot-display`, `spike-menu`, then `game`: execute the Mesa FBO smoke test, verify changing noVNC frames on port 6081, nonzero WAV capture, five-minute attract stability, one synthetic-ball Start/flipper/drain cycle from the dashboard, a qemu-user smoke run, and live GDB attachment. Record each failure from the timestamped `emulation/work/runs/` bundle rather than treating source completion as playability.
- Continue from `emulation/`: QEMU now boots the generated rootfs, exposes noVNC/dashboard, uses cached rootfs preparation, loads `virtio_gpu`, creates `/dev/dri/card0` plus `/dev/dri/renderD128`, and reaches `boot-display`, `spike-menu`, and `/games/game`. The generated rootfs includes a display probe, `fbdev` fallback interposer, Mesa surfaceless/pbuffer support, synthetic GLES/GBM/EGLImage fallbacks, GPIO sysfs stubs, framebuffer diagnostic renderer, and null ALSA default. Verified captures include nonblank `emulation/work/boot-display-fbo-wrap.ppm`, `emulation/work/spike-menu-gpio-wrap.ppm`, `emulation/work/game-gl-limits.ppm`, `emulation/work/game-fbdev-real-egl.png`, and the earlier diagnostic screen in `emulation/work/game-diagnostic.png`. The prior black-screen, missing-ALSA-default, boot-display framebuffer retry, SPIKE-menu GPIO 25 spam, Mesa loader dependency, and zero-varying shader blockers are cleared. The Docker/VNC softpipe path still encounters the target-created EGLImage/FBO issue and fallback color bands. The native macOS HVF/Cocoa path now has a verified complete 1920x1080 game-frame capture with the opt-in Debian Mesa 22.3.6 llvmpipe stack, although it uses about 5.3 host CPU cores and logs rejected page flips. Other current blockers are TAS/MAX amp register verify reads and bridge/netbridge startup failures (`OpenOCD init failed`, `netbridge image CRC mismatch`, `failed to init bridge thread`). The next display pass should measure llvmpipe frame pacing against softpipe and then investigate an Apple-GPU-backed VirtIO/translation path separately from the working CPU-rendered baseline.
- Validate `analysis/topper-emulator/topper_emulator.py` on live hardware or a nodebus capture. - Validate `analysis/topper-emulator/topper_emulator.py` on live hardware or a nodebus capture.
- Recover the exact Magikarp topper `IDENTITY_BOARD_PART_ID` expected by command `0xfe`. - Recover the exact Magikarp topper `IDENTITY_BOARD_PART_ID` expected by command `0xfe`.
- Confirm physical UART pins, baud, voltage level, and any netbridge timing/break behavior required for a MicroPython node. - Confirm physical UART pins, baud, voltage level, and any netbridge timing/break behavior required for a MicroPython node.
@@ -89,7 +157,7 @@ Follow-up passes:
## Priority 4: Netbridge Debug Path ## Priority 4: Netbridge Debug Path
Status: first-pass strings, ELF program-header review, targeted Ghidra decompilation, host protocol recovery, a Python host-side emulator scaffold, Docker/QEMU optional serial wiring, and a Pico SDK firmware recreation are complete; see `package-image-firmware.md`, `netbridge-re.md`, and `docker-qemu-emulation.md`. Status: first-pass strings, ELF program-header review, targeted Ghidra decompilation, host protocol recovery, power-distribution command `0x15` static recovery, a Python host-side emulator scaffold, Docker/QEMU optional serial wiring, and a Pico SDK firmware recreation are complete; see `package-image-firmware.md`, `netbridge-re.md`, and `docker-qemu-emulation.md`.
Targets: Targets:
@@ -108,6 +176,10 @@ Findings:
- `netbridge.elf` is a 32-bit ARM static executable with code mapped at `0x10000000` and RAM at `0x20000000`/`0x20040000`. - `netbridge.elf` is a 32-bit ARM static executable with code mapped at `0x10000000` and RAM at `0x20000000`/`0x20040000`.
- Strings include `uart_node.c`, `NODEBUS_Poll`, `NODEUART_Write`, `UART_NodeRxIrq`, and `WS2812_NodeSetLED`. - Strings include `uart_node.c`, `NODEBUS_Poll`, `NODEUART_Write`, `UART_NodeRxIrq`, and `WS2812_NodeSetLED`.
- Ghidra decompilation identifies the Pi-facing host UART as 2,000,000 baud STX/ETX/ESC framed messages on RP2040 GPIO 16/17, with `0x30` returning firmware CRC `0x357f5327` and `0x41` carrying direct nodebus requests. - Ghidra decompilation identifies the Pi-facing host UART as 2,000,000 baud STX/ETX/ESC framed messages on RP2040 GPIO 16/17, with `0x30` returning firmware CRC `0x357f5327` and `0x41` carrying direct nodebus requests.
- Static disassembly of `powerdist_ext.hex` confirms that command `0x15`
returns GPIO P0_8/P0_9/P0_11 in bits 0/1/2 and emits an unsolicited
sequence-0 report whenever a debounced cabinet input changes. The Python
emulator now reproduces both the poll and asynchronous paths.
- `analysis/netbridge-emulator/netbridge_emu.py` implements the recovered host frame codec, core bridge commands, CRC response, PTY serving mode, and a stub node at address `0x0a`. - `analysis/netbridge-emulator/netbridge_emu.py` implements the recovered host frame codec, core bridge commands, CRC response, PTY serving mode, and a stub node at address `0x0a`.
- `analysis/netbridge-pico-firmware/` implements the same recovered protocol as a Pico SDK firmware recreation with a portable host-core test. - `analysis/netbridge-pico-firmware/` implements the same recovered protocol as a Pico SDK firmware recreation with a portable host-core test.
- Docker/QEMU now has an optional `SPIKE3_NETBRIDGE_EMU=1` path that starts the Python emulator on `emulation/work/netbridge.sock`, attaches it as a second PL011 serial, maps guest `/dev/ttyAMA5` to the real serial, and lets the preload shim pass that path through. - Docker/QEMU now has an optional `SPIKE3_NETBRIDGE_EMU=1` path that starts the Python emulator on `emulation/work/netbridge.sock`, attaches it as a second PL011 serial, maps guest `/dev/ttyAMA5` to the real serial, and lets the preload shim pass that path through.
@@ -122,12 +194,12 @@ Follow-up passes:
## Priority 5: Node Firmware ## Priority 5: Node Firmware
Status: first-pass Intel HEX range parsing complete for top-level node firmware; see `package-image-firmware.md`. Status: first-pass Intel HEX range parsing is complete for top-level node firmware, and the power-distribution `0x15` input path has been disassembled; see `package-image-firmware.md` and `netbridge-re.md`.
Targets: Targets:
- All `*-LPC*.hex` files under `/games/pokemon_pro`. - All `*-LPC*.hex` files under `/games/pokemon_pro`.
- `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex`. - `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex` (input-report path complete; other commands remain open).
Why: Why:

View File

@@ -131,4 +131,7 @@ Parsed ranges:
| `/games/pokemon_pro/ws2812node-LPC1313-1_33_0.hex` | 900 | 14336 | `0x1000` | `0x47ff` | | `/games/pokemon_pro/ws2812node-LPC1313-1_33_0.hex` | 900 | 14336 | `0x1000` | `0x47ff` |
| `/games/pokemon_pro/ws2812pinnode-LPC1313-1_33_0.hex` | 1157 | 18432 | `0x1000` | `0x57ff` | | `/games/pokemon_pro/ws2812pinnode-LPC1313-1_33_0.hex` | 1157 | 18432 | `0x1000` | `0x57ff` |
`/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex` is present in the `.sidx` package index and remains a separate follow-up target for power-distribution firmware parsing. `/games/pokemon_pro/spike3/powerdist/powerdist_ext.hex` is present in the
`.sidx` package index. Its command `0x15` cabinet-input poll and unsolicited
change-report paths have now been recovered; other power-distribution commands
remain follow-up targets. See `netbridge-re.md`.

120
process-level-emulation.md Normal file
View File

@@ -0,0 +1,120 @@
# Process-Level Game Emulation
## Scope
The process lane runs only the extracted AArch64 game/display target, game
assets, extracted loader/library runtime, generated Mesa overlay, and clean-room
machine model. It does not boot Buildroot and does not modify extracted evidence.
Implementation entry points:
- `spike3-emu`
- `emulation/process/spike3_emu.py`
- `emulation/process/config.default.json`
- `emulation/process/Dockerfile`
Generated state is confined to `emulation/work/capsule/`,
`emulation/work/runs/`, and `emulation/work/packs/`; all three are excluded from
Git.
## Confirmed Static Runtime Contract
The launcher's dependency resolver was run against:
- `/games/pokemon_pro/game`
- `/games/pokemon_pro/spike3/spike_menu/game`
- `/games/pokemon_pro/spike3/bin/boot_display`
It resolves 51 unique `DT_NEEDED` libraries with no unresolved SONAMEs. The
highest required and provided version nodes both match: `GLIBC_2.39` and
`GLIBCXX_3.4.32`. This confirms that the extracted loader, glibc, and libstdc++
must remain the process runtime; substituting Debian bookworm's older
libstdc++ would be incompatible.
The initial asset capsule retains the full read-only
`/games/pokemon_pro` directory. `trace` inventories actual file opens, and
`pack --profile traced` creates a generated minimal tree whose copied files are
verified against the read-only source with SHA-256.
## Runtime Architecture
| Layer | Implementation |
| --- | --- |
| Host selection | Apple Silicon uses an ARM64 Linux container; AArch64 Linux uses native execution; x86-64 Linux uses qemu-user. |
| Isolation | Bubblewrap mounts game and target libraries read-only, creates an offline target network namespace, and binds only generated writable state. |
| Machine I/O | `libspike3machine_client.so` connects `/dev/spidev4.0`, `/dev/i2c-10`, GPIO, and netbridge calls to `emulation/work/machine.sock`. |
| Display | `libspike3emu_stub.so` writes XRGB frames to a generated regular `/dev/fb0`; ffplay loops the frame into Xvfb, then x11vnc/noVNC serves port 6081. |
| Controls | Dashboard port 8090 uses the private Unix RPC socket; loopback-only TCP port 8091 serves VPX and other external controllers. |
| Audio | ALSA is converted to fixed 48 kHz stereo S16LE file output and finalized as `audio.wav` per run. |
| Debugging | Native uses extracted `gdbserver`; qemu-user uses `-g`; generated helpers resolve the PIE slide and named Ghidra offsets. |
| System fallback | `--engine system` delegates to the existing `qemu-system-aarch64`/virtio-gpu/noVNC workbench. |
Generated safe command replacements log and block `reboot`, `halt`, `poweroff`,
`shutdown`, and hostname changes. OpenOCD is replaced by a controlled stub whose
exit status defaults to success. The machine service implements no physical
outputs; the launcher's `hardware` backend is deliberately refused.
## Usage
```sh
./spike3-emu prepare
./spike3-emu run boot-display
./spike3-emu run spike-menu
./spike3-emu run game
./spike3-emu debug game
./spike3-emu trace game
./spike3-emu pack --profile traced
./spike3-emu clean --legacy-system
./spike3-emu clean --legacy-system --yes
```
Run artifacts include effective configuration, stdout, syscall trace, broker
transactions, display-service logs, asset hashes, blocked commands, PCM/WAV
capture, GDB setup, and exit status.
## Verified Runtime Results (2026-07-11)
`./spike3-emu prepare` completed on Apple Silicon with 51 resolved libraries.
A repeat preparation completed from the dependency/config fingerprint cache in
0.56 seconds. Docker inspection reported the image as `linux/arm64`, 340,201,729
content bytes (1.36 GB virtual size). No QEMU process is used in process mode.
The ARM64 Mesa smoke command completed with framebuffer status `0x8cd5`, pixel
`64,128,191,255`, EGL 1.4, and no GL error. Live `boot-display`, `spike-menu`,
and `game` runs loaded the HLE preload and executed the target. The menu/game
accepted netbridge CRC `0x357f5327`. Dashboard port 8090 returned broker state,
and a direct JSONL request to `127.0.0.1:8091` returned a revisioned
`state.delta`. Manual Start/launch/drain events changed the virtual lifecycle.
A one-shot `docker stats --no-stream` sample shortly after game startup was
129.78% CPU, 200.9 MiB resident usage, and 63 container processes. The native
game reached its netbridge CRC check in about 5.1 seconds in that run. This is a
startup sample, not a five-minute steady-state benchmark. `docker top` showed
the extracted `ld-linux-aarch64.so.1` launching `/games/game` under bubblewrap
and no `qemu-system-*` or `qemu-aarch64` process. The legacy cleaner dry-run
reported 6,505,625,192 removable bytes (6 GiB rootfs plus a 63,173,402-byte
macOS runtime cache); those files were deliberately not removed automatically.
Changing-game FPS is not measurable until the connector failure is resolved.
The remaining display limit is confirmed: software EGL and the synthetic DRM
resource inventory initialize, but the proprietary window helper reports
`failed to get DRM connector for display_number 1`; GBM buffers are therefore
not allocated. Current nonzero framebuffer captures are HLE diagnostic frames,
not genuine changing game frames. Short runs produced no WAV data. Five-minute
attract mode, a target-observed full three-ball game, live GDB attachment, and
VPX round-trip remain open.
Unit verification is 14 launcher tests, 25 machine-model tests, and 3 dashboard
tests. Two machine socket-bind tests skip only because the managed test sandbox
denies AF_UNIX/AF_INET binds; the live Docker TCP and Unix paths were exercised.
## VPX Bridge
`emulation/vpx-plugin/` is pinned to VPX commit
`215129065954d265100e4fa7ff063154c6fedf5d`. Its build produced a Mach-O ARM64
ControllerPlugin dylib with `Spike3ControllerPluginLoad` and
`Spike3ControllerPluginUnload` exports. It forwards standard cabinet actions,
publishes SPIKE binary inputs/controlled devices, reconnects safely, and exposes
the `Spike3.Controller` script API. `example-spike3.vbs` covers target, lane,
trough, and drain events. VPX is not installed in `/Applications` on this host,
so actual VPX loading and bidirectional table verification are not claimed.

384
qr-code-analysis.md Normal file
View File

@@ -0,0 +1,384 @@
# QR Code Analysis
## Scope
The QR code system on Pokemon Pro involves **two separate code paths**:
1. **QR Code Generation** — The game uses Nayuki's `qrcodegen` library to generate QR codes (WiFi config, Stern Insider Connected login, support URLs) and display them on-screen.
2. **QR Code Scanning** — A standalone hardware scanner module connected to the game via a `node4` bridge board (LPC812/LPC1124 MCU on the SPIKE-3 nodebus) and a serial UART.
**Important finding**: The QR code scanning functionality is in the **main game binary** (`/games/pokemon_pro/game`), **not** in `conagent`. The `conagent` binary has no QR-related strings or code. See `conagent-re.md` for the connectivity agent's separate role.
## Source Binary
- Target: `/games/pokemon_pro/game`
- Workspace path: `games/pokemon_pro/game`
- SHA-256: (see `artifact-index.md`)
- Type: stripped AArch64 PIE ELF, 52,829,400 bytes
- Embedded library: `qrcodegen` (Nayuki's QR Code Generator, C library)
## QR Code Generation
### Library: qrcodegen
The game embeds **Nayuki's QR Code Generator** library (`qrcodegen.c` at file offset `0x25e6c88`). This is a standalone C library that generates QR code symbol data from text input. Confirmed API functions:
| Function | Offset | Purpose |
|---|---|---|
| `qrcodegen_encodeSegmentsAdvanced` | `0x26028f8` | Core encoding entry point |
| `qrcodegen_getSize` | `0x2602760` | Get QR code module size |
| `qrcodegen_getModule` | `0x2602998` | Get individual module bit |
| `qrcodegen_isNumeric` | `0x2602728` | Check if text is numeric mode |
| `qrcodegen_isAlphanumeric` | `0x26029b0` | Check if text is alphanumeric mode |
| `qrcodegen_makeBytes` | `0x2602a00` | Create byte segment |
| `qrcodegen_makeNumeric` | `0x2602a18` | Create numeric segment |
| `qrcodegen_makeAlphanumeric` | `0x2602a38` | Create alphanumeric segment |
| `qrcodegen_makeEci` | `0x2602a30` | Create ECI segment |
Internal assertion strings confirm library version constraints:
- `qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX` (0x25e6c98)
- `21 <= qrsize && qrsize <= 177` (0x25e6d90)
- `0 <= (int)ecl && (int)ecl < 4` (0x25e7070)
### QR Code Output: BMP Files
Generated QR codes are rendered as **BMP image files**:
| Path | Offset | Purpose |
|---|---|---|
| `/tmp/qrx_.bmp` | `0x25db068` | Temporary QR code image |
| `/tmp/login.bmp` | `0x25e1760` | Login QR code output |
| `/connectivity/qr_code_storage/%d.bmp` | `0x25e26e8` + `0x25e2718` | Persistent QR code storage |
### QR Code Configuration Properties
QR code display properties are configured via a JSON-like object with these fields (all at offsets `0x25db270``0x25db2e0`):
| Field | Type | Purpose |
|---|---|---|
| `qrCodeText` | string | The text/URL to encode in the QR code |
| `qrCodeAppendMachineUuid` | bool | Whether to append machine UUID to text |
| `qrCodeStartTime` | timestamp | QR code validity window start |
| `qrCodeEndTime` | timestamp | QR code validity window end |
| `qrCodeXPosition` | int | Screen X position |
| `qrCodeYPosition` | int | Screen Y position |
| `qrCodeSize` | int | QR code size in pixels |
### Generated QR Code Types
| Type | String Context | Offset |
|---|---|---|
| Stern Insider Connected login | `Stern Insider Connected™ QR code.` | `0x25a7dba` |
| Connected Login QR | `Connected™ Login QR code.` | `0x25a7e99` |
| WiFi QR Code | `Scan WiFi QR Code` | `0x25a8360` |
| Quick Registration | `Connectivity/QrCodes/quick_registration_qr_code` | `0x25e1cf8` |
| Support | `Connectivity/QrCodes/support` | `0x25dc7e8` |
| Tech Support | `SCAN THIS QR CODE TO REACH OUR TECH SUPPORT TEAM` | `0x25dc231` |
## QR Code Scanning — Hardware Architecture
### Physical Components
The QR scanner system consists of:
1. **Scanner module** — A standalone 2D barcode scan engine (OEM part `part:520-9865-00` at `0x25b8c20`). The specific module model (Zebra, Honeywell, Newland, etc.) is not identifiable from strings; the firmware is a thin bridge without scanner-brand identifiers.
2. **Node4 bridge board** — A `node4` SPIKE-3 nodebus peripheral with dual firmware options:
- `node4-LPC812-1_33_0.hex` (LPC812, Cortex-M0+, 12 KB decoded)
- `node4-LPC1124_303-1_33_0.hex` (LPC1124, Cortex-M0+, 14 KB decoded)
3. **Connections**:
- Ribbon cable from scanner module to node4 board
- RJ45 nodebus cable from node4 to the game system
4. **GPIO antenna switch**: Two RF antennas on `gpiochip1` lines 3 (`ant1`) and 7 (`ant2`), controlled via `libgpiod.so.2`
### Serial Interface
The game communicates with the scanner module via **serial UART**:
| Path | Selection Logic | Offset |
|---|---|---|
| `/dev/ttyAMA2` | Default (kernel release byte != '5') | `0x25e9468` |
| `/dev/ttyAMA5` | Kernel release byte == '5' | `0x25e9478` |
Selection function at VA `0x94b8b0`: Uses `uname()` to check a kernel release byte, compares to `0x35` (`'5'`), selects ttyAMA5 on match, ttyAMA2 otherwise.
### Serial Configuration (VA `0x92d350`)
- **Open**: `open(path, O_RDWR | O_NOCTTY)`
- **Termios2**: Uses `TCGETS2` (ioctl `0x541e`) / `TCSETS2` (ioctl `0x541f`) — `struct termios2` for raw baud rates
- **Data bits**: 8 data bits, no parity
- **Stop bits**: 1 stop bit (configurable to 2 via `config[5] == 2`)
- **Local mode**: `CLOCAL | CREAD` (`0x880`)
- **Baud rate**: Configurable via 4-entry table indexed by `config[4] - 5`; actual rates not extractable from static analysis (uses non-standard termios2 CBAUD encodings)
- **Flow control**: `tcflow(fd, TCOON)`, `tcflush(fd, TCIOFLUSH)`
- **Timeout**: 1000 ms via `tcdrain`
### Node4 Bridge Firmware
The `node4` firmware files are thin, simple firmware images (6-11 ARM Thumb function entries) that bridge between:
- **Scanner side**: UART connection to the scanner module (baud rate divisors suggest 57600 for LPC812, 9600/230400/38400 for LPC1124)
- **Nodebus side**: SPIKE-3 nodebus protocol with node address `0x04`
The firmware does **not** contain:
- Scanner decode libraries (zbar, quirc, etc.)
- Scanner brand identifiers or protocol tables
- Any human-readable strings at all
This confirms the scanner module is a pre-integrated 2D barcode engine that handles decode internally, and the node4 board is a thin UART-to-nodebus bridge.
## QR Scanner Protocol
### Bridge Protocol (VA `0x94b9d0``0x94bbfc`)
The game communicates with the bridge (and through it to the scanner) using a custom framed protocol:
- **Buffer size**: 256 bytes (`mov w2, #0x100`)
- **Frame format**: `STX` + escaped body (cmd, seq, payload, checksum) + `ETX`
- **STX**: `0x02` — start of frame
- **ETX**: `0x03` — end of frame, triggers message processing
- **ESC**: `0x1B` — escape byte; next byte has bit `0x80` set
- **Checksum**: `(-sum(body)) & 0xFF`, verified before dispatch
- **Valid commands**: `0x10` through `0x41` (checked via `sub w0, w2, #0x10; cmp w0, #0x31`)
- **Command 0x30**: Firmware CRC handler at VA `0x953fc0`
- **Error handling**: "bridge rx checksum error", "message overrun", "too many message", "bridge %s unknown command: 0x%02x"
### Scanner Status Register (16 states)
The game tracks scanner status via an enum/string table at `0x25b8a40``0x25b8c00`:
| Offset | String | Meaning |
|---|---|---|
| `0x25b8a40` | `QR SCANNER BACKLIGHT` | Backlight control command |
| `0x25b8a58` | `QR SCANNER STATUS READY` | Scanner is ready for a scan |
| `0x25b8a70` | `QR SCANNER STATUS STRING` | Text string decoded from QR |
| `0x25b8a90` | `QR SCANNER STATUS ENCODED DATA` | Raw binary encoded data received |
| `0x25b8ab0` | `QR SCANNER STATUS MISSED STRING` | A scan was started but no decode |
| `0x25b8ad0` | `QR SCANNER STATUS OVERRUN` | Buffer overrun condition |
| `0x25b8af0` | `QR SCANNER STATUS UNSUPPORTED_AP` | Unsupported access point (WiFi) |
| `0x25b8b18` | `QR SCANNER STATUS FIFO OVERRUN` | Hardware FIFO overrun |
| `0x25b8b38` | `QR SCANNER STATUS CONFIG` | Configuration change event |
| `0x25b8b58` | `QR SCANNER STATUS ERROR` | Generic error |
| `0x25b8b70` | `QR SCANNER STATUS ERR1` | Error code 1 |
| `0x25b8b88` | `QR SCANNER STATUS ERR2` | Error code 2 |
| `0x25b8ba0` | `QR SCANNER STATUS ERR3` | Error code 3 |
| `0x25b8bb8` | `QR SCANNER STATUS ERR4` | Error code 4 |
| `0x25b8bd0` | `QR SCANNER STATUS ERR5` | Error code 5 |
| `0x25b8be8` | `QR SCANNER STATUS ERR6` | Error code 6 |
| `0x25b8c00` | `QR SCANNER STATUS NO READER` | Scanner disconnected |
### GPIO Antenna Switch (VA `0x950260`)
The QR scanner has a **dual-antenna RF switch** controlled via `gpiochip1`:
| GPIO | Label | Function |
|---|---|---|
| Line 3 | `ant1` | Antenna 1 |
| Line 7 | `ant2` | Antenna 2 |
Control logic:
- `arg=0`: Both antennas off (low)
- `arg=1`: `ant1` high, `ant2` low
- `arg=2`: `ant2` high, `ant1` low
## QR Parsing Classes
### C++ Class Hierarchy
The game uses C++ classes for QR code parsing, identified from RTTI/vtable references at `0x25fe1c0``0x25fe2d0`:
| Class | Offset | Role |
|---|---|---|
| `QrOfflineParser` | `0x25fe1c0` | Parses offline/local QR codes |
| `NetworkQrParser` | `0x25fe218` | Parses network/connectivity QR codes |
| `QrOfflineListener` | `0x25fe2d0` | Listens for offline QR scan events |
| `NetworkDataInterface` | `0x25fe1d8` | Base interface for network data |
| `CommandInterface` | `0x25fe1f0` | Base interface for commands |
| `NetworkData` | `0x25fe208` | Network data container |
### QR Code Format: `REG:T:`
The game uses a **proprietary QR code data format** identified by the prefix `REG:T:` at `0x25dbe30`:
- `REG:T:WPA` (offset `0x25dbe30`) — WiFi configuration QR code
- `REG:T:ETH` (offset `0x25dbe40`) — Ethernet/machine registration QR code
The format extends `REG:T:{TYPE}:{key}:{value}:{key}:{value}:...` with fields parsed programmatically.
### WiFi QR Data Flow
When a `REG:T:WPA` QR code is scanned:
1. `NetworkQrParser` extracts SSID, PSK, and encryption type
2. Validation: `"Not a valid WiFi QR code"` (`0x25a7200`)
3. Game builds `wpa_supplicant` config block:
```
ssid="%s"
scan_ssid=1
key_mgmt=%s (WPA-PSK)
pairwise=%s (TKIP or "CCMP TKIP")
group=%s
psk=%s
```
4. Writes to `/etc/wpa_supplicant.conf` (`0x25dfd68`)
5. Controls WiFi via `wpa_supplicant` control interface:
- `ADD_NETWORK` (`0x25d5688`)
- `SET_NETWORK` (`0x25d5808`)
- `ENABLE_NETWORK %s` (`0x25d5780`)
- `CTRL-EVENT-CONNECTED` (`0x25d57e8`)
- `CTRL-EVENT-DISCONNECTED` (`0x25d57e8`)
6. Control socket: `/var/run/wpa_supplicant/wlan0` (`0x25d5708`)
7. Also uses `/usr/sbin/wpa_cli status` (`0x25e26b8`)
### Stern Insider Connected QR Data Flow
When a Stern Insider Connected login QR code is scanned:
1. `QrOfflineParser` extracts user UUID from QR code
2. Flow: `QERT_SCANNED_USER_ACCOUNT` (`0x25dedd0`) → `ScannedUserAccountUUID` (`0x25dd568`) → `AuthenticatePlayer` (`0x25dd590`)
3. Player UUID is sent to backend via `PLAYER_AUTHENTICATE` (`0x25ecf18`)
4. Audit counters track results:
- `AUD_CONNECTIVITY_USERS_SCANNED` (`0x2594d60`)
- `AUD_CONNECTIVITY_USERS_JOINED` (`0x2594d80`)
- `AUD_CONNECTIVITY_BAD_QR_SCANS` (`0x2594ea8`)
- `AUD_CONNECTIVITY_BAD_USER_SCANS` (`0x2594e88`)
## Exploit Surface Analysis
### High Priority Targets
#### 1. `REG:T:WPA` → wpa_supplicant Command Injection
**Risk**: HIGH. The QR code content is parsed and passed to `wpa_supplicant` control interface commands (`ADD_NETWORK`, `SET_NETWORK`, `ENABLE_NETWORK`). If the SSID or PSK fields are not properly sanitized, an attacker could:
- Inject arbitrary `wpa_cli` or `wpa_supplicant` control interface commands
- Write to `/etc/wpa_supplicant.conf` with crafted content
- Potentially execute shell commands via the control interface
**Evidence**: The game uses `wpa_cli` at `/usr/sbin/wpa_cli status` (`0x25e26b8`). The control socket at `/var/run/wpa_supplicant/wlan0` (`0x25d5708`) accepts textual commands. SSID and PSK are inserted into format strings (`ssid="%s"`, `psk="%s"`) — if these use `sprintf`/`snprintf` without length limits, buffer overflow is possible.
#### 2. Serial Bridge Buffer Handling
**Risk**: MEDIUM-HIGH. The bridge protocol uses a **256-byte buffer** with a **1000 ms timeout**. The frame format uses STX/ETX/ESC framing with checksum. Potential issues:
- Buffer overflow if a message larger than 256 bytes is sent (the `read()` call reads up to 256 bytes, but the parser accumulates across multiple reads)
- Escape sequence mishandling (ESC `0x1B` sets bit 0x80 on next byte — crafted sequences could cause state confusion)
- Checksum bypass (the checksum is a simple sum complement)
#### 3. RapidJSON Parsing of QR Content
**Risk**: MEDIUM. The game uses **RapidJSON** library to parse QR-related data. Failure messages:
- `Failed to parse JSON.` (`0x259e9e0`)
- `INVALID JSON: expected "achievement_descriptors"` (`0x25dff48`)
- `INVALID JSON: expected "machine"` (`0x25e0088`)
- `INVALID JSON: failed to parse "machine"` (`0x25e00b0`)
RapidJSON assertion failures at `0x25d6348``0x25d6f20` (internal type checks). If assertions are compiled in (debug build), malformed JSON can crash the process. If assertions are compiled out, type confusion bugs may be exploitable.
#### 4. `qrCodeText` Buffer Handling
**Risk**: MEDIUM. The `qrCodeText` field is a raw string field that gets encoded into QR code data. If the text is used in `sprintf`/`snprintf` for display or file path construction without proper bounds checking, it could be a buffer overflow vector. QR codes are saved to `/connectivity/qr_code_storage/%d.bmp` using a numeric index — if the index is attacker-controlled, format string injection via `%d` is possible.
#### 5. Nodebus Secure Protocol
**Risk**: MEDIUM. The netbridge emulator (`netbridge_emu.py`) documents that secure nodebus requests (command `0x11`) use XOR encryption with session key rotation when `wire_address & 0xC0 == 0xC0`. If the encryption is weak or the session key derivation is predictable, a physical attacker with nodebus access could forge QR scan results.
### Medium Priority Targets
#### 6. GPIO Antenna Bypass
The dual-antenna system uses GPIO lines 3 and 7 on `gpiochip1`. If the antenna selection logic can be influenced by QR scan data, an attacker could potentially read from or interfere with the RF path.
#### 7. Audit Counter Manipulation
Audit counters (`AUD_CONNECTIVITY_BAD_QR_SCANS`, `AUD_CONNECTIVITY_USERS_SCANNED`, etc.) are tracked in shared memory or audit logs. If the QR parser crashes before incrementing error counters, brute-force scanning of QR codes could go undetected.
#### 8. Node4 Firmware Update Path
The `node4` bridge board has an LPC812/LPC1124 MCU that can be firmware-updated over the nodebus. If the update path is triggered by a crafted nodebus message, an attacker could flash malicious firmware that intercepts or modifies QR scan data.
### Low Priority / Design Notes
#### 9. `/tmp/qrx_.bmp` Race Condition
The QR code BMP is written to `/tmp/qrx_.bmp` — a predictable path in a world-writable directory. A local attacker could replace the QR code image between generation and display, but this requires local code execution on the Raspberry Pi.
#### 10. Pre-Generated QR Code Assets
The game includes pre-generated QR code images:
- `assets/nuk/images/Connectivity/QrCodes/quick_registration_qr_code.png`
- `assets/nuk/images/Connectivity/Icons/ScanQR/ScanQR.png`
These are imported into Godot but have **no script or scene references** — they are orphaned assets. The `quick_registration_qr_code.png` is a static QR code that may encode a fixed URL or machine identifier.
## Audit Counter Strings
| Offset | String | Purpose |
|---|---|---|
| `0x2594d60` | `AUD_CONNECTIVITY_USERS_SCANNED` | Users scanned via QR |
| `0x2594d80` | `AUD_CONNECTIVITY_USERS_JOINED` | Users joined via QR |
| `0x2594e88` | `AUD_CONNECTIVITY_BAD_USER_SCANS` | Failed user QR scans |
| `0x2594ea8` | `AUD_CONNECTIVITY_BAD_QR_SCANS` | Failed QR scans overall |
| `0x2594ec8` | `AUD_CONNECTIVITY_NET_CONNECTION_BAD_ACTION` | Bad network action |
| `0x25964b0` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCAN_STARTED` | In-game scan started |
| `0x25964e0` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCANNED` | In-game scan completed |
| `0x2596508` | `AUD_RULE_AREA_DISCOVER_POKEMON_SCAN_FAILED` | In-game scan failed |
## Godot UI Classes
| Class Name | Offset | Role |
|---|---|---|
| `NetPlayQrCode` | `0x25db058` | QR code display in network play |
| `NetWrongQrOverlay` | `0x25d9d20` | Error overlay for invalid QR |
| `MenuPageQrAdjustments` | `0x25ff0e8` | Menu page for QR adjustments |
| `QrRC` | `0x280770f` | QR resource class (abbreviated) |
## Diagnostic Messages
| Offset | String |
|---|---|
| `0x25dd3e8` | `Check QR scanner RJ45 nodebus cable` |
| `0x25dd410` | `Check QR scanner ribbon cable` |
| `0x25a85e0` | `Pull Interlock Switch To Power QR Scanner` |
| `0x25a5528` | `This login QR code is no longer valid` |
| `0x25a5578` | `This machine is not registered with Insider Connected` |
## Key Differences from conagent
The QR code system is **entirely separate** from `conagent`:
| Aspect | conagent | QR Scanner (game binary) |
|---|---|---|
| Binary | `/games/pokemon_pro/conagent` | `/games/pokemon_pro/game` |
| Role | Backend connectivity agent | Main game process |
| QR code gen | None | Uses `qrcodegen` library |
| QR code scan | None | Via hardware scanner + node4 bridge |
| WiFi config | No | Yes, via `wpa_supplicant` |
| User auth | API token/device auth | QR-based player UUID scan |
## Follow-up Targets
1. **Binary-level analysis**: Use Ghidra to decompile:
- `QrOfflineParser::parse` (find via vtable xref from `0x25fe1c0`)
- `NetworkQrParser::parse` (find via vtable xref from `0x25fe218`)
- The `REG:T:WPA` string splitting and SSID/PSK extraction code
- The `wpa_cli` control interface invocation site
- The serial bridge read/parse loop at VA `0x94b9d0`
2. **Dynamic testing**: If hardware or emulation is available:
- Fuzz the serial bridge protocol with crafted STX/ETX/ESC frames
- Test `REG:T:WPA` with SSID/PSK containing shell metacharacters
- Test `REG:T:WPA` with oversized SSID (>256 bytes) for buffer overflow
- Test RapidJSON parsing with malformed JSON in QR code content
3. **Node4 firmware analysis**: Disassemble `node4-LPC812-1_33_0.hex` and `node4-LPC1124_303-1_33_0.hex` as ARM Thumb to:
- Recover the UART baud rate configuration
- Understand the scanner module command passthrough protocol
- Verify the firmware update mechanism
4. **Physical inspection**: If hardware is available:
- Identify the scanner module model from the PCB
- Check if the scanner module has its own debug/firmware update port
- Verify the baud rate and protocol between node4 MCU and scanner module
5. **Nodebus security audit**: Verify the XOR encryption scheme for secure nodebus commands and assess whether it can be bypassed to inject forged QR scan results.

File diff suppressed because one or more lines are too long

193
vpx-table-recreation.md Normal file
View File

@@ -0,0 +1,193 @@
# Pokemon Pro VPX Clean Rebuild
## Status
The active Visual Pinball X implementation is the from-scratch Stern Pokemon
Pro rebuild under `emulation/vpx-rebuild/`. Its local assembled output is
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx`; the default native cabinet
launcher builds and selects that file through
`emulation/scripts/run-playable-macos.sh`.
`emulation/vpx/PokemonPro_Greybox.vpx` is deprecated historical work. The clean
rebuild generator does not inspect or write it, and it is not a generator
input, geometry source, build dependency, or runtime default. The rebuild also
does not import the legacy `emulation/vpx/layout.json`, script, materials,
models, or coordinates.
The clean table targets the **Pro** edition only. VPX owns balls, contacts,
collision, mechanical motion, rendering, and mechanical sound. The genuine
game running in QEMU remains authoritative for rules, scoring, display, music,
speech, lamp levels, and ordered mechanism-driver pulses. The complete native
cabinet path is documented in `native-playable-table.md`.
## Evidence and edition scope
The design basis and source URLs are recorded in
`emulation/vpx-rebuild/design/REFERENCE.md`. The official product-family page
provided for edition verification is
<https://www.sternpinball.com/game/pokemon/>. Primary evidence is:
- Stern's official Pokemon Pro product photography and feature information;
- the official Pokemon Pro service and game manual, preserved locally as
`emulation/vpx/reference/Pokemon_Pro_web.pdf` with SHA-256
`50a1906b5c609cd3a84970b6a6e9b0be839b4d0a588d8b27361936ab6537d6bb`;
- Stern's Pro/Premium/LE feature matrix and official overview/gameplay video;
and
- the extracted target-root Pro locator
`/games/pokemon_pro/assets/nuk/images/Test/pokemon_playfield_pro.png`.
The 451 by 998 locator is registered to a 952 by 2115 VPX-unit playfield. It is
used for XY placement, while official manual assemblies and photographs
constrain paths and mechanism identities. The table implements the Pro layout:
two lower flippers and slings, dual inlanes/outlanes, right shooter lane,
Bulbasaur outer-left orbit, Squirtle left ramp, Pikachu right ramp, Charmander
right orbit/spinner, two Tall Grass pops, six BATTLE targets, motorized Meowth,
the Pokedex captive/Newton-ball area, Town scoop/VUK, static Pro Poke Ball with
stop post, and the Pro Psyduck stand-up target.
Premium/LE-only moving Poke Ball, animatronic Pikachu, arena magnet, whirlpool,
and Psyduck scoop behavior are excluded. Secondary photographs are useful for
cross-checking, but official Stern evidence controls when sources differ.
## Sources of truth and generated artifacts
The rebuild is generator-owned. Its maintained inputs are:
| Path | Role |
| --- | --- |
| `emulation/vpx-rebuild/design/geometry.json` | Clean-room XY/Z, collision, mechanism, and physics design with evidence/provenance fields. |
| `emulation/vpx-rebuild/design/lights.json` | Independent trace of 113 logical Pro lamp locations and exact component bindings. |
| `emulation/playfield-contract/pokemon-pro-hardware.json` | Geometry-free switch, sensor, cabinet, driver, and lamp contract. |
| `emulation/vpx-rebuild/script/PokemonPro_Rebuild.vbs.in` | Table behavior and SPIKE 3 integration template. |
| `emulation/vpx-rebuild/assets/` | Registered private playfield image plus original procedural models and mechanical audio. |
| `emulation/vpx-rebuild/tools/generate_table.py` | Deterministic unpacked-VPX source generator. |
`emulation/vpx-rebuild/source/` is generated and must not be hand-edited.
`emulation/vpx-rebuild/dist/PokemonPro_Rebuild.vpx` and its `.vbs` sidecar are
local build outputs. `emulation/vpx-rebuild/qa/` contains the validation report,
round-trip extraction, overlays, captures, and runtime logs. The final verified
2026-07-20 distribution is 5,902,336 bytes with SHA-256
`ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`;
the same value is recorded in `qa/rebuild-validation.json` and the matching
runtime-smoke log.
## Physical and visual implementation
The generator constructs native VPX walls, rails, posts, rubbers, flippers,
slings, targets, spinner, gates, kickers, bumpers, ramps, wireforms, plunger,
lights, and procedural models. Major retained-ball mechanisms have explicit
physical ownership: the Town VUK owns its captured ball, the right-ramp hold
kicker retains its ball until the Pikachu post driver releases it, and the
Pokedex captive chain is bounded by guides, a Newton target, and a one-way
retainer gate. Driver commands are queued briefly for their spatial mechanism
envelope rather than moving an unrelated ball. The validator rejects scripted
ball teleports.
The captive lower/upper optos use circular VPX occupancy volumes and aggregate
`BallCntOver` state across all three retained balls. Their construction kickers
are disabled seed-only loaders. This avoids the custom polygon trigger's z=50
grazing plane, which produced hundreds of thousands of false make/break edges
in the first integrated attempt. The accepted run published one initial state
per opto and no continuing idle chatter.
The current playfield base is deterministically registered from Stern's
official Pro photograph. It preserves authentic visible artwork but is not a
bare playfield scan: photographed rails, toys, shadows, and lamps remain baked
under separately rendered VPX geometry. Provenance and rejected cleanup
experiments are recorded in
`emulation/vpx-rebuild/assets/playfield/PROVENANCE.md`. A licensed, stripped
playfield scan remains necessary before the base art can be called
publication-quality.
The active build has 113 logical lamps expanded through the hardware contract
to 176 component bindings at 150 exact SPIKE addresses, including 30 RGB
logical lamps. Integrated mode consumes exact-address `L` records so separate
red, green, and blue levels are retained; the lossy maximum-component
compatibility getter is not the primary render path. GI, feature lamps, and
flashers are represented by VPX light emitters over the registered trace.
Mechanical and physics audio is an original procedural CC0 bank under
`emulation/vpx-rebuild/assets/sounds/`. Its 69 synthesized source events cover
rolling surfaces and impact, flipper, sling, bumper, target, gate, spinner,
kicker, plunger, drain, and mechanism behavior. The assembled table registers
the variants and aliases needed by the script. It does not copy sound samples
from the game image; QEMU continues to provide licensed music, speech, and game
effects.
## Electrical contract and integration behavior
`emulation/playfield-contract/pokemon-pro-hardware.json` is the canonical
geometry-free mapping. Static validation records 45 switches, 11 sensors, 9
cabinet controls, 13 drivers, and 113 logical lamps. The controller plugin and
netbridge load its compatibility projection directly, while the clean rebuild
uses its richer channel records for VPX object/event bindings.
Physical make/break contacts publish both edges; momentary targets, pops,
slings, and spinner events publish pulses. The Town VUK and right-ramp hold are
real kicker-owned states rather than dummy triggers. Start and Action retain
their installed power-distribution command `0x15` routing, and the service
panel retains its live-corrected CPU-SPI routing.
In `spike3` and `qa-spike3`, one `D` record is one ordered mechanism pulse.
Snapshot driver counts establish reconnect baselines and are never replayed as
new physical actions. Flippers are the documented exception: cabinet edges
move them locally and publish cabinet/EOS state because the live node-board
path does not produce ordinary flipper driver pulses. A disconnected controller
causes transient contacts and energized mechanisms to release; it does not
silently fall back to standalone rules. Reconnect restores stable state and
lamp/driver baselines before new events are accepted.
## Build and run
The deterministic build regenerates the registered playfield, procedural
models, and audio; generates unpacked source; assembles and imports VBS with
`vpxtool`; verifies and round-trip extracts the compound file; compares the
script; and runs the rebuild validator:
```sh
emulation/vpx-rebuild/build.sh
```
Run the table without QEMU for a physics/rendering check:
```sh
emulation/vpx-rebuild/run-standalone.sh
```
Run the native QEMU/VPX cabinet path:
```sh
emulation/scripts/run-playable-macos.sh
```
The default controls are left/right Shift for flippers, Return for
plunger/action, `1` for Start, `5` for coin, and the configured VPX nudge keys.
The QEMU window relays the same cabinet and service controls through the
hardware contract.
## Validation record and remaining acceptance
The following claims have different evidence boundaries and must not be
collapsed into one "playable" result:
| Layer | Current evidence | What it does not prove |
| --- | --- | --- |
| Hardware contract | `python3 emulation/playfield-contract/validate_contract.py --json` passes schema, counts, routes, evidence, consumer loading, and generated-header parity. | Live bridge traffic, rendered lamps, mechanism motion, or gameplay. |
| Generated table | `emulation/vpx-rebuild/build.sh` passes audio validation, `vpxtool verify`, exact VBS round-trip comparison, contract/object checks, collision/clearance checks, lamp routes, and the no-ball-teleport rule; details are in `qa/rebuild-validation.json`. | Real-machine dimensions, every shot trajectory, long-session behavior, or QEMU rules progression. |
| VPX runtime load | `emulation/vpx-rebuild/qa/runtime-smoke.log` records a bounded VPX 10.8.1 `CaptureAttract` pass on the exact current table SHA-256 `ddce5d48033ee3c6e67248910a73897293e8b9c36eee29df164160a93572a30e`: table/VBS load, script compile/start, Metal initialization, two captured frames, clean close, and exit code 0. | Interactive ball/shot coverage, audible-output quality, or every mechanism sequence. |
| Integrated cabinet | The 2026-07-20 native run reached `Playable cabinet connected` with QEMU authoritative, VPX physics synchronized, 392 lamp updates, 7 LED frames, 1 driver pulse, stable captive contacts, and QEMU-keyboard coin/Start press-release records. Shutdown left the bridge disconnected and the QEMU monitor stopped. Plugin/netbridge suites also cover reconnect baselines and ordered delivery. | A complete normal game, multiball, every mechanism/shot, or long-session performance. |
| Physical accuracy | Official locator/manual/photo registration supports edition, identities, and top-down placement. | Measured ramp Z, clearances, coil forces, friction/restitution, toy travel, or production-hardware timing. |
The next acceptance gate is a complete normal game on this final build,
including trough serve, launch, scoring shots, retained-ball mechanisms,
drain/next ball, and game over, followed by restart/inventory checks. A
multiball sequence, every full ramp/orbit trajectory,
60-minute soak, frame-time percentiles, and direct comparison with a physical
Pokemon Pro remain open. In particular, Meowth crank motion and Pikachu post
travel are evidence-backed simulations whose production-hardware direction,
stroke, and timing still need direct confirmation.
The official photograph, Pokemon/Stern character imagery, service manual, and
assembled private-use table are assessment artifacts. Do not publish them
without the relevant rights. The procedural mechanical audio is CC0, but that
does not make the complete table redistributable.