Use the exact record 148 spawn point and the reconstructed 0.8 * 3800 millipixel velocity for ball slot two. This removes the guessed shooter-lane launch and keeps the multiball lifecycle tied to the recovered effect-seven path. Test Plan: - cargo test --all-targets - cargo clippy --all-targets --all-features -- -D warnings - rumdl check CHANGELOG.md RECONSTRUCTION.md README.md - git diff --check
90 lines
11 KiB
Markdown
90 lines
11 KiB
Markdown
# Reconstruction ledger
|
|
|
|
## Source artifacts
|
|
|
|
The reconstruction was derived from the preserved files under `../original/`.
|
|
The primary program fingerprint is:
|
|
|
|
```text
|
|
a9022f1894e3e6e21fc42e8f6c932f7c549ca77f63aaa0c488bb9d55d9d0174c TDKPIN.EXE
|
|
f1d9ac980c7bfba5dc53eaa9e7cb2c3cd9b82f879ee8962ad40bf863d641bb49 MMTIMER.DLL
|
|
```
|
|
|
|
The Ghidra recovery covers all 301 named NE exports plus 62 internal function
|
|
entries. Its raw output and coverage tables remain beside the binary, so later
|
|
work can be checked against the same evidence rather than against this Rust
|
|
implementation.
|
|
|
|
## Coverage by subsystem
|
|
|
|
| Subsystem | Rust status | Evidence and boundary |
|
|
| --- | --- | --- |
|
|
| Artwork | Exact | All 34 custom DIB images, three standard bitmaps, icon, and palette derivatives are preserved in `assets/original/`. The game uses the original 640x460 table, loading, help, ball, wheel, robot, plunger, media, and diamond frames. |
|
|
| Audio | Exact samples and recovered dispatch | All 16 mono PCM WAV resources are embedded unchanged. Playback is emitted at the reconstructed call sites, including multi-sound bank completions and nudge-then-tilt ordering. WAV 2022 is loaded by the original generic resource loop but has no playback call and is therefore never played by Rust. |
|
|
| Help and languages | Exact | Original resource images 1001-1005 are displayed directly. |
|
|
| Playfield collision layout | Recovered | All 109 active type-2 line objects and 40 static active type-1 circles are transcribed from the original 175-object registration table. The registration routine converts its sideways inputs with `screen = (y, x - 20)` and accumulates explicitly relative objects. Type-2 records retain every recovered Real48 normal/tangent response pair and registered one-sided orientation. Type-1 records retain their swept-circle radius, radial rebound, tangent coupling, and bumper kick. Each flipper uses its exact two line records plus moving tip circle in both positions. Moving-flipper contact ports `1000:7ed9` rather than fitting live samples: delta-specific pivots/edges, integer cross gates, radial/penetration calculations, response-record gain, and position/velocity publication are tested against all four C harness directions and the raised release geometry. Object 174 is overwritten with the live first ball and Rust handles its ball-to-ball role directly. |
|
|
| Ball launcher and nudge input | Recovered | The initial 32-bit fixed-point coordinates decode to `(325, 413)` in the right shooter lane. Each Down keydown subtracts `15*50 = 750` millipixels, release subtracts another `15*100 = 1500`, and the result follows the recovered randomized `-3800` lower and `-2280` weak upper clamp branches. The ten decoration frames use the same strict 750-millipixel thresholds. Left Shift and keypad 3 apply their directional `(50-Random(20))*15` impulses; Space uses the recovered Real48 horizontal factor and `(60-Random(20))*15` vertical impulse. Each nudge adds 25 to the wrapping 16-bit tilt counter, compares it with `30+Random(10)`, and the detail timer decrements a nonzero counter once per callback. |
|
|
| Physics arithmetic | Recovered Real48 core | Production movement uses the original 10 ms millipixel substep, `+15` vertical acceleration, Real48 `3800/speed` clamp, type-2 distance/cross gates, type-1 midpoint normal, surface-distance candidate ordering, persistent Real48 spin, and the common impulse response. Dynamic records 174/175 transfer normal impulse to the other ball before applying `normal_velocity-1000` to the moving ball; effect seven spawns record/slot 2 from record 148 coordinates `(17000,23000)` with exact `(0,3040)` velocity. The original Borland seed update, high-word `Random(n)`, and normalized Real48 random register result drive launcher variation, effects, claw terminals, magnetic fields, and trigger response. Type-4 triggers retain a separate transient entry flag; type-3 captures retain per-player 16-bit contact words, deep-inside pull/hold progression to age 300, first-contact sound, and `99`/`2` completion sentinels. Zero-spin C harnesses and retained-spin Wine probes are tested separately rather than conflated. |
|
|
| Rules | Recovered gameplay paths | Player count, controls, the five three-line bumper-value groups, four three-line TDK-diamond groups, five doubling-value lock holes, wheel-reset target, seven-way effect selector/consumer including multiball, permanent double scoring, and four exact media/extra-ball thresholds follow original help/code paths, globals, and object flags. The ninth diamond pays the original 24,464 completion value; the following completed bank enables double scoring, and later completions add 100,000 to the per-player secondary score. Bumpers 51-53 and targets 140-147/150-152 use the original 5/20/10 active-callback countdowns and exact overlay-A render rectangles. Record 121 runs the six-callback DAT600 target rotation with exact 91x90 frames/target points and rotates the five contact/item values at state 6. Completing all five lock holes suspends physics for the full 281-callback panel animation, clears contact/item state at the recovered boundaries, and uses the exact WAVE 2013/2012/stop sequence. Turn changes mirror the original save/load of all 175 collision record states: wheel/top targets, active/contact slots, selected effect, and multiball readiness remain attached to their player. Claw contact and all initially active type-4 targets use recovered records. The top three targets score 500 each and independently enable the left, center, or right magnetic field record; each field pulls the ball upward until it exits and then deactivates. The claw state machine and release table have live differential coverage for all four random terminals. |
|
|
| Numeric scoring | Recovered gameplay values | Static scores come from the initialized 175-object ledger. Dynamic bumper progression, target-bank completion, diamond awards, 10k-160k lock bonuses, 310k transfer, six effect values, multiball mode, and all four media thresholds are transcribed from `1000:b476`, `1000:c4e1`, `1000:bc36`, and live state probes. Lock and effect awards share the original per-player secondary score and display multiplier; the fifth hole transfers and clears it, increments the multiplier, and grants the recovered ball award. Score mutation uses the original 32-bit wrapping behavior, and each add operation can advance at most one media threshold. |
|
|
| High scores | Recovered visible flow; portable storage | The original 276-byte table is decoded as ten `IWIK`-XOR-obfuscated little-endian scores plus ten 22-byte names. Each player is checked immediately when their own last ball is lost; qualifying scores use the original signed-high/unsigned-low comparison and a `TDK Pinball Player`-prefilled name screen before the table is shown and play resumes. Persisted updates use portable JSON rather than rewriting the Win16 file. |
|
|
| Configuration | Behaviorally compatible | Sound, language, and five detail levels are retained. Storage moves from a local Win16 INI file to the platform user-data directory. |
|
|
| Windows UI shell | Original gameplay surface with native host shell | Win16 window ownership and GDI calls are replaced by a fixed native window, but the visible game, idle table, help resources, automatic per-player high-score flow, original keys, and 640x460 logical pixels are retained. F2/F3 remain unassigned as in normal original operation; optional portable settings and table viewers use F10/F9 and do not replace gameplay input. |
|
|
|
|
## Extracted asset inventory
|
|
|
|
`assets/original/` contains 69 files used or preserved by the modern project:
|
|
|
|
- 40 image files, including all gameplay frames and five localized help pages;
|
|
- 16 original WAV resources;
|
|
- six decoded dialog descriptions;
|
|
- the original icon in ICO and PNG form;
|
|
- palette and version metadata;
|
|
- the original high-score table and INI configuration sample.
|
|
|
|
The parent `original/assets/` directory remains the lossless extraction record,
|
|
including raw resource blobs and the extraction manifest. The Rust copy is the
|
|
decoded, build-ready subset; it does not replace that evidence archive.
|
|
|
|
## Validation levels
|
|
|
|
- Static coverage: all intended decoded visual/audio resources are preserved;
|
|
the recovered static collision layout is represented in Rust.
|
|
- Build coverage: `cargo check`, unit tests, and strict Clippy complete on the
|
|
host. Cross-target checks pass for `x86_64-pc-windows-gnu` and
|
|
`x86_64-apple-darwin`; CI is configured to build and test natively on Linux,
|
|
macOS, and Windows.
|
|
- Runtime coverage: the Linux executable was launched through the real window
|
|
backend, the attract screen was inspected, a game was started, a ball was
|
|
launched, collision scoring was observed, and a rendered frame was captured.
|
|
- Original-runtime comparison: Wine 11.15 staging ran the preserved Win16 NE
|
|
executable. Live millipixel traces cover keypad `+`, repeat-driven charging,
|
|
release, the shooter route, ordinary rail responses, and a controlled
|
|
terminal-18 claw capture and release. The line solver has unit tests against
|
|
exact outer/inner shooter-wall states from those traces.
|
|
- Deterministic mechanics coverage: named launcher, flipper, and all four claw
|
|
terminal scenarios are driven by a 120 Hz validation clock while production
|
|
physics accumulates the recovered 100 Hz substep. Seeded runs use the original
|
|
Borland random stream. Traces include the last collision-object id and can
|
|
export the logical 640x460 render target.
|
|
- End-to-end gameplay coverage: seeded autoplay charges each launcher ball and
|
|
operates both flippers from ball position. The two-minute acceptance run
|
|
covers repeated launches, both flippers, targets, bumpers, lock holes, a claw
|
|
capture/release pair, and drains while checking every state for finite values.
|
|
- Semantic boundary: collision, random, spin, fixed-point state, detail callback
|
|
batching, and flipper publication now use the reconstructed Borland/Win16
|
|
timing model. Native texture drawing replaces Win16 GDI calls while preserving
|
|
the logical 640x460 framebuffer and original resource pixels.
|
|
|
|
## Architecture
|
|
|
|
- `app.rs`: screen flow, controls, rendering, audio dispatch, and overlays;
|
|
- `assets.rs`: compile-time original asset embedding;
|
|
- `game.rs`: fixed-step game state, recovered walls, rules, and scoring;
|
|
- `geometry.rs`: segment/circle collision primitives;
|
|
- `original_physics.rs`: millipixel integration and recovered type-2 responses;
|
|
- `real48.rs`: bit-exact Borland six-byte arithmetic, conversion, and square root;
|
|
- `flipper_physics.rs`: reconstructed moving-flipper geometry and response;
|
|
- `table.rs`: source-traceable collision objects recovered from the Win16 table;
|
|
- `persistence.rs`: platform paths, settings, high scores, and legacy import.
|