Playable table (hinge-motor flippers, plunger, bumper, drain) plus a headless probe harness that measured, per engine: tunneling of a fast ball through a 1 cm wall with CCD off/on, and hinge-motor drive. Findings in godot/README.md: Box3D never tunnels even with CCD off, but its hinge motor sign is inverted vs Jolt/GodotPhysics, and Jolt blows through angular limits that Box3D holds. Ships a from-source macOS arm64 libgodot-box3d.dylib (upstream has no releases). Also gitignore the remaining Microsoft media at repo root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
4.3 KiB
Markdown
78 lines
4.3 KiB
Markdown
# macrosoft3dpinball
|
||
|
||
`3D Pinball for Windows – Space Cadet` on macOS. Two halves:
|
||
|
||
- The vendored decompilation (below) — the faithful original.
|
||
- `godot/` — a Godot 4.7 pinball testbed whose real job is A/B-ing 3D physics
|
||
backends (Box3D vs Jolt vs GodotPhysics) on CCD, hinge-motor flippers, and joint
|
||
limits. See `godot/README.md` for the probe harness and current findings (Box3D
|
||
never tunnels with CCD off but has an inverted hinge-motor sign vs Jolt).
|
||
|
||
Vendored copy of
|
||
[k4zmu2a/SpaceCadetPinball](https://github.com/k4zmu2a/SpaceCadetPinball) (MIT) at upstream
|
||
commit `cb9b7b8`, not a submodule — patch it in place, pull upstream by re-vendoring.
|
||
|
||
## Build (Apple Silicon)
|
||
|
||
```bash
|
||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||
-DCMAKE_PREFIX_PATH="$(brew --prefix sdl2);$(brew --prefix sdl2_mixer)"
|
||
cmake --build build -j8
|
||
```
|
||
|
||
Binary lands in `bin/SpaceCadetPinball`. Needs `brew install sdl2 sdl2_mixer`.
|
||
|
||
Do **not** use upstream `build-mac-app.sh` — it downloads SDL2 `.framework` DMGs and builds a
|
||
signed universal `.app`+`.dmg`. Unnecessary here; brew SDL2 is already installed.
|
||
|
||
## Gotchas
|
||
|
||
- `CMakeLists.txt` line ~27 upstream hard-set `CMAKE_OSX_ARCHITECTURES "arm64;x86_64"`, which
|
||
silently overrode the `-D` flag the README tells you to pass. Universal linking then fails
|
||
because brew SDL2 is arm64-only. Patched to only set the default when not given on the
|
||
command line. Re-apply this when re-vendoring upstream.
|
||
- Homebrew resolves `sdl2` to `sdl2-compat` (SDL2 API on top of SDL3). Links and runs fine.
|
||
- Deployment target is 10.11, so the link emits "built for newer version 26.0" warnings against
|
||
the brew dylibs. Harmless.
|
||
|
||
## Game data (not in this repo)
|
||
|
||
The engine ships no assets. It needs the original data files, which are Microsoft's and are
|
||
**not redistributable** — supply them from your own media (Windows XP or earlier install disc
|
||
for `PINBALL.DAT`; the retail `Full Tilt! Pinball` CD for `CADET.DAT`). `.DAT`/`.MID` are
|
||
gitignored so they never land in a commit.
|
||
|
||
Drop them in any of these; first match wins in the order `CADET.DAT`, `PINBALL.DAT`, `DEMO.DAT`:
|
||
|
||
- the working directory, or `bin/` next to the binary
|
||
- `~/Library/Application Support/SpaceCadetPinball/` ← preferred, keeps the repo clean
|
||
|
||
Without them the binary exits with "Could not load game data" and lists these paths.
|
||
|
||
## The other two Full Tilt tables (Dragon's Keep, Pirate's Cove)
|
||
|
||
The retail CD carries `DRAGON.DAT` and `PIRATES.DAT` alongside `CADET.DAT`. **They do not play, and
|
||
making them play is a months-long reverse-engineering project per table — not a loader fix.**
|
||
Investigated 2026-07-28; evidence, so nobody re-derives it:
|
||
|
||
- The container format is shared (`PARTOUT(4.0)RESOURCE`) and all three tables use the same five
|
||
component type ids (200, 201, 202, 300, 400). So the DAT *parses* fine. That is the easy half.
|
||
- Only ~35 of Dragon's 278 named components and ~36 of Pirate's 355 match Cadet's names. The
|
||
88-entry `control::score_components` table in `control.cpp` binds behaviour to those names, and
|
||
`control.cpp` is 4600 lines of Cadet-specific rules (missions, ranks, wormholes, Gravity Well).
|
||
Each table needs its own equivalent written from scratch.
|
||
- Both tables use float attributes the engine has no semantics for: `409`, `1306`, `1406`,
|
||
`1600`–`1604` (plus `604`, `704`–`706` for Dragon, `1405` for Pirate). These are hard errors, not
|
||
missing-value errors — e.g. `loader::kicker` accepts only 401–406 and calls `error(10, 20)` on
|
||
anything else. Dragon's kickers carry `409`, meaning unknown.
|
||
- Upstream will not do it. Maintainer k4zmu2a, issue #148: the effort is "comparable to doing the
|
||
whole process all over again, once per table", and issue #20: "I most likely will not be adding
|
||
support for DRAGON and PIRATE tables." Cadet was reversed with the aid of the *public PDB symbols*
|
||
shipped for XP's `pinball.exe`; no equivalent symbols exist for the Full Tilt table binaries.
|
||
Four issues have requested it (#20, #148, #160, #256); none has ever produced a PR.
|
||
|
||
If someone does take it on, `tools/datdump.py` dumps and diffs DAT structure, and
|
||
[DatPartoutExplorer](https://github.com/belaw/DatPartoutExplorer) has a `Resource Identifiers.txt`
|
||
mapping attribute ids to object types — including entries flagged as unused in Cadet, which is the
|
||
obvious first place to look up what `1600`–`1604` mean.
|