Skip to content

v2.11.0 "Field Trip"

Latest

Choose a tag to compare

@doublegate doublegate released this 03 Jul 02:36

Rusty2600 v2.11.0 "Field Trip"

Eighth release of the RustyNES gap-closure arc — wires
rusty2600-mobile's already-real save_state()/load_state() UniFFI
methods (present since v1.11.0, never exposed in either mobile UI)
into real Android and iOS save-state slot UIs, researches cloud
save-state sync, and checks physical Android hardware availability.

Shipped through PR #21, reviewed by GitHub Copilot and Gemini Code
Assist — 11 findings, all genuine, all fixed, including a real
Android thread-safety bug.

Android Save State / Load State UI

8 numbered slots, matching desktop's v2.4.0 "Save Point" convention
exactly — same slot count, .r26s extension, and label wording. Keyed
by the same CRC32 ROM tag MainActivity already computes for
loadRom, stored under app-private storage (Context.filesDir), so a
slot can never even be written under the wrong ROM's key.

Genuinely verified end-to-end, not just "it compiles": rebuilt,
reinstalled on the real Pixel_8_API_34 emulator (KVM-accelerated),
loaded a real vendored homebrew ROM through the actual Storage Access
Framework file picker, saved to a slot, confirmed the on-disk file's
directory name against an independently-computed CRC32 of the source
ROM (an exact match), reopened the dialog to see a live timestamp, and
loaded it back — a real "Loaded slot N" toast confirming the full
round trip. Zero crashes across the entire session's logcat.

iOS Save State / Load State UI

The same convention over FileManager's Application Support directory
(a genuinely private, non-user-visible location — see Fixed below).
Also fixed a latent bug while wiring this up: the ROM tag was
previously derived only from byte count (two same-size ROMs would
silently collide) — replaced with a real FNV-1a-64 content hash, since
this is the first release to actually build per-tag slot directories
on iOS. Ships source-complete-but-unverified by compilation, the same
standing, accepted limitation as the rest of this project's iOS work
(no Xcode toolchain in this sandbox).

Cloud save-state sync — researched, deliberately deferred

Checked the sibling RustyNES project's actual shipped implementations
rather than assuming a target: iOS uses CloudKit; Android uses
Google Play Games Services v2 Snapshots, not Google Drive as the
roadmap assumed. Both need live backend credentials (a real Google
Play Console project, an Apple Developer account) this sandbox has
none of — documented as the concrete reference implementations to
port later, once those credentials exist.

Physical hardware verification — checked, none available

adb devices -l showed only the emulator, both before and during the
session. No device-farm tooling configured. Verification stays at the
v1.11.0 "real emulator" bar — checked for real this release per the
roadmap's own framing, not assumed unavailable.

PR review, for real

PR #21 was reviewed by GitHub Copilot and Gemini Code Assist — 11
findings, all genuine, all fixed:

  • A real Android thread-safety bug (Gemini Code Assist):
    saveState()/loadState() ran directly on the UI thread — but
    emulator is the same instance the ~60Hz frame loop mutates on a
    background handler, so this wasn't just a jank risk, it raced the
    frame loop over the same emulator state. Fixed by posting the bridge
    call + file I/O onto that same handler.
  • File I/O failures could crash the app (Copilot): widened
    catch (e: MobileException) to catch (e: Exception), covering the
    IOException the slot store's file writes/reads can throw.
  • iOS SaveSlots used .documentDirectory, which is user-visible
    via the Files app / iTunes-style sharing (Copilot): switched to the
    genuinely private .applicationSupportDirectory.
  • iOS EmulatorViewModel's save/load/probe ran synchronously on the
    @MainActor
    (Gemini Code Assist): offloaded to Task.detached.
  • Two smaller findings (Gemini Code Assist): ULong.toString(16) +
    padStart instead of a Long-cast %016x format (avoids
    JVM-specific negative-number hex formatting); url.resourceValues (forKeys:) instead of the deprecated FileManager.attributesOfItem (atPath:).

All Android fixes independently re-verified end-to-end on the live
emulator: rebuilt, reinstalled, saved to a new slot through the fixed
threaded code path, confirmed the file on disk, confirmed a
pre-existing slot (written by the old code) still loads correctly with
the new hex formatting, and scanned the full re-verification session's
logcat for any exception/error/crash: zero.

Test count

374 tests passing on default features (378 with --features test-roms) — unchanged from v2.10.0, since this release touches
zero Rust code (purely Kotlin/Swift/docs work). Full CI green —
Linux/macOS/Windows, the perf regression gate, and the no_std gate.

Honest verification boundary

iOS remains unverified by compilation (no Mac in this sandbox — a
permanent, accepted limitation, not a regression). Cloud save-state
sync is researched and scoped but not implemented, since both
platforms' real implementations need live backend credentials this
sandbox cannot provide. Physical Android hardware verification was
genuinely checked this release and confirmed unavailable.

What's next

v2.12.0 "Open Book" — Repo Content & Debugger Depth: real authored
GitHub Wiki content (mirroring RustyNES's own 20-page structure), a
trace logger debugger panel, an inline 6507 assembler, and cart
header/info + perf-monitor panels. See to-dos/ROADMAP.md for the
complete v2.4.0 -> v3.0.0 roadmap — this is the last feature release
before v3.0.0 "Convergence"'s beta/rc cycle begins.