• C++ 58.3%
  • Python 21.1%
  • Objective-C++ 14.5%
  • CMake 2.8%
  • Java 1%
  • Other 2.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Casuallynoted bce3825b9b Carry real per-material textures through the pipeline
Textures are referenced only by hash: no tex_ name survives in the binary, and
no resource hash appears as a code constant. The link lives in mesh material
blocks, at +20 within each 160-byte material.

That matters because meshes are genuinely multi-material. The surf shack uses
eight textures, palms need separate frond and bark art, and both towel meshes
share one texture. My hand-written mesh-to-texture table was guesswork and had
real errors -- it pointed palms at tex_PalmTree_01, which does not exist, so
those lookups failed silently and the props were skipped. That was a large
part of why the beach looked empty.

.dsmesh is now v2, carrying a texture hash per material. The renderer binds
textures per part rather than per draw item, and uploads are cached by hash so
shared art loads once. v1 is rejected outright rather than misread: without a
materials block the part count would be read as a material count and every
subsequent field would desynchronise.

99 tests, zero warnings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 00:58:17 -04:00
.forgejo/workflows Run CI tests as a Dockerfile stage; document tile and path model 2026-08-05 23:56:56 -04:00
client Carry real per-material textures through the pipeline 2026-08-06 00:58:17 -04:00
DistantShore.app Track the original bundle; point deployment at casually.cat 2026-08-05 23:38:46 -04:00
re Carry real per-material textures through the pipeline 2026-08-06 00:58:17 -04:00
server Run CI tests as a Dockerfile stage; document tile and path model 2026-08-05 23:56:56 -04:00
.gitignore Add iOS app shell; it builds, launches, and renders 2026-08-06 00:27:03 -04:00
README.md Track the original bundle; point deployment at casually.cat 2026-08-05 23:38:46 -04:00

Distant Shore

A preservation project for Distant Shore (Blimp Pilots, 2009), a networked iPhone game whose servers have long since gone dark. The goal is a client that runs on current phones and a server you can host yourself.

What is here

Path What it is
DistantShore.app/ The original 2009 bundle, the source of truth
re/tools/ Format readers and the asset converter
re/docs/ Protocol and file format specifications
server/ The backend: legacy XML protocol + a modern JSON API
.forgejo/workflows/ CI that publishes the server image

This is a private, self-hosted repository. The original bundle is tracked because it is the irreplaceable input everything else derives from.

Generated directories

re/extracted/, re/assets/, re/decomp/, and re/ghidra/ are gitignored because they are mechanically produced from the bundle and would churn on every tool change. Regenerate them:

python3 re/tools/wad.py DistantShore.app/resources.wad re/extracted
python3 re/tools/convert_assets.py DistantShore.app/resources.wad re/assets

The decompiler output comes from re/tools/DumpDecomp.java run under Ghidra headless; it takes about 30 seconds for all 1,237 functions.

The client is written fresh against the specs in re/docs/ rather than translated from decompiler output, because the original is armv6 with fixed-function OpenGL ES 1.1 — not a usable porting base for arm64 and Metal.

Server

Serves two front ends over one shared data model, so both see the same ocean:

  • /bc/*.php — the original 2009 XML protocol, reproduced from the shipped binary. The original client can talk to this unmodified.
  • /api/v1/* — JSON for the new client.
cd server
docker compose up --build        # http://localhost:8000, docs at /docs

Full detail, configuration, and deployment notes: server/README.md.

Deploying

Pushes to main that touch server/** run the test suite and, if it passes, publish a multi-arch (amd64 + arm64) image to the container registry on git.casually.cat.

To deploy on the NAS, copy server/docker-compose.prod.yml and server/.env.example (as .env) to the host — it pulls the published image, so the host never needs this repo:

docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d

The server is served at https://distantshore.casually.cat. Set HOST_PORT=127.0.0.1:8000 in .env if a reverse proxy terminates TLS in front of it, so the container is not also exposed directly on the LAN.

CI needs one repository secret, REGISTRY_TOKEN — a Forgejo access token with write:package. The runs-on: docker label must match how your runner is registered.

Status

Done — reverse engineering, specs, asset pipeline, and the server (24 tests, image built and exercised end to end).

Next — the remaining gameplay reverse engineering (dsi_bounds, the tile/zone system, and the shell-and-bottle economy), then the C++ engine core, the iOS shell, and the Android port.

Reverse engineering notes

Recovered with Ghidra from the shipped armv6 binary, which ships decrypted (cryptid 0) and retains its symbol names, so the analysis reads in terms of real classes rather than addresses.

Findings are validated rather than assumed. All 56 meshes parse with every declared stride equal to the sum of its components and zero of 19,947 indices out of range; all 108 textures satisfy length == 60 + w*h*channels exactly, and decode to imagery with a mean adjacent-pixel delta of 2.24 against roughly 85 for noise.

The sharp edge in the mesh format, documented in re/docs/formats.md: BpMeshTemplate offsets are file-relative but BpPrimBuffer offsets are relative to the prim buffer's own base. Mixing them yields plausible-looking garbage rather than an obvious failure.

About

Reimplementation of Distant Shore, an early networked iOS game.

Releases

Topics

Metadata

9.8 MiB

Languages

  • C++ 58.3%
  • Python 21.1%
  • Objective-C++ 14.5%
  • CMake 2.8%
  • Java 1%
  • Other 2.3%