diff --git a/CLAUDE.md b/CLAUDE.md
index cdb5ed8..0673df2 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -101,21 +101,9 @@ The NDK must be installed: **Android Studio → SDK Manager → SDK Tools → ND
After the first successful build, subsequent builds skip the VICE compilation step entirely (outputs are up-to-date).
-**C64 ROM files** must be copied to the device separately (not redistributable).
-VICE expects files named exactly `kernal`, `basic`, `chargen` (no extensions).
-
-**Via USB file transfer (recommended):** connect the phone, open it in Windows Explorer or
-Android File Transfer (Mac), and copy the files to:
-```
-Android/data/de.ladkau.schwertundmagieonpebblecompanionapp/files/
-```
-
-**Via adb (alternative):**
-```bash
-adb push kernal /sdcard/Android/data/de.ladkau.schwertundmagieonpebblecompanionapp/files/kernal
-adb push basic /sdcard/Android/data/de.ladkau.schwertundmagieonpebblecompanionapp/files/basic
-adb push chargen /sdcard/Android/data/de.ladkau.schwertundmagieonpebblecompanionapp/files/chargen
-```
+**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
diff --git a/SchwertUndMagieOnPebbleCompanionApp/.idea/deploymentTargetSelector.xml b/SchwertUndMagieOnPebbleCompanionApp/.idea/deploymentTargetSelector.xml
index f5f9d3d..f30b5fc 100644
--- a/SchwertUndMagieOnPebbleCompanionApp/.idea/deploymentTargetSelector.xml
+++ b/SchwertUndMagieOnPebbleCompanionApp/.idea/deploymentTargetSelector.xml
@@ -13,6 +13,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SchwertUndMagieOnPebbleCompanionApp/app/.gitignore b/SchwertUndMagieOnPebbleCompanionApp/app/.gitignore
index 42afabf..4218c1e 100644
--- a/SchwertUndMagieOnPebbleCompanionApp/app/.gitignore
+++ b/SchwertUndMagieOnPebbleCompanionApp/app/.gitignore
@@ -1 +1,7 @@
-/build
\ No newline at end of file
+/build
+
+# ROM files extracted from the VICE tarball at build time (see extractViceRoms task)
+/src/main/assets/kernal
+/src/main/assets/basic
+/src/main/assets/chargen
+/src/main/assets/1541
\ No newline at end of file
diff --git a/SchwertUndMagieOnPebbleCompanionApp/app/build.gradle.kts b/SchwertUndMagieOnPebbleCompanionApp/app/build.gradle.kts
index 15daf39..8ec2505 100644
--- a/SchwertUndMagieOnPebbleCompanionApp/app/build.gradle.kts
+++ b/SchwertUndMagieOnPebbleCompanionApp/app/build.gradle.kts
@@ -130,6 +130,48 @@ tasks.whenTaskAdded {
}
}
+// ---------------------------------------------------------------------------
+// ROM extraction task
+//
+// Extracts C64 and drive ROMs from the bundled VICE tarball into the Android
+// assets directory so they are packaged into the APK automatically.
+// Output files are gitignored — the tarball is the single source of truth.
+// ---------------------------------------------------------------------------
+
+val assetsDir = layout.projectDirectory.dir("src/main/assets")
+
+tasks.register("extractViceRoms") {
+ group = "build"
+ description = "Extract C64 and 1541 ROMs from the VICE tarball into assets/"
+
+ from(tarTree(viceTarball.asFile)) {
+ include("vice-3.8/data/C64/kernal-901227-03.bin")
+ include("vice-3.8/data/C64/basic-901226-01.bin")
+ include("vice-3.8/data/C64/chargen-901225-01.bin")
+ include("vice-3.8/data/DRIVES/dos1541-325302-01+901229-05.bin")
+ eachFile {
+ // Flatten the tarball directory structure: place each ROM directly
+ // in assets/ with its short name rather than the versioned filename.
+ relativePath = RelativePath(true, when (name) {
+ "kernal-901227-03.bin" -> "kernal"
+ "basic-901226-01.bin" -> "basic"
+ "chargen-901225-01.bin" -> "chargen"
+ "dos1541-325302-01+901229-05.bin" -> "1541"
+ else -> name
+ })
+ }
+ includeEmptyDirs = false
+ }
+ into(assetsDir)
+}
+
+// Run extractViceRoms before assets are merged into the APK.
+tasks.whenTaskAdded {
+ if (name.startsWith("merge") && name.endsWith("Assets")) {
+ dependsOn("extractViceRoms")
+ }
+}
+
dependencies {
implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.appcompat)
diff --git a/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/C64Engine.kt b/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/C64Engine.kt
index d71aead..611a7dd 100644
--- a/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/C64Engine.kt
+++ b/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/C64Engine.kt
@@ -37,6 +37,9 @@ class C64Engine {
*/
external fun injectKey(keyCode: Int, pressed: Boolean)
+ /** Enable or disable audio output. Disabled by default. */
+ external fun setSoundEnabled(enabled: Boolean)
+
companion object {
init { System.loadLibrary("vice_jni") }
diff --git a/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/MainActivity.kt b/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/MainActivity.kt
index 1d208c4..3d96f19 100644
--- a/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/MainActivity.kt
+++ b/SchwertUndMagieOnPebbleCompanionApp/app/src/main/java/de/ladkau/schwertundmagieonpebblecompanionapp/MainActivity.kt
@@ -11,11 +11,9 @@ import android.util.Log
import android.widget.Button
import android.widget.ScrollView
import android.widget.TextView
-import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
-import androidx.documentfile.provider.DocumentFile
import fi.iki.elonen.NanoHTTPD
import java.io.File
import java.text.SimpleDateFormat
@@ -45,6 +43,7 @@ class MainActivity : AppCompatActivity() {
private val engine = C64Engine()
private var emuThread: Thread? = null
@Volatile private var emuRunning = false
+ private var soundEnabled = false
// ---- disk slots ---------------------------------------------------------
@@ -56,41 +55,6 @@ class MainActivity : AppCompatActivity() {
)
private val episodeButtons = mutableMapOf()
- // ---- ROM import (pick folder — kernal/basic/chargen have no extension) --
-
- private val romNames = listOf("kernal", "basic", "chargen")
-
- private val romDirPickerLauncher: ActivityResultLauncher =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == Activity.RESULT_OK) {
- result.data?.data?.let { treeUri -> importRomDir(treeUri) }
- }
- }
-
- private fun importRomDir(treeUri: Uri) {
- val dir = DocumentFile.fromTreeUri(this, treeUri) ?: run {
- appendLog("Cannot access selected folder")
- return
- }
- val files = dir.listFiles()
- var imported = 0
- for (name in romNames) {
- val file = files.firstOrNull { it.name?.lowercase() == name } ?: run {
- appendLog("Not found: $name")
- continue
- }
- importFileToAssets(file.uri, name)
- appendLog("Imported: $name")
- imported++
- }
- if (imported == romNames.size) {
- appendLog("All ROMs imported — restarting emulator")
- mainHandler.postDelayed({ recreate() }, 400)
- } else {
- appendLog("Only $imported/${romNames.size} ROMs found — check folder contents")
- }
- }
-
// ---- Disk import (multi-select) -----------------------------------------
private val diskPickerLauncher: ActivityResultLauncher =
@@ -126,13 +90,17 @@ class MainActivity : AppCompatActivity() {
display = findViewById(R.id.c64_display)
keyboard = findViewById(R.id.c64_keyboard)
- findViewById