Files
schwert_und_magie_on_pebble/CLAUDE.md
T

126 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, 19891992) 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 <phone-ip> # sideload via Pebble/Core app on the phone
pebble logs --phone <phone-ip> # 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/<abi>/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")
```