Back to Supersonic RC Revive
SOURCE / PINNED RELEASE

Made of little things.

Supersonic RC Revive

Release
1ba42f1ca1d6…
Author-recorded commit
baecad10b1cd…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f

Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tools/README.md
# Original-game extraction tools (dev only)

These tools turn the original Shockwave game in `ref/SuperSonicRC/` into readable
reference material under `ref-out/`. Both folders, and `tools/vendor/`, are gitignored.
The extracted assets are copyrighted originals: use them for local reference only and
never commit or publish them.

## One-command build from a fresh clone

The original game is not stored in Git. On Ubuntu/Debian, the build script downloads
the preserved archive from Internet Archive, verifies its SHA-256 checksum, builds pinned
versions of the two extraction tools, and regenerates `ref-out/game`. It then packs that
folder into `ref-out/SuperSonicRC-original.ssrcpack`, converts the level for Blender
(`ref-out/editor/original.blend` and `level.glb`, see
[docs/level-editing.md](../docs/level-editing.md); skipped without Blender 5.2+, or set
`SSRC_BLENDER` to its path) and creates a local `dist/index.html` with the assets
embedded. Every step is pinned, so rerunning it reproduces the same files byte for byte:

```sh
./tools/build-original-assets.sh
# or, after pnpm is available:
pnpm build:original-assets
```

To make only the side-loadable pack, for example to play the originals on another machine:

```sh
./tools/build-original-assets.sh --pack-only   # or: pnpm build:original-pack
./tools/build-original-assets.sh --reuse --pack-only   # repack an existing ref-out/game
```

`--pack-only` skips `pnpm install` and the napplet build. `--reuse` skips the download and
extraction. The pack is about 1.8 MB, well under the 10 MiB side-load limit. On the other
machine, open any build of the napplet in a host with a file picker (the `fs` domain): the
published one, a release build or `soyli dev`. Choose "Asset pack…" under "Click to Start"
on the title screen, then the file button. If the build's own pack does not load (for
example offline), use the file button in the "Game data" panel instead. Keep the original
pack off Blossom: share it as a file only between your own machines.

The script checks prerequisites but does not install packages or invoke `sudo`. Install
the native dependencies with:

```sh
sudo apt-get update
sudo apt-get install build-essential curl git unzip xxd libboost-dev zlib1g-dev \
  libmpg123-dev ffmpeg python3 python3-pil
```

Without root access, unpack the mpg123 headers locally as "Manual setup" shows, then point
the script at them:

```sh
D=$PWD/tools/vendor/debs/root/usr
CPPFLAGS=-I$D/include LDFLAGS=-L$D/lib/x86_64-linux-gnu ./tools/build-original-assets.sh
```

macromelt requires Rust 1.85 or newer; use [rustup](https://rustup.rs/) when the
distribution package is older. Run `soyli setup` for this project's pinned Node and pnpm
toolchain. The script then runs `pnpm install --frozen-lockfile` itself.

Inputs and tools are pinned for repeatability:

- `SuperSonicRC.zip` from <https://archive.org/download/lego-super-sonic-rc/SuperSonicRC.zip>
  (`sha256:108cd542dad9bc6452d300bd5e83a1dd387aa3944bd594d09938c4d03e5c0850`)
- ProjectorRays commit `6f9bcebf626b43719abe2affcbbcb041d154d666`
- macromelt commit `78f1ad6c5e8ce8b4f9a36dd1a25ca8682f3976e6`, plus the tracked node-user-data patch

`ref/`, `ref-out/`, `tools/vendor/`, and `dist/` are gitignored. The script replaces
`ref/SuperSonicRC/` and `ref-out/` on each run, but reuses a verified archive and existing
pinned tool checkouts. Preserve any experiments in those generated directories elsewhere.

The resulting asset bundle and HTML contain copyrighted originals. They are for local
preservation and development only. **Do not publish the generated napplet.** Ordinary
`pnpm build` and all `soyli build`/`soyli publish` operations continue to use the custom
asset bundle under `src/game/assets-custom`.

## Manual setup

```sh
# Lingo decompiler (needs boost, zlib and mpg123 headers)
git clone https://github.com/ProjectorRays/ProjectorRays tools/vendor/ProjectorRays
git -C tools/vendor/ProjectorRays checkout --detach 6f9bcebf626b43719abe2affcbbcb041d154d666
make -C tools/vendor/ProjectorRays
# Without system mpg123 headers:
#   apt-get download libmpg123-dev && dpkg-deb -x libmpg123-dev_*.deb tools/vendor/debs/root
#   ln -s /usr/lib/x86_64-linux-gnu/libmpg123.so.0 tools/vendor/debs/root/usr/lib/x86_64-linux-gnu/libmpg123.so
#   make -C tools/vendor/ProjectorRays CPPFLAGS=-I$PWD/tools/vendor/debs/root/usr/include \
#     LDFLAGS=-L$PWD/tools/vendor/debs/root/usr/lib/x86_64-linux-gnu

# Shockwave 3D (W3D) -> glTF decoder, plus our node user-data fix
git clone https://github.com/Dilaz/macromelt tools/vendor/macromelt
git -C tools/vendor/macromelt checkout --detach 78f1ad6c5e8ce8b4f9a36dd1a25ca8682f3976e6
git -C tools/vendor/macromelt apply ../../patches/macromelt-node-user-props.patch
cargo build --release --locked --manifest-path tools/vendor/macromelt/Cargo.toml
```

## Manual extraction pipeline

```sh
# 1. Decompile Lingo -> ref-out/decompiled/<movie>/casts/*/*.ls
mkdir -p ref-out/decompiled && cp ref/SuperSonicRC/{game.dcr,GUILibrary.cct,wrapper_SupersonicRC.dir} ref-out/decompiled/
for f in game.dcr GUILibrary.cct wrapper_SupersonicRC.dir; do
  (cd ref-out/decompiled && ../../tools/vendor/ProjectorRays/projectorrays decompile "$f" --dump-scripts)
done

# 2. Named assets -> ref-out/assets/{bitmaps,sounds,3d,text} + manifest.json
python3 tools/extract/extract_assets.py

# 3. Derived collision and lightmap data used by the game-ready bundle
python3 tools/extract/hke.py
python3 tools/extract/lightmap_uvs.py

# 4. 3D cast members -> ref-out/assets/glb/*.glb (Director Z-up units)
mkdir -p ref-out/assets/glb
for f in ref-out/assets/3d/*.w3d; do
  tools/vendor/macromelt/target/release/macromelt "$f" -q --export-glb "ref-out/assets/glb/$(basename "${f%.w3d}").glb"
done

# 5. Game-ready, bundle-compact data -> ref-out/game (needs ffmpeg for the MP3s)
python3 tools/extract/build_level.py
```

## Playing with the originals

Builds bundle `ref-out/game` only on request; everything else gets the custom bundle
(`src/game/assets-custom`, generated by `pnpm assets:build`; see `tools/assets/`).

- `soyli dev`: put `SSRC_ORIGINAL_ASSETS=1` in `.env.local` (gitignored), then restart `soyli dev`.
  soyli runs Vite with a scrubbed environment, so a shell variable never reaches it.
  The file is read only by the dev server and watch builds. `soyli build`/`publish` and
  `pnpm build` keep shipping the custom bundle. After a dev session `dist/` holds the originals;
  `soyli publish` rebuilds before it uploads.
- Plain pnpm: `SSRC_ORIGINAL_ASSETS=1 pnpm build` (or `pnpm dev`).
- Side-load: `pnpm build:original-pack --reuse` packs an existing `ref-out/game` (or, by hand,
  `pnpm assets:pack ref-out/game supersonic-original.ssrcpack --original`). Then use
  "Asset pack…" on the title screen of any build. The game shows the
  "original assets" badge. Keep the file to yourself: it holds the copyrighted originals.

## Notes

- `game.dcr` is Afterburner-compressed. ProjectorRays' `--dump-chunks` writes the decompressed
  chunks, and `extract_assets.py` maps them to cast members through the `KEY*` table.
- Bitmaps are JPEG (`ediM`) or RLE (`BITD`, planar ARGB rows) with an optional RLE `ALFA` alpha plane.
  Sounds are Mac `snd ` resources (16 kHz, 16-bit, big-endian) or Shockwave Audio (MP3 frames behind
  a header).
- W3D model nodes (0x70/0x72) carry a 3ds Max user-properties string (the Havok settings) before the
  matrix. Upstream macromelt skipped it as a 2-byte pad, which left most of this level at identity.
  `patches/macromelt-node-user-props.patch` fixes that.
- Lightmap UVs are not stored in the W3D. They live in the `UV_Lightmap_UVs` movie script as a huge
  Lingo literal (per model: `#lightmap` name plus a UV list), applied at runtime by `lightMapManager`.