Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
132cbc2
fix(extract): use ZipFile instead of ZipInputStream
KleirRampage45 Jun 13, 2026
b729184
fix(extract): use ZipInputStream with ISO-8859-1 charset
KleirRampage45 Jun 13, 2026
0b4c8ae
fix(store): invalidate game scan cache after install completes
KleirRampage45 Jun 13, 2026
13d5eec
fix(workspace): cleanup orphan install dirs on allocateGameDir
KleirRampage45 Jun 14, 2026
f730d06
feat(per-game): add Delete Game button in settings
KleirRampage45 Jun 14, 2026
ae6470f
feat(per-game): use in-app confirmation dialog, refresh home on delete
KleirRampage45 Jun 14, 2026
34d5025
fix(delete): refresh in-memory game list and log removeGame result
KleirRampage45 Jun 14, 2026
8f33398
fix(delete): do not double-dismiss overlay in performDeleteGame
KleirRampage45 Jun 14, 2026
a674389
feat: bring back store-work build dependencies and fix onOpenDetail
KleirRampage45 Jun 14, 2026
b6e89de
chore: add JUnit and kotlin-test deps for unit testing
KleirRampage45 Jun 16, 2026
85c9d87
feat(webgl): add WebglConfigBuilder with unit tests
KleirRampage45 Jun 16, 2026
251f6bf
feat(webgl): prefer WebGL2, fall back to WebGL1, then canvas; tune PI…
KleirRampage45 Jun 16, 2026
9a61905
fix(webgl): correct MZ renderer pick, log real version, ease PIXI ove…
KleirRampage45 Jun 16, 2026
6b42b30
fix(webgl): stop emitting webgl2=1 in the game URL hint
KleirRampage45 Jun 16, 2026
8e5e680
fix(detector): recognise MZ games with index.html at the root
KleirRampage45 Jun 16, 2026
9af1fda
fix(webgl): force window.devicePixelRatio=1 to avoid WebView tile mem…
KleirRampage45 Jun 16, 2026
c872fdb
fix(webgl): disable setOffscreenPreRaster to stop WebView tile memory…
KleirRampage45 Jun 16, 2026
855a9e8
chore(webgl): strip all PIXI-side overrides to bisect black-screen
KleirRampage45 Jun 16, 2026
31754e5
fix(webgl): serve .wasm assets via shouldInterceptRequest; mirror pag…
KleirRampage45 Jun 16, 2026
b504f81
chore(diag): log when the WASM request interceptor fires
KleirRampage45 Jun 16, 2026
496525e
chore(diag): disable webgl-bootstrap.js injection to bisect look-outside
KleirRampage45 Jun 16, 2026
36eb65a
chore(webgl): restore bootstrap; drop diagnostic log; record known is…
KleirRampage45 Jun 16, 2026
393c13a
fix(webgl): use 6-arg WebResourceResponse for .wasm with CORP + CORS
KleirRampage45 Jun 17, 2026
7fe361d
chore(scripts): adb-connect.sh for persistent wireless adb over USB
KleirRampage45 Jun 17, 2026
1ee1691
feat(webgl): serve games via local HTTP with COOP/COEP for cross-orig…
KleirRampage45 Jun 17, 2026
8d55303
fix(webgl): diagnostic shows Android WebView never enables crossOrigi…
KleirRampage45 Jun 18, 2026
1e1c1b4
feat(webgl): swap WASM Effekseer for asm.js runtime (skips shared mem…
KleirRampage45 Jun 18, 2026
c930604
chore: reorganize taste files into subdirectories, update adb port to…
KleirRampage45 Jun 19, 2026
6e73004
feat(easyrpg): recursive project root detection with depth-limited se…
KleirRampage45 Jun 19, 2026
5162b9b
feat(extraction): smart ZIP charset detection (UTF-8/Shift_JIS/ISO-88…
KleirRampage45 Jun 19, 2026
9a711f5
feat(adb): support game: prefix to launch game directly via ADB
KleirRampage45 Jun 19, 2026
566074e
refactor(ui): store grid density control, card resizing, visual polish
KleirRampage45 Jun 19, 2026
0021a31
refactor: modularize MainActivity and GameActivity, add Room, i18n, t…
KleirRampage45 Jun 23, 2026
35b5a7c
chore: add TESTING.md and fix ViewModel collectLatest race condition
KleirRampage45 Jun 23, 2026
a3af08c
feat: multi-step animated onboarding wizard
KleirRampage45 Jun 23, 2026
1a68179
fix: integrate ViewModel with shared deps, sync overlay/settings stat…
KleirRampage45 Jun 26, 2026
a0dfb55
docs: comprehensive bug and feature inventory
KleirRampage45 Jun 26, 2026
75529cd
fix: resume banner, playtime cap, STOP button session sharing
KleirRampage45 Jun 26, 2026
fade08f
fix: grid padding off-center, sort label NEW->ADDED
KleirRampage45 Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .commandcode/patch-haven.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"performance": {
"forceCanvas": true
}
}
5 changes: 5 additions & 0 deletions .commandcode/patch-look-outside.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"performance": {
"forceCanvas": true
}
}
10 changes: 10 additions & 0 deletions .commandcode/taste/code-style/taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# code-style
- Do NOT add emojis to the codebase. Use SVG icons instead. Confidence: 0.80
- All UI views are programmatic Kotlin — no XML layouts. Keep this pattern for all UI work. Confidence: 0.70
- Always include `Co-authored-by: CommandCodeBot <noreply@commandcode.ai>` in commit messages. Confidence: 0.85
- Write commit messages with a clear root-cause → fix → rationale structure, including behavioural-change notes and the trade-offs considered. Confidence: 0.80
- In Android WebView, `.wasm` asset requests fail silently over `file://` URLs in modern Chromium. Intercept `.wasm` requests in `shouldInterceptRequest` and serve the file directly with `application/wasm` MIME to bypass the broken `fetch()` path. Confidence: 0.80
- Mirror page-side `console.log/error/warning` from `WebChromeClient.onConsoleMessage` to a project-specific logcat tag (e.g. `Runestone`) so game-side issues can be debugged without `chrome://inspect`. Confidence: 0.75
- For Android WebView games requiring SharedArrayBuffer / cross-origin-isolation (e.g. Effekseer-based RPG Maker MZ games, shared-memory WASM): serve the game directory over `http://127.0.0.1:0/` (random local port) with `Cross-Origin-Opener-Policy: same-origin`, `Cross-Origin-Embedder-Policy: require-corp`, and `Cross-Origin-Resource-Policy: cross-origin` response headers. `file://` pages cannot be cross-origin-isolated in the system WebView. The `RunnerSettings.useHttpServer` flag controls this — default it to `true` for HTML/WebView engines. Confidence: 0.85
- For local HTTP servers inside the app (used to serve WebView games), bind to `127.0.0.1` only (not `0.0.0.0`), serve exactly one configured root directory, and reject any URL containing `..` path segments. Listen on port `0` (random) so port collisions are impossible. Daemon threads are fine — the activity lifecycle stops the server in `onDestroy`. Confidence: 0.80
- When a `shouldInterceptRequest` branch overrides a response served by another mechanism (e.g. the local HTTP server for the same game), skip the override for the local-server URLs so the proper response (with the right headers) is used. Confidence: 0.75
9 changes: 2 additions & 7 deletions .commandcode/taste/taste.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
[cmd]: https://commandcode.ai/

# workflow
- Do NOT commit changes unless explicitly asked. Make edits but leave them uncommitted. Confidence: 0.85
- Do NOT modify files outside those specified in the task. Only edit the explicitly listed files. Confidence: 0.75
- After making code changes, verify the build with: `./gradlew :app:compileDebugKotlin`. Confidence: 0.85

See [workflow/taste.md](workflow/taste.md)
# code-style
- Do NOT add emojis to the codebase. Use SVG icons instead. Confidence: 0.80
- All UI views are programmatic Kotlin — no XML layouts. Keep this pattern for all UI work. Confidence: 0.70

See [code-style/taste.md](code-style/taste.md)
13 changes: 13 additions & 0 deletions .commandcode/taste/workflow/taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# workflow
- Commit changes as you complete logical units of work (so changes can be rolled back). Only leave uncommitted when the user is still iterating on the current unit. Default to committing; the user prefers multiple rollback points over fewer larger commits. Confidence: 0.95
- Do NOT modify files outside those specified in the task. Only edit the explicitly listed files. Confidence: 0.75
- After making code changes, verify the build with: `./gradlew :app:compileDebugKotlin`. Confidence: 0.85
- Enter plan mode for non-trivial design work so the user can review the plan before implementation begins. Confidence: 0.85
- The user's phone for wireless ADB is at IP `192.168.100.28`. The port rotates per session — known/used values include 5555, 38775, and 46417 — always confirm the current port with the user or `adb devices` before running adb commands. Cache the IP:PORT in `.runestone/.last-adb-ip` after each successful connection. To keep the connection persistent across screen-off / device sleep, run `adb tcpip <port>` once after USB connect — adbd then listens on that port over Wi-Fi in addition to USB, so the wireless session survives display sleep (use `scripts/adb-connect.sh` for the full workflow). Confidence: 0.90
- After code changes the user can test: build the debug APK and install it to the phone via wireless ADB, then provide (1) what to test, (2) expected outcome, and (3) troubleshooting guidance so the user can report back useful information if something fails. Confidence: 0.90
- When the user indicates ADB/device connectivity is already available on their end, attempt to install, launch, logcat, and verify directly via `adb` rather than handing them shell commands. The user prefers the assistant to operate the device when the connection is reachable. Confidence: 0.75
- When writing files into the app's private data dir via `adb shell run-as <pkg>`, scoped storage blocks reading `/sdcard/`. Use stdin piping instead: `cat local.json | adb shell "run-as <pkg> sh -c 'cat > /data/data/<pkg>/files/path/file.json'"`. Confidence: 0.75
- For WebView/HTML game debugging on device, the canonical logcat stream is `adb logcat Runestone:V chromium:V '*:S'` (filtered to Runestone + chromium tags, silencing everything else). Use `-c` first to clear the buffer. Streaming will hold the shell — instruct the user to Ctrl+C when done. Confidence: 0.85
- When investigating a non-obvious bug, document findings in a `known-issues.md`-style doc with sections per issue (Symptom / Root cause / Fix commit / Workaround) as you discover them, not in a single end-of-session pass. The user values this as a long-term debugging reference. Confidence: 0.80
- In the Runestone app, tapping a game card does NOT launch the game directly — it opens a submenu with a "Play" and "Settings" button. The flow is: tap game card → tap the "Play" button in the submenu → game launches. The home screen layout is grid style (not a 3D carousel). When driving the app via `adb shell input tap`, do not assume a single tap on a game card starts the game; perform two sequential taps (card, then the "Play" button at the position shown in the `uiautomator` dump). Confidence: 0.85
- When the user reports a bug and a working version/branch/orientation/config exists, default to porting the working version over reverse-engineering the broken one. The user prefers the simpler, direct reuse of known-good code over deep investigation, complex refactors, or adding debug instrumentation to trace a root cause. Apply this to layouts, code paths, configs, and any case where there is a clearly working reference. Confidence: 0.80
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CI

on:
push:
branches: [develop, master]
pull_request:
branches: [develop]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- uses: gradle/actions/setup-gradle@v4

- name: Decode keystore
run: |
if [ -n "$KEYSTORE_B64" ]; then
echo "$KEYSTORE_B64" | base64 -d > app/keystore.jks
fi
env:
KEYSTORE_B64: ${{ secrets.KEYSTORE_B64 || '' }}

- name: Lint
run: ./gradlew :app:lintDebug

- name: Unit tests
run: ./gradlew :app:testDebugUnitTest

- name: Build debug APK
run: ./gradlew :app:assembleDebug

- name: Build release APK
if: github.ref == 'refs/heads/master'
run: ./gradlew :app:assembleRelease
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD || '' }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS || '' }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD || '' }}

- name: Upload debug APK
uses: actions/upload-artifact@v4
with:
name: runestone-debug
path: app/build/outputs/apk/debug/app-debug.apk

- name: Upload release APK
if: github.ref == 'refs/heads/master'
uses: actions/upload-artifact@v4
with:
name: runestone-release
path: app/build/outputs/apk/release/app-release.apk

- name: Upload test report
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-report
path: app/build/reports/tests/
1 change: 1 addition & 0 deletions .runestone/.last-adb-ip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
192.168.100.28
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ holding all merged work since the v0.6.13 cycle.
- `animTap()` en cada elemento táctil: scale bounce con OvershootInterpolator.
- Sin dependencias externas pesadas — `org.json` para configs, sin Gson/Moshi.
- Commit messages en inglés, descriptivos.
- **Language parity obligatorio.** Cada vez que se añada un texto visible en UI (títulos, botones, labels, diálogos, mensajes, hints, descripciones, notificaciones, tooltips), debe tener traducción en los 3 idiomas soportados: English, Español, Português. Usar `I18n.get(context, "key")` en lugar de strings hardcodeados. NO mergear código con strings sin traducir.

### Arquitectura de Motores

Expand Down
224 changes: 224 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Runestone v0.9 — Testing Checklist

> Probar en dispositivo físico Android 14+ (arm64-v8a)

---

## 1. First Launch (Onboarding)

- [ ] App shows onboarding wizard on first launch (not home screen)
- [ ] Language selection works: English / Español / Português
- [ ] All 3 languages render correctly (no mojibake, no truncated text)
- [ ] Engine toggles enable/disable correctly (mkxp-z, easyrpg, onscripter, renpy, etc.)
- [ ] RAWG API key input accepts text, link opens browser
- [ ] "Install VX Ace RTP" toggle present
- [ ] "START PLAYING" button completes onboarding and transitions to home
- [ ] On second launch, onboarding is skipped (home screen appears directly)
- [ ] Chosen locale persists across app restarts

## 2. Home Screen

- [ ] Installed games appear as cards
- [ ] Empty state: "No games installed" message when no games present
- [ ] Search bar works with debounced filtering (300ms delay)
- [ ] Search clear button (X) resets filter
- [ ] Sort modes cycle correctly: Date Added → Name A-Z → Name Z-A → Recently Played
- [ ] Engine filter: tap to cycle through installed engine types
- [ ] Card layout toggle: 2-column → 3-column → Wide
- [ ] Long-press on card shows inspect overlay
- [ ] RESUME banner appears when a game is paused
- [ ] STOP button on RESUME banner kills the game session
- [ ] Dock bar buttons work: Home, Add, Browse, Manage, Settings

## 3. Game Import (SAF)

- [ ] Tap + on dock → opens folder browser
- [ ] Folder navigation works (drill in, go up, breadcrumbs)
- [ ] Import detection works for:
- [ ] RPG Maker MV (www/index.html + www/data/System.json)
- [ ] RPG Maker MZ (.rmmzproject)
- [ ] RPG Maker VX Ace (.rvproj2 or .rgss3a)
- [ ] RPG Maker VX (.rvproj or scripts.rvdata)
- [ ] RPG Maker XP (.rxproj or scripts.rxdata)
- [ ] EasyRPG 2000/2003 (RPG_RT.exe + .ldb/.lmt)
- [ ] ONScripter (0.txt or nscript.___)
- [ ] Ren'Py (game/ or renpy/ folder)
- [ ] TyranoBuilder (data.ks or first.ks)
- [ ] Flash/SWF (.swf file)
- [ ] Progress bar shows during import
- [ ] Import failure shows error message
- [ ] RTP download dialog appears when VX Ace game imported without RTP
- [ ] Game appears in home screen after successful import

## 4. Game Launch

- [ ] MV game launches in WebView
- [ ] MZ game launches in WebView
- [ ] VX Ace game launches via mkxp-z
- [ ] VX game launches via mkxp-z
- [ ] XP game launches via mkxp-z
- [ ] EasyRPG 2000/2003 game launches
- [ ] ONScripter game launches
- [ ] Ren'Py game launches
- [ ] TyranoBuilder game launches
- [ ] Flash/SWF game launches via Ruffle
- [ ] UNKNOWN engine shows "trying WebView" toast

## 5. Runtime Controls (WebView games)

- [ ] Touch overlay appears over WebView game
- [ ] D-pad works: UP/DOWN/LEFT/RIGHT
- [ ] A/B buttons trigger confirm/cancel
- [ ] Settings button (...) opens runtime menu
- [ ] Home button pauses game and returns to launcher
- [ ] Runtime menu: RESUME returns to game
- [ ] Runtime menu: HOME pauses and goes to launcher
- [ ] Runtime menu: CONTROLS ON/OFF toggles overlay
- [ ] Runtime menu: BASIC/FULL toggles controller preset
- [ ] Runtime menu: PORTRAIT/LANDSCAPE rotates layout
- [ ] Runtime menu: KEYBOARD opens virtual keyboard
- [ ] Layout rotation works (portrait console split, landscape 4:3)
- [ ] Control layout editor (EDIT) allows drag repositioning
- [ ] Control layout persists across game relaunches

## 6. Controller Support

- [ ] Physical gamepad: D-pad navigates home screen
- [ ] Button A = select, Button B = back
- [ ] START = import, SELECT = manage, X = store, Y = filter
- [ ] L1/R1 cycle card layout / sort
- [ ] MODE = settings
- [ ] Controller combo (L2+R2) resumes paused game
- [ ] Controller navigation auto-enables on first controller input

## 7. Store / Catalogue

- [ ] Browse button shows Available Games screen
- [ ] Default catalogue loads (bundled JSON)
- [ ] Game cards show title, engine badge, download button
- [ ] Download starts and shows progress
- [ ] Pause/Resume download works
- [ ] Download completes → extraction starts → game installed
- [ ] Installed badge appears on store cards
- [ ] Refresh button reloads catalogue
- [ ] Add Source: custom catalogue URL
- [ ] Manage Sources: remove sources
- [ ] Provider Settings: switch to public catalogue

## 8. Manage Games

- [ ] Manage screen shows all installed games with storage info
- [ ] Per-game settings: opens PerGameSettingsScreen
- [ ] Hero card cover: pick custom image
- [ ] Metadata: fetch from RAWG (with API key)
- [ ] Metadata: edit title, developer, publisher, genres, year, description
- [ ] Input section: layout mode, hide gamepad, diagonal, X/Y buttons, haptics
- [ ] Controller Profile section: preset selector, L1/R1, L2/R2 toggles
- [ ] Video section: FPS, VSync, integer/smooth scaling, brightness, contrast, gamma
- [ ] Audio section: mute toggles, volume sliders
- [ ] Performance section: threaded rendering, background loading, shadows, particles
- [ ] Fonts section: use game fonts, bold, italic, scale, line spacing
- [ ] Patch install: select ZIP file, applies patches with backup
- [ ] Delete game: Keep Saves / Delete Fully options
- [ ] View Saves: shows save file list
- [ ] Save Actions: sync, backup, restore, export ZIP, import ZIP, view backups

## 9. Settings

- [ ] DISPLAY section: layout mode, UI mode, smooth/integer scaling, text scale, keep screen on
- [ ] GAMEPAD section: hide gamepad, diagonal, haptics, button opacity/scale, mapping
- [ ] AUDIO section: audio extension, emulation
- [ ] RGSS section: dialog logs, ruby18, vsync, frame skip, shaders
- [ ] MV/MZ section: WebGL, canvas, HTTP server, desktop mode
- [ ] HTML section: renderer, quality, scale mode
- [ ] APPLICATION section: Theme toggle (Dark/Light/System)
- [ ] Color Palette: Amber, Emerald, Royal, Crimson, Ocean, Monochrome
- [ ] RAWG API Key input
- [ ] RUNTIME PACKAGES: RTP install button
- [ ] HELP & ABOUT section renders
- [ ] Settings persist across app restart

## 10. Theme System

- [ ] Default theme is Dark
- [ ] Toggle to Light theme in Settings → APPLICATION → Theme
- [ ] Light theme: backgrounds are light/white, text is dark
- [ ] Dark theme: backgrounds are near-black, text is light
- [ ] System mode follows device dark/light setting
- [ ] Theme change applies immediately (overlay rebuilds)
- [ ] All 9 screens use correct theme colors:
- [ ] HomeScreen
- [ ] SettingsScreen
- [ ] AvailableGamesScreen
- [ ] ManageFilesScreen
- [ ] SourcesScreen
- [ ] ProviderSettingsScreen
- [ ] GameFolderBrowserScreen
- [ ] GameDetailOverlay
- [ ] ImportProgressScreen

## 11. i18n / Locale

- [ ] English: all UI strings in English
- [ ] Español: all UI strings in Spanish
- [ ] Português: all UI strings in Portuguese
- [ ] Locale persists after app restart
- [ ] No hardcoded English strings visible when using ES/PT
- [ ] Special characters render correctly (ñ, ç, á, é, í, ó, ú, â, ê, ô, ã, õ)

## 12. Cover Art

- [ ] RAWG cover art appears when API key is set
- [ ] Fallback cover from game files:
- [ ] MV/MZ: www/img/titles1/*.png
- [ ] RGSS: Title.png in game root
- [ ] .rpgmvp decoded correctly
- [ ] Custom cover via per-game settings works
- [ ] Fallback thumbnail is scaled to max 480px

## 13. Play Stats / Session Tracking

- [ ] Play time increments while game is running
- [ ] Play time persists across app restarts
- [ ] Recently Played sort works
- [ ] RESUME banner shows for paused games
- [ ] STOP button on RESUME banner records play time

## 14. RTP Installer

- [ ] VX Ace RTP download triggers when importing a game that needs it
- [ ] Download progress shows percentage
- [ ] Extraction progress shows file count
- [ ] Installed RTP is detected on subsequent imports
- [ ] Innoextract JNI extracts .exe archive correctly

## 15. Error Handling

- [ ] Missing game directory shows toast and returns to home
- [ ] Import failure shows error and does not crash
- [ ] Download failure shows error notification
- [ ] Extraction failure shows error and cleans up temp files
- [ ] Unknown engine gracefully shows "trying WebView"
- [ ] Kill signal from STOP button terminates GameActivity
- [ ] Activity recreation (config change) doesn't crash

## 16. Performance

- [ ] Home screen scroll is smooth (no jank)
- [ ] Game list sorting/filtering is instant (<16ms)
- [ ] Import progress doesn't block UI
- [ ] Download doesn't block UI
- [ ] Splash screen dismisses within 500ms of game scan completion
- [ ] Play stats read doesn't trigger disk I/O (uses in-memory cache)
- [ ] Game size cache read doesn't trigger disk I/O
- [ ] No `runBlocking` on main thread (verify with StrictMode)

## 17. Regression Tests

- [ ] All existing game imports still work (MV, MZ, VX Ace, XP, EasyRPG, ONScripter, Ren'Py)
- [ ] Save/load still works in all engines
- [ ] Audio still works in all engines
- [ ] Controller still works in all engines
- [ ] RTP still works for VX Ace games
- [ ] Store downloads still work
- [ ] Patch system still applies patches
Loading
Loading