Skip to content

Commit f060987

Browse files
authored
Merge pull request #166 from doublegate/feat/v1.8.8-atlas
v1.8.8 "Atlas" — Google Play launch readiness + Android-native excellence
2 parents f4e6156 + 6e4f1bc commit f060987

53 files changed

Lines changed: 5373 additions & 217 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,115 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
1616
## [Unreleased]
1717

18+
## [1.8.8] - 2026-06-20 - "Atlas" (Google Play launch readiness)
19+
20+
The Google-Play-launch + Android-native-excellence release: it modernizes the Android
21+
toolchain to the **Android 16 (API 36) target mandate**, lands **adaptive / foldable /
22+
TV** layouts and a **modern-UX** pass (edge-to-edge, predictive back, splash), brings
23+
**Material You** + **EN/ES i18n**, a **box-art ROM library** with scrapers and secure
24+
secret storage, a **performance / startup / app-size** pass (Baseline Profiles, R8
25+
full-mode, Compose stability), **capture / share + platform surfaces** (screenshots,
26+
MP4 clips, Picture-in-Picture, a Quick-Settings tile, app shortcuts, a home-screen
27+
widget), **Android TV / Leanback + accessibility**, and the **Google-Play-integration
28+
readiness** (Play Games cloud saves, achievements / leaderboards, Play Integrity, in-app
29+
update / review). Everything is presentation / Gradle-side: the emulation core stays
30+
**byte-identical**, so **AccuracyCoin holds 100% (139/139)** on host CI and the desktop
31+
is unchanged. The Play-services / Integrity / Chromecast features are **default-off**
32+
(the maintainer flips them on with the Play projects). This is the **sideload** build
33+
shipping to GitHub Releases now; the **Google Play production** promotion is a maintainer
34+
step gated on a batched on-device pass.
35+
36+
### Changed
37+
38+
- **Toolchain modernized for the API 36/37 target mandate.** AGP 8.7.3 → **9.2.1**,
39+
Gradle 8.13 → **9.4.1** (AGP-9 built-in Kotlin, dropping the standalone
40+
`kotlin-android` plugin; Compose compiler 2.3.10), **compileSdk 35 → 37** (core-ktx
41+
1.19 / lifecycle 2.11 require it) and **targetSdk 35 → 36** (the Android-16 Play
42+
mandate). Compose BOM **2026.06** (M3 1.4 stable) + adaptive 1.2 / window 1.5.1 /
43+
core-splashscreen 1.2. (AGP-9 built-in Kotlin needs the generated UniFFI bindings on
44+
the `kotlin` source set, not `java`.)
45+
46+
### Added
47+
48+
- **(A) Adaptive / foldable layouts (targetSdk 36).** **Window Size Classes**
49+
(`currentWindowAdaptiveInfo` + `isWidthAtLeastBreakpoint`) replace the ad-hoc 520dp
50+
test; an expanded width (≥ 840dp tablet / unfolded / desktop) gets a **two-pane**
51+
layout (a `LibraryRail` beside the player), with `appCategory="game"` as the ≥ 600dp
52+
safety net and 8:7-PAR pillarbox carrying correctness at any size / orientation.
53+
- **(K) Modern UX.** **Edge-to-edge** (`enableEdgeToEdge` + `WindowInsets.safeDrawing`,
54+
gameplay full-bleed), **predictive back** (`enableOnBackInvokedCallback` + a Compose
55+
`BackHandler`, since `onBackPressed` is gone at targetSdk 36), the **Splash Screen
56+
API** (keep-on-screen-until-ready), a Material 3 brand `ColorScheme`, and a
57+
**monochrome** (Material You themed) launcher-icon layer.
58+
- **(B) Material You dynamic color.** Gated API 31+ **and** a `dynamicColor` setting
59+
(default on): qualifying devices tint the **chrome** from the wallpaper palette, while
60+
**gameplay stays black**; light / dark / system are unchanged. A Settings "Material
61+
You" toggle (API 31+ only).
62+
- **(B) EN / ES internationalization.** ~70 user-facing strings externalized to
63+
`res/values/strings.xml` (EN) + `res/values-es/strings.xml` (ES), Spanish reusing the
64+
desktop **ADR-0023** catalog where it lines up, plus a **per-app Language picker**
65+
(System / English / Español via `AppCompatDelegate.setApplicationLocales`, back-compat
66+
to minSdk 26) wired to the system **Settings → Apps → RustyNES → Language** entry on
67+
Android 13+. (`MainActivity` is now an `AppCompatActivity`; Compose still draws all UI.)
68+
- **(C) Box-art ROM library.** A library model keyed by **ROM SHA-256** (name / mapper /
69+
region / last-played / favorite / box-art / folder) with a JSON store and a one-time
70+
recents → library migration; a `LazyVerticalGrid` (stable SHA key + `contentType`)
71+
with **All / Favorites / folder** filter chips, search, sort, favorite stars, and a
72+
long-press context menu. Compact = the idle grid; expanded ≥ 840dp = a 340dp library
73+
side-pane beside the player. A **batch SAF-tree import** enumerates `.nes` / `.fds` /
74+
`.unf` / `.unif` / `.zip`, dedups by SHA, and auto-links sibling box-art off-thread.
75+
- **(C) Box-art scrapers.** User-triggered box-art auto-download with a preview —
76+
**libretro-thumbnails** (no account, default on) + **TheGamesDB** (user API key);
77+
**ScreenScraper** is fully implemented but gated off until a devid is registered.
78+
Sources are user-supplied / local only, so the no-bundled-content posture holds. Coil
79+
3 loads / caches local thumbnails; the save-state store gains a THM PNG thumbnail API.
80+
- **(C) Secure secret storage.** **Android Keystore AES-256-GCM** at-rest encryption
81+
(per-record IV, non-exportable key) for the RA token, TURN secret, and scraper keys,
82+
replacing the deprecated `EncryptedSharedPreferences`; legacy plaintext is read and
83+
re-encrypted on the next write.
84+
- **(J) Performance, startup & app-size.** A new **`:baselineprofile`** module
85+
(baselineprofile / benchmark 1.5.0-alpha06, the first AGP-9-ready line) with cold-launch
86+
Baseline + Startup Profile generators (device generation is a maintainer step); **R8
87+
full-mode keeps** rewritten for AGP-9 `strictFullModeForKeepRules` (UniFFI / JNA /
88+
renderer / Cast — `assembleRelease` builds and the dex retains the FFI / Cast / renderer
89+
refs); **Compose stability** (`@Immutable` + `ImmutableList` so the library grid is
90+
restartable / skippable instead of recomposing every emulated frame); a deferred Play
91+
Billing connect off cold start; `ndk debugSymbolLevel = SYMBOL_TABLE`; and
92+
`Surface.setFrameRate(60.0988)`.
93+
- **(F) Capture & share.** A **screenshot** (PNG → MediaStore via scoped storage, no
94+
`WRITE_EXTERNAL_STORAGE`) and a **~30s / ~30fps MP4 clip** (a bitmap ring buffer encoded
95+
on Stop with `MediaCodec` + `MediaMuxer` to H.264, off the loop's critical path), both
96+
gameplay-only with an `ACTION_SEND` share. (Audio in the clip is a documented TODO.)
97+
- **(H) Platform surfaces.** **Picture-in-Picture** (8:7 aspect + `sourceRectHint`,
98+
auto-enter on user-leave while a ROM runs); a **Quick-Settings tile**, three **app
99+
shortcuts** (resume / open / library), and a **Glance "Resume" home-screen widget** —
100+
all deep-linking the last-played game via a shared resume / open / library contract.
101+
- **(G) Android TV / Leanback.** The adaptive Compose UI **is** the TV UI (no separate
102+
`androidx.leanback`): a TV banner + the `LEANBACK_LAUNCHER` filter + `touchscreen
103+
required=false`, full **d-pad navigation** reusing the v1.8.7 focus infra (a focusable
104+
library grid, control bar, chips, search, Settings, and the gamepad-driven Guide /
105+
Start + Select menu).
106+
- **(I) Accessibility.** A **high-contrast** scheme (~19:1) + three **Okabe-Ito
107+
colorblind** palettes (deuteranopia / protanopia / tritanopia), chrome-only (gameplay
108+
palette untouched), applied with **precedence over Material You** dynamic color; plus
109+
TalkBack semantics, 48dp touch targets, and a font-scale audit. A Settings
110+
"Accessibility theme" picker.
111+
- **(D) Play Games cloud saves (default-off, `PGS_ENABLED`).** Play Games Services v2
112+
**Snapshots** sync of the SHA-keyed `.rns` save-states — one snapshot per (ROM-SHA,
113+
slot) with a `MOST_RECENTLY_MODIFIED` policy and a keep-device / keep-cloud dialog for
114+
true conflicts; push on save, pull on first open. (A 3-way merge UI is a TODO.)
115+
- **(E) Play Games achievements / leaderboards (default-off, `PGS_ENABLED`).** PGS v2
116+
auto-sign-in + unlock / increment / submit-score with placeholder achievement IDs
117+
(first-rom / save / netplay / turbo-100 / first-cloud-sync) and a play-time leaderboard,
118+
wired at the triggers — **distinct from RetroAchievements** (the v1.8.6 commercial-game
119+
system, untouched and kept separate).
120+
- **(L) Play quality / integrity (default-off, `PLAY_INTEGRITY_ENABLED`).** A **Play
121+
Integrity** client (defense-in-depth over Billing — Billing stays the entitlement
122+
truth, a failed verdict never revokes a purchase; a decryption stub awaits the
123+
maintainer's server endpoint), **in-app update** + **in-app review** (no-op on non-Play
124+
installs), and an **opt-in local crash-log** handler (no upload / Firebase) with a
125+
Settings Diagnostics toggle.
126+
18127
## [1.8.7] - 2026-06-20 - "Android" (Connectivity completion)
19128

20129
Completes v1.8.6's connectivity work: **CGNAT / TURN room-code netplay** so phones on

‎Cargo.lock‎

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ members = [
1919
]
2020

2121
[workspace.package]
22-
version = "1.8.7"
22+
version = "1.8.8"
2323
edition = "2024"
2424
rust-version = "1.96"
2525
license = "MIT OR Apache-2.0"

‎README.md‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
[![Build Status](https://github.com/doublegate/RustyNES/workflows/CI/badge.svg)](https://github.com/doublegate/RustyNES/actions)
1212
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)
13-
[![Version](https://img.shields.io/badge/version-v1.8.7-blue.svg)](https://github.com/doublegate/RustyNES/releases)
13+
[![Version](https://img.shields.io/badge/version-v1.8.8-blue.svg)](https://github.com/doublegate/RustyNES/releases)
1414
[![Rust: 1.96](https://img.shields.io/badge/rust-1.96-orange.svg)](rust-toolchain.toml)
1515
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20Web-lightgrey.svg)](#platform-support)
1616
[![AccuracyCoin](<https://img.shields.io/badge/AccuracyCoin-100%25%20(139%2F139)-brightgreen.svg>)](#compatibility-and-accuracy)
@@ -645,17 +645,22 @@ workspace API docs (rustdoc) at
645645

646646
## Version History
647647

648-
The current release is **v1.8.7 "Android"**, continuing the first **platform** (not
649-
accuracy) release line **v1.8.0 "Android"** — a complete Android app, verified
650-
end-to-end on a Samsung Galaxy Z Fold 7. It builds on the bugfix patch **v1.7.1** and
651-
the writable + programmable **v1.7.0 "Forge"** line, on the cycle-accurate v1.0.0
652-
production core. (v1.8.7 completes the connectivity work — **CGNAT / TURN room-code
653-
netplay**, robust **hardware controllers**, and a controller-aware UI — atop the
654-
feature-complete v1.8.6; v1.8.0–v1.8.7 are GitHub-Releases / sideload, full-featured.
655-
The **Google Play launch** moves to **v1.8.8 "Atlas"**.) The road so far:
648+
The current release is **v1.8.8 "Atlas"**, the Google-Play-launch + Android-native-
649+
excellence increment on the first **platform** (not accuracy) release line **v1.8.0
650+
"Android"** — a complete Android app, verified end-to-end on a Samsung Galaxy Z Fold 7.
651+
It builds on the bugfix patch **v1.7.1** and the writable + programmable **v1.7.0
652+
"Forge"** line, on the cycle-accurate v1.0.0 production core. (v1.8.8 modernizes the
653+
toolchain to the **Android 16 / API 36 target mandate** and lands **adaptive / foldable
654+
/ TV** layouts, **Material You + EN/ES i18n**, a **box-art ROM library**, **capture /
655+
PiP / widgets**, **accessibility**, and the **Google-Play-integration readiness** —
656+
Play Games cloud saves, achievements, Play Integrity, in-app update / review — all
657+
default-off until the maintainer wires the Play projects. v1.8.0–v1.8.8 ship as
658+
GitHub-Releases / sideload, full-featured; the **Google Play production** promotion is a
659+
maintainer step gated on a batched on-device pass.) The road so far:
656660

657661
| Version | Highlights |
658662
| ---------- | ------------------------------------------------------------------------------------------- |
663+
| **v1.8.8** | "Atlas" — Google Play launch readiness. The Android toolchain moves to the **Android 16 / API 36 target mandate** (AGP 9.2.1 / Gradle 9.4.1 / compileSdk 37 / targetSdk 36, Compose BOM 2026.06). **(A+K)** adaptive **Window Size Classes** + a two-pane ≥ 840dp layout, edge-to-edge, predictive back, splash, a Material 3 brand theme + monochrome icon. **(B)** **Material You** dynamic color (chrome only; gameplay stays black) + **EN / ES** i18n with a per-app Language picker. **(C)** a **box-art ROM library** (SHA-256-keyed grid, favorites, search, folders, batch SAF import) with **libretro / TheGamesDB** scrapers (user-supplied only) and **Keystore AES-256-GCM** secret encryption. **(J)** a `:baselineprofile` module, **R8 full-mode** keeps, and Compose stability. **(F+H)** **screenshot / MP4 capture**, **Picture-in-Picture**, a Quick-Settings tile, app shortcuts, and a **Glance "Resume" widget**. **(G+I)** **Android TV / Leanback** d-pad nav + a high-contrast theme and three **Okabe-Ito** colorblind palettes (over dynamic color). **(D+E+L, default-off)** **Play Games** cloud-save Snapshots + placeholder achievements / leaderboards (distinct from RetroAchievements), **Play Integrity** (defense-in-depth over Billing), and in-app update / review. All presentation / Gradle-side; AccuracyCoin holds 100% (139/139) on host CI. **Sideload** ships now; the **Google Play production** promotion + the Play / Integrity / Chromecast projects are maintainer post-verify steps. |
659664
| **v1.8.7** | "Android" — connectivity completion. **CGNAT / TURN room-code netplay** completes v1.8.6's direct-IP / LAN netplay: a **`NatConnect`** orchestrator (STUN discovery → UDP hole-punch → a `SignalMessage::PublicAddr` exchange over the same `signaling::Relay` → a **TURN relay fallback** for symmetric NAT, `is_relayed`) with `np_host_room` / `np_join_room` bridge methods and an **"Online (room code)"** Android UI (share a 6-char code, join by code, Registering / Punching / Relaying progress + a "via relay" badge). Plus **robust hardware controllers** (the input pipeline gains `onGenericMotionEvent` — analog sticks + the d-pad-as-HAT axis silently failed on 8BitDo / DualSense / OTG pads before — with per-port P1–P4 masks, `InputManager` hot-plug, per-descriptor remapping, and turbo / autofire; no Rust change), a **controller-aware UI** (connect a pad → the on-screen controller hides + the ROM display maximizes; Guide or Start + Select opens a d-pad-navigable menu), and **Chromecast prep** behind a default-off `CHROMECAST_ENABLED` flag (a CAF sender + a static Web Receiver spectator mirror; the Presentation API stays primary). Loopback / mock-verified; live cross-NAT + the hosted relay are maintainer carryovers. All host-side; AccuracyCoin holds 100% (139/139). Sideload-only — the **Google Play launch** is **v1.8.8 "Atlas"**. |
660665
| **v1.8.6** | "Android" — connectivity & scripting. **Lua** (the sandboxed desktop engine over the bridge — made `Send`; SAF `.lua` load + an on-screen log overlay), **RetroAchievements** (login + hardcore + unlock toasts + a `.rap` progress sidecar; the session extracted into a new shared **`rustynes-ra`** crate, `ureq` + `rustls` + `ring` TLS — no OpenSSL), and **direct-IP / LAN netplay** (GGPO rollback over `rustynes-netplay`; the loop calls `np_advance_frame` instead of `run_frame` while connected), plus an **Open / Close ROM** toggle. The shared `rustynes-mobile` bridge is now connectivity-complete, so **v1.9.0 iOS inherits all three**. All host-side; AccuracyCoin holds 100% (139/139). The **Google Play launch** (`$2.99` Full Unlock) is the decoupled capstone; CGNAT/TURN netplay is deferred to v1.8.7. |
661666
| **v1.8.5** | "Android" — power-user features. **Custom `.pal` palettes** + **compressed `.zip` ROM loading** (extract the first NES entry, like desktop); **Bisqwit composite NTSC on the GPU** (the desktop WGSL shared via `rustynes-gfx-shaders`, an `R16Uint` palette-index pipeline fed by the bridge); **TAS `.rnm` movies** (record / play / save via SAF, determinism-exact); a **per-game settings DB** (each game reopens with its last video filter, keyed by ROM SHA); and **HD-packs on Android** — the HD-pack loader + compositor + HD audio were **extracted into a new shared `rustynes-hdpack` crate** (the core is `#![no_std]`) so the bridge can composite the upscaled picture (Bitmap path). All presentation / host-side; AccuracyCoin holds 100% (139/139). |
@@ -799,7 +804,7 @@ If you use RustyNES in academic research, please cite:
799804
author = {RustyNES Contributors},
800805
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
801806
year = {2026},
802-
version = {1.8.7},
807+
version = {1.8.8},
803808
url = {https://github.com/doublegate/RustyNES},
804809
note = {Cycle-accurate NES emulator on a master-clock-precise scheduler;
805810
AccuracyCoin 100\% (139/139), nestest 0-diff; 168 mapper families,

0 commit comments

Comments
 (0)