# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project goal Port the classic German C64 RPG series **Schwert und Magie** (8 episodes, German Design Group, 1989–1992) to a Pebble Time 2 smartwatch. The phone runs a VICE C64 emulator as a companion app; the watch displays the game and sends button presses back. ## Repository layout ``` SchwertUndMagieOnPebbleWatchApp/ Pebble watchapp (C + PebbleKit JS) SchwertUndMagieOnPebbleCompanionApp/ Android companion app (Kotlin + NDK) versions/ Original .d64 disk images (4 disks, 8 episodes) docs/ Background notes ``` ## Watch app ### Build & install ```bash cd SchwertUndMagieOnPebbleWatchApp pebble build # all platforms pebble install --phone # sideload via Pebble/Core app on the phone pebble logs --phone # stream JS + C app logs ``` In headless / CI environments, add `--vnc` to every emulator command: ```bash pebble install --emulator emery --vnc pebble screenshot --vnc --scale 6 --no-open screenshot.png pebble emu-button --emulator emery --vnc click select ``` ### Key files | File | Purpose | |---|---| | `src/c/SchwertUndMagieOnPebbleFrontend.c` | Single-file C watchapp | | `src/pkjs/index.js` | PebbleKit JS — runs inside Core for Pebble on the phone | | `package.json` | UUID, target platforms, `messageKeys` | ### Communication protocol AppMessage keys (defined in both C and JS): | Key | Direction | Value | |---|---|---| | `0` (TIME) | phone → watch | `"HH:mm:ss"` string (later: C64 framebuffer) | | `1` (COMMAND) | watch → phone | button name string (`"UP"`, `"DOWN"`, `"SELECT"`) | The watch UUID is `8039ba8c-f1e8-4620-838d-650fa35335b7`. ### PebbleKit JS notes (Core for Pebble quirks) - **Use `127.0.0.1` not `localhost`** — Core for Pebble's JS runtime does not resolve `localhost`. - **Use numeric keys in `sendAppMessage`** (`{ 0: value }` not `{ 'TIME': value }`) — symbolic key resolution from `package.json` messageKeys is unreliable with Core for Pebble. - PebbleKit Android (`com.getpebble.android.*`) does **not** work with Core for Pebble; all phone↔watch communication goes through PebbleKit JS + the HTTP server. ## Android companion app ### Build Open `SchwertUndMagieOnPebbleCompanionApp/` in Android Studio, or: ```bash cd SchwertUndMagieOnPebbleCompanionApp ./gradlew assembleDebug ./gradlew installDebug ``` Android SDK is at `/opt/android-sdk`. ### Communication architecture ``` Watch (AppMessage BT) ↕ PebbleKit JS — index.js runs inside Core for Pebble ↕ HTTP on 127.0.0.1:8888 Android companion app — NanoHTTPD server GET /time → {"time":"HH:mm:ss"} GET /key?cmd=UP → logs keystroke ``` `MainActivity` starts both the NanoHTTPD server and the VICE emulator loop thread. All HTTP callbacks marshal to the main thread via `mainHandler`. ### VICE integration (NDK) VICE 3.8 tarball is at `SchwertUndMagieOnPebbleCompanionApp/res/vice-3.8.tar.gz`. **Without VICE built** (first state): `vice_jni.c` fills the framebuffer with a placeholder blue screen; the app builds and runs normally. **VICE compilation is automatic.** The Gradle `buildVice` task runs before every native CMake build. On the first build it: 1. Calls `app/src/main/jni/build_vice.sh` with `$NDK` set from the Android Studio SDK config 2. The script unpacks the tarball, cross-compiles VICE headless for ARM64 and x86_64, and produces `vice-libs//libvice.a` 3. `CMakeLists.txt` auto-detects the library via `EXISTS` and links it in — no manual flags needed The NDK must be installed: **Android Studio → SDK Manager → SDK Tools → NDK (Side by side)**. After the first successful build, subsequent builds skip the VICE compilation step entirely (outputs are up-to-date). **C64 ROM files** (`kernal`, `basic`, `chargen`, `1541`) all ship inside the VICE 3.8 tarball at `res/vice-3.8.tar.gz` and are bundled in the APK under `app/src/main/assets/`. They are copied automatically to the external files dir on first launch — no user action needed. ### Key Kotlin/C files | File | Purpose | |---|---| | `MainActivity.kt` | Emulator loop thread (50 fps), NanoHTTPD server, keyboard event logging | | `C64Engine.kt` | JNI interface to VICE — init, runFrame, getVideoBuffer, injectKey, loadDisk | | `C64DisplayView.kt` | SurfaceView — blits 320×200 ARGB framebuffer, scaled with correct aspect ratio | | `C64KeyboardView.kt` | Multi-touch virtual C64 keyboard; fires `KeyEventListener` on press/release | | `jni/vice_jni.c` | JNI wrapper — custom VICE video canvas writes frames to `g_framebuf[320×200]` | | `jni/CMakeLists.txt` | NDK build; conditionally links `libvice.a` when `HAVE_VICE_SRC=1` | ### Disk images The four original `.d64` disk images are in `versions/`. Copy the relevant image to the same external files directory as the ROMs (via USB or adb), then load via: ```kotlin engine.loadDisk(getExternalFilesDir(null)!!.absolutePath + "/schwert_und_magie_1.d64") ```