# 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 <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")
```
