build-image/Dockerfile: add bison (VICE's configure needs a yacc-compatible parser generator, same category as the earlier flex fix) and exclude packageRelease/signReleaseBundle from the cache-warm stage's gradlew run, since those always fail there for lack of a keystore that's never baked into the image. vice_jni.c: guard getFrameCount()/setSoundEnabled() with #ifdef HAVE_VICE_SRC like their sibling functions already do — they referenced globals that only exist when VICE is linked in, breaking the documented no-VICE placeholder build. Only surfaced now because the cache-warm stage is the first thing to ever compile this file without VICE. dist.sh: floor the untagged dev-build's versionCode at 1, since the 0.0.0-dev+<sha> placeholder otherwise computes to 0, which Android's Gradle plugin rejects. run-image.sh: wipe generated build artifacts (vice-src, vice-libs, nibtools-src/libs, app/build, .cxx) before each run so it exercises a true from-scratch build like CI does, instead of silently reusing artifacts left over from a previous local run. Also chown the bind-mounted repo back to the host user on exit, since the container runs as root and was otherwise leaving root-owned files behind.
Schwert und Magie on Pebble
A port of the classic German C64 text-adventure RPG series Schwert und Magie (German Design Group, 1989–1992) to the Pebble Time 2 smartwatch.
Architecture
The phone runs VICE 3.8 as a companion app and streams the C64 text screen to the watch. Button presses on the watch flow back to VICE via Bluetooth.
The game
Schwert und Magie is an 8-episode text-adventure / RPG series for the Commodore 64, published in German by boeder-Verlag. Two episodes share one disk:
| Disk | Episodes |
|---|---|
| I | Folge 1: Das geheimnisvolle Kraut · Folge 2: Der unheimliche Tempel |
| II | Folge 3: Das Piratenhaus · Folge 4: Die Burg des Magiers |
| III | Folge 5: Das Haus des Vampirs · Folge 6: Der Turm des Todes |
| IV | Folge 7: Unter Wasser · Folge 8: Insel der Wunder |
The copy-protection scheme asks for a word from the printed Anleitung (manual). The app includes the full manual text with a look-up button (📖) so you can answer the prompt without keeping the paper around.
Repository layout
SchwertUndMagieOnPebbleWatchApp/ Pebble watchapp (C + PebbleKit JS)
SchwertUndMagieOnPebbleCompanionApp/ Android companion app (Kotlin + NDK)
versions/ Original .d64 disk images (4 disks)
docs/ Architecture notes and diagrams
build-image/ Dockerfile for the release build environment
dist/ Build artifacts (gitignored) — see dist.sh
Run ./dist.sh to build release artifacts for both apps in one step:
dist/schwert-und-magie-<version>.{aab,apk,pbw} — the signed Android AAB
(Play Store), APK (sideload), and Pebble .pbw (Rebble / direct install).
Requires the Android release keystore to already be configured
(docs/publish.md §2.1-2.3). The version comes from the current git tag by
default (git tag v1.2.3); see docs/publish.md §4 for pushing that tag to
trigger an automated, containerized build via Gitea Actions instead.
How it works
Three processes cooperate across two devices:
Pebble watch
│ Bluetooth AppMessage
▼
Core for Pebble (on phone) — PebbleKit JS bridge
│ HTTP on 127.0.0.1:8888
▼
Android companion app — NanoHTTPD server
│ JNI
▼
VICE 3.8 (C64 emulator, cross-compiled for ARM64/x86_64)
The watch displays the C64 text screen (40×25 cells) in a scroll view and provides a key wheel for the number keys most used by the game. The companion app renders VICE's 320×200 framebuffer on-screen and also provides a virtual C64 keyboard for direct input.
See docs/architecture.md for the full design.
Requirements
- Android phone running Android 7.0+ (API 24)
- Pebble Time 2 (or any Pebble running firmware 3.x — the
.pbwtargets all SDK 3 platforms) - Core for Pebble installed on the phone (the community Pebble app)
- C64 ROM files —
kernal,basic,chargen,1541— legally obtained from your own C64 or from Cloanto's C64 Forever. The app prompts you to import them on first launch; they are never bundled. - Disk images —
.d64or.g64files for the four game disks. You can import your own or use the in-app Fetch Disks button to download community-preserved dumps from the Internet Archive's C64 Preservation Project (requires nibtools, which is cross-compiled automatically).
Building
Android companion app
cd SchwertUndMagieOnPebbleCompanionApp
./gradlew assembleDebug # debug build
./gradlew assembleRelease # signed release build (requires keystore.properties)
./gradlew installDebug # build + install on connected device
VICE 3.8 and nibtools are cross-compiled automatically the first time — the
Gradle buildVice and buildNibtools tasks unpack the source tarballs from
res/ and build libvice.a / libnibtools.a for ARM64 and x86_64. The NDK
must be installed (Android Studio → SDK Manager → SDK Tools → NDK (Side by
side)).
Pebble watch app
cd SchwertUndMagieOnPebbleWatchApp
pebble build
pebble install --phone <phone-ip>
In headless / CI environments add --vnc to every emulator command.
Getting disk images onto the device
Option A — Fetch Disks button (in-app)
Opens a dialog, downloads all 8 episode disks as .nbz files from the
Internet Archive C64 Preservation Project, and converts them to G64 using
nibtools. Requires a network connection; conversion runs on-device.
Option B — manual copy
Copy .d64 or .g64 files to the app's external files directory via USB or
adb push, matching the names expected by the drawer (e.g. SCHWUM1A.D64).
Hero saves
Hero characters are stored on separate hero disks (up to 3 slots). The app manages these from the drawer — create a new hero disk, load an episode disk, then swap to the hero disk when the game asks for it. A built-in stat editor lets you inspect and edit a saved hero's attributes directly.
Save states
The 💾 button saves or restores a full VICE snapshot (CPU registers, RAM,
VICII, SID, CIA) to one of three slots. Snapshots go through a CPU trap to
ensure register consistency — see docs/architecture.md §4.4.
Publishing
See docs/publish.md for keystore setup, Google Play,
F-Droid, and Rebble submission.
Automated releases (Gitea Actions)
Pushing a tag vX.Y.Z builds both apps in a containerized runner and
publishes a Gitea Release with the versioned artifacts attached — see
docs/publish.md §4. One-time setup:
-
Build the build environment image locally and push it (
./build-image.shthen./upload-image.sh, needsregistry.env— copy fromregistry.env.example). Use./run-image.shin between to sanity-check the image before pushing. -
Register a self-hosted
act_runnerwith a Docker executor. -
Add repo secrets under Settings → Actions → Secrets:
Secret Value RELEASE_KEYSTORE_B64base64 -w0 SchwertUndMagieOnPebbleCompanionApp/release.keystoreRELEASE_KEYSTORE_PROPERTIESfull contents of SchwertUndMagieOnPebbleCompanionApp/keystore.propertiesREGISTRY_USER/REGISTRY_PASSWORDsame as in registry.env, so the runner can pull the private build imageGITEA_TOKENis injected automatically per job — nothing to add for it. -
git tag v1.2.3 && git push origin v1.2.3.
Full details, including runner registration commands, are in
docs/publish.md §4.
License
The watch app and companion app source code in this repository are released under the MIT License.
The Schwert und Magie game content (disk images, manual text) remains the
property of its original authors and is not part of this license. VICE and
nibtools are GPLv2 and are built from source at compile time; their source
tarballs in res/ are not covered by this repository's MIT license.
