Adds app-side support for the ESP32-WROOM-32 Joy-Con bridge over USB
serial. The ESP32 pairs with the Joy-Con via Bluetooth Classic (using
Bluepad32), parses input reports, and streams an 11-byte wire protocol
frame at 60 Hz over the CP2102's USB-CDC serial connection.
ControllerManager now auto-detects known USB-serial chips (CP2102,
CH340, Espressif native USB) by VID/PID, opens the port at 115200 8N1
via usb-serial-for-android, and parses incoming frames into the same
dispatchButton / onAnalogEvent pipeline used by all other controller
paths. Frame format: [0xA5 sync] [seq] [btns_lo] [btns_hi] [dpad hat]
[lx] [ly] [rx] [ry] [flags] [XOR crc8].
Verified stable for 10+ minutes with zero disconnects on the Samsung
Fold 6 — the 8BitDo USB adapter wedged within 2 minutes on the same
phone. Full firmware control eliminates the OTG selective-suspend issue
that plagued the adapter path.
GameScreen: add FLAG_KEEP_SCREEN_ON while a game is active. Keeps the
system out of the deeper sleep states that aggravate OTG selective-suspend
on Samsung, where the 8BitDo adapter otherwise drops every 60-120 seconds.
Cleared on dispose so other screens are unaffected.
docs: drop a design sketch for a DIY Joy-Con bridge built on an original
ESP32 or Raspberry Pi Pico W. Covers the two architectures (fully
wireless via BT Classic host + BLE peripheral, or wired hybrid via
BT Classic host + USB-CDC serial), the Joy-Con init subcommand
requirement, a proposed wire protocol for the serial path, and the
changes we'd make app-side to consume it. Also captures the dongle
rabbit hole we went through so future-us doesn't repeat the same
experiments.
Notes that Bluepad32 already implements BT Classic HID host with
first-class Joy-Con support on original ESP32 and Pico W, which
shrinks the firmware side considerably. Also warns explicitly against
ESP32-S3 for this project — despite being the obvious "newer, better"
ESP32, it is BLE-only and cannot pair with Joy-Cons (BR/EDR-only).
Three independent stability fixes that surfaced while testing the Joy-Con
controller path:
1. Manifest: add keyboard|keyboardHidden|navigation|uiMode to MainActivity's
configChanges. Without these, unplugging the OTG cable mid-game triggered
a navigation/keyboard configuration change that destroyed and recreated
the activity. The new activity instance never re-established the emulator
surface cleanly, leaving a white screen, and Samsung Freecess then froze
the seemingly-idle process.
2. GameCard: cap Coil's image decode at 384px. LibRetro thumbnail PNGs are
often 1500x2000+, which burns ~12 MB of bitmap per card. With 30 visible
tiles that's hundreds of MB and was OOM-ing the app during in-game input
delivery (NewObjectV in nativePollOnce → JNI abort).
3. CoverArtManager: try (World) as a fallback region tag and add a cleaned-
title-plus-common-region cascade. LibRetro stores Super Mario Bros. as
'(World).png' but most US dumps tag the ROM '(U)', so the previous
expansion table only tried (USA)/(USA, Europe) and missed it.
Wires up ControllerManager to handle the 8BitDo USB Wireless Adapter 2 in
PS-Classic mode (Select+Down 3s), which is the only adapter mode the
Samsung Fold 6 kernel actually binds. Adds a Test Controller screen
(reachable from Settings) showing detected input devices, USB host
enumeration, live pressed-button chips, axis readout, and a scrolling raw
event log for debugging.
Per-device fixups for the adapter's PS-Classic identity (Sony 0x054C/0x0CDA):
treat ABS_X/Y as the dpad (no real analog stick), remap BTN_TL2/TR2 to
Select/Start so the Joy-Con's minus/plus buttons reach cores, and remap
BTN_C to FACE_BOTTOM so the NES Joy-Con's bottom face button works as
NES B / SNES B / PS Cross.
Also includes a manifest USB intent filter and a vendor-spec XInput
claim/reader path retained for future XInput-mode adapters, dpad routing
through dispatchButton so buttonMappingManager applies to dpad input, and
a concurrency fix in cover-art fetching (parallelism 8->2 + per-fetch
try/catch) to stop OOM crashes during launch.
Bundles genesis_plus_gx_libretro_android.so (~12 MB) as an asset and
wires up GenesisCore with 3/6-button Genesis mappings (A/B/C/X/Y/Z/
Mode/Start/D-pad) following Genesis Plus GX's default retropad layout.
Add deleteState method to EmulatorCore interface and implement in
Ps1Core and NesCore. Delete button (trash icon) in the in-game
Save/Load menu removes the state file and its screenshot, then
refreshes the slot list.
Replace static console filter with swipeable tabs (All / Favorites /
per-console) and a collapsible search icon in the app bar. Persist
favorites via SharedPreferences keyed by ROM path. Display titles are
now cleaned (region tags stripped) for better UX while raw filenames
are preserved for cover art lookups. Sort games alphabetically and
fetch cover art in parallel (8 concurrent) for faster initial load.
Also fix PS1 controller setup so it works with both new games and save
states: pcsx_rearmed needs retro_set_controller_port_device called at
a specific point inside loadCore (between callbacks and get_system_info)
which FCEUmm crashes on. Added a "pending controller device" mechanism
that the Kotlin core can pre-set before nativeLoadCore. Save states are
now loaded BEFORE start() so the game's first frame already has the
restored state, avoiding the "controller removed" detection race.
Implement NesCore with FCEUmm/Nestopia/Mesen libretro core support.
Generalize native frontend to handle cores that require ROM data in
memory (need_fullpath=false) and move PS1-specific config out of C++
into Ps1Core. Bundle both core .so files as APK assets with automatic
extraction. Fix cover art fetching for GoodNES-named ROMs by expanding
region codes (U)->(USA, Europe) to match LibRetro thumbnail naming.
Downloads box art from libretro-thumbnails GitHub repos for all
supported consoles (PS1, NES, SNES, N64, Genesis, PS2). Art is
fetched in the background on first scan and cached locally. Also
fixes multi-track .bin files showing as separate game entries.
Save states now support 5 slots per game with per-game directories,
screenshot thumbnails captured via PixelCopy, and a launch menu that
shows existing saves on game start (continue/new game flow). In-game
menu combines save and load into a single overlay. Also adds button
remapping, controller overlay visibility settings, and preferences
management.
Renders the emulator framebuffer into a calculated sub-rect with
nearest-neighbor scaling and black bars, driven by the core's
reported aspect ratio. Adds a cycle button in the game HUD and
a display mode setting in the settings screen.
Jetpack Compose app with adaptive layouts (phone/tablet/foldable) using
Window Size Classes. Integrates a C++ libretro frontend via JNI for
actual PS1 emulation using pcsx_rearmed, with AAudio output and
ANativeWindow rendering.
- Emulation core architecture with interface for NES, SNES, N64,
Genesis, PS1, PS2 (PS1 implemented, others placeholder)
- Touch controller overlay with D-pad (diagonal support), face buttons,
shoulders, and analog sticks
- Bluetooth/USB controller detection and input mapping
- Customizable controller layout editor with drag-to-reposition
- Game library with auto-scan of /sdcard/Games/ subfolders
- Auto-import of libretro core .so from /sdcard/Games/cores/
- Save states and SRAM persistence