Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.8.2] - 2026-06-20 - "Android" (Input & the virtual controller)

### Added

- **Multi-touch virtual NES controller (Android).** A single `Canvas` both draws
an NES-001 control pad — styled per `assets/RustyNES_Icon/rustynes.svg`
(`nes_controller()` geometry + palette: grey body, charcoal face plate, black
cross D-pad, grey Select/Start pills, the red racetrack logo, dark-red concave
A/B buttons, red labels, with pressed-state lighting) — and collects input
through one container-level pointer loop that recomputes the full `Buttons` mask
from **all** currently-pressed pointers each event. This gives **simultaneous
presses** (A + a direction), **D-pad diagonals**, and slide-between-buttons,
replacing the old per-button overlay that registered one input at a time. The
art and the touch regions both derive from the measured size, so they **resize +
remap in lockstep** across the Z Fold 7's cover and unfolded-inner displays. Plus
light haptics on press, and touch + hardware-gamepad masks OR'd so they don't
clobber each other. Determinism-safe — input still flows through the one
late-latched mask.
- **The RustyNES app icon** (Android). The launcher icon is now the actual
RustyNES artwork (from `assets/RustyNES_Icon/icon-1024.png`) — an adaptive icon
(a 62%-inset foreground over a `#1C1F24` background, so the launcher squircle
keeps the corner peripherals) + legacy density mipmaps — replacing the
placeholder monogram.

### Changed

- **Icon wordmark refresh.** Regenerated the icon (`make_icon.py`) so "Rusty"
matches "NES" in size (cap height 27 → 35, baseline kept, nudged right ~1/8
char, overlapping the cog). This refreshes `assets/RustyNES_Icon/` (svg / ico /
`icon-*.png` / preview), the Android launcher mipmaps, and the README header
logo (`images/RustyNES_Logo-Icon.png`) — so the desktop window/About icon
updates too.

- **`PLAY_BUILD` build flag (Android, default false).** Per the deferred-Play-launch
plan, the freemium logic (the demo timer + persistence locks + Play Billing) is
now gated on `PLAY_BUILD`, so **sideload / GitHub / dev builds are full-featured**;
only the Google Play AAB (the v1.8.6 launch) flips it on. The verified freemium
code stays compiled but dormant.

## [1.8.1] - 2026-06-19 - "Android" (Patch)

### Changed
Expand Down
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ members = [
]

[workspace.package]
version = "1.8.1"
version = "1.8.2"
edition = "2024"
rust-version = "1.96"
license = "MIT OR Apache-2.0"
Expand All @@ -27,17 +27,17 @@ keywords = ["nes", "emulator", "famicom", "6502", "retro"]
categories = ["emulators", "games"]

[workspace.dependencies]
rustynes-core = { path = "crates/rustynes-core", version = "1.8.1" }
rustynes-cpu = { path = "crates/rustynes-cpu", version = "1.8.1" }
rustynes-ppu = { path = "crates/rustynes-ppu", version = "1.8.1" }
rustynes-apu = { path = "crates/rustynes-apu", version = "1.8.1", default-features = false }
rustynes-mappers = { path = "crates/rustynes-mappers", version = "1.8.1" }
rustynes-netplay = { path = "crates/rustynes-netplay", version = "1.8.1" }
rustynes-script = { path = "crates/rustynes-script", version = "1.8.1" }
rustynes-test-harness = { path = "crates/rustynes-test-harness", version = "1.8.1" }
rustynes-core = { path = "crates/rustynes-core", version = "1.8.2" }
rustynes-cpu = { path = "crates/rustynes-cpu", version = "1.8.2" }
rustynes-ppu = { path = "crates/rustynes-ppu", version = "1.8.2" }
rustynes-apu = { path = "crates/rustynes-apu", version = "1.8.2", default-features = false }
rustynes-mappers = { path = "crates/rustynes-mappers", version = "1.8.2" }
rustynes-netplay = { path = "crates/rustynes-netplay", version = "1.8.2" }
rustynes-script = { path = "crates/rustynes-script", version = "1.8.2" }
rustynes-test-harness = { path = "crates/rustynes-test-harness", version = "1.8.2" }
# v1.8.0 "Android" — the shared mobile bridge, consumed by rustynes-android (and
# v1.9.0's rustynes-ios). Path dep; the version req is a floor.
rustynes-mobile = { path = "crates/rustynes-mobile", version = "1.8.1" }
rustynes-mobile = { path = "crates/rustynes-mobile", version = "1.8.2" }

# v1.1.0 beta.3 (Workstream E) — Lua scripting. `vendored` compiles Lua 5.4
# from source (no system Lua needed); native-only (the wasm/no_std builds never
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

[![Build Status](https://github.com/doublegate/RustyNES/workflows/CI/badge.svg)](https://github.com/doublegate/RustyNES/actions)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)
[![Version](https://img.shields.io/badge/version-v1.8.1-blue.svg)](https://github.com/doublegate/RustyNES/releases)
[![Version](https://img.shields.io/badge/version-v1.8.2-blue.svg)](https://github.com/doublegate/RustyNES/releases)
[![Rust: 1.96](https://img.shields.io/badge/rust-1.96-orange.svg)](rust-toolchain.toml)
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS%20%7C%20Web-lightgrey.svg)](#platform-support)
[![AccuracyCoin](<https://img.shields.io/badge/AccuracyCoin-100%25%20(139%2F139)-brightgreen.svg>)](#compatibility-and-accuracy)
Expand Down Expand Up @@ -645,16 +645,18 @@ workspace API docs (rustdoc) at

## Version History

The current release is **v1.8.1 "Android"**, a patch on the first **platform** (not
accuracy) release **v1.8.0 "Android"** — a complete, shippable Android app, verified
The current release is **v1.8.2 "Android"**, continuing the first **platform** (not
accuracy) release line **v1.8.0 "Android"** — a complete Android app, verified
end-to-end on a Samsung Galaxy Z Fold 7. It builds on the bugfix patch **v1.7.1** and
the writable + programmable **v1.7.0 "Forge"** line, on the cycle-accurate v1.0.0
production core. The road so far:
production core. (The Google Play launch is **held to v1.8.6**, feature-complete;
v1.8.0–v1.8.5 are GitHub-Releases / sideload, full-featured.) The road so far:

| Version | Highlights |
| ---------- | ------------------------------------------------------------------------------------------- |
| **v1.8.2** | "Android" — input & the virtual controller. A **multi-touch** on-screen **NES controller** (one Canvas draws the NES-001 pad styled per the RustyNES icon **and** collects all pressed pointers → simultaneous presses, D-pad diagonals, slide) that **resizes + remaps across the Z Fold 7 cover/inner displays**, with pressed-state lighting + light haptics; the real **RustyNES app icon** for the launcher; and a **`PLAY_BUILD`** flag so sideload/GitHub builds are full-featured (freemium engages only in the deferred Play AAB). |
| **v1.8.1** | "Android" patch — the free-tier demo is trimmed from 10 minutes to **8 minutes** per launch (the paid Full Unlock is unaffected), and the debug "Full Unlock" override is confirmed **R8-stripped from the release AAB** (0 occurrences of the debug-toggle symbols in the shipped `classes*.dex`). |
| **v1.8.0** | "Android" — the first **platform** release: a complete Android app on the byte-identical core (so AccuracyCoin holds 100% / 139/139 unchanged). A shared **`rustynes-mobile`** UniFFI bridge + a **`rustynes-android`** platform crate + a Jetpack **Compose** shell; **audio** (`AudioTrack`), **touch + hardware-gamepad** input, **save-states / battery-SRAM / a recent-ROMs library** (SAF persistable URIs, save-on-background + auto-resume), **pause / fast-forward / mute**, a responsive **foldable-aware + immersive** UI (cover + unfolded inner screen), **GPU CRT / scanline** post-processing (AGSL `RuntimeShader`), and a thermal-throttle fast-forward backoff. Distributed on **Google Play** as a free download with a one-time **$2.99 "Full Unlock"** (Play Billing) — the free tier is a **10-minute demo** without save-states / resume / battery-SRAM, otherwise feature-complete. Verified **arm64 R8 release AAB**. The full wgpu-`SurfaceView` native renderer (reusing the desktop WGSL shader stack) + netplay / RetroAchievements / Lua on mobile are the documented next increment. |
| **v1.8.0** | "Android" — the first **platform** release: a complete Android app on the byte-identical core (so AccuracyCoin holds 100% / 139/139 unchanged). A shared **`rustynes-mobile`** UniFFI bridge + a **`rustynes-android`** platform crate + a Jetpack **Compose** shell; **audio** (`AudioTrack`), **touch + hardware-gamepad** input, **save-states / battery-SRAM / a recent-ROMs library** (SAF persistable URIs, save-on-background + auto-resume), **pause / fast-forward / mute**, a responsive **foldable-aware + immersive** UI (cover + unfolded inner screen), **GPU CRT / scanline** post-processing (AGSL `RuntimeShader`), and a thermal-throttle fast-forward backoff. Distributed on **Google Play** as a free download with a one-time **$2.99 "Full Unlock"** (Play Billing) — the free tier is a **10-minute demo** without save-states / resume / battery-SRAM, otherwise feature-complete. Verified **arm64 R8 release AAB**. A freemium **$2.99 Full Unlock** (Play Billing) is built + verified but the **Google Play launch is held to v1.8.6** (feature-complete); interim releases are sideload/full-featured. The full wgpu-`SurfaceView` native renderer (reusing the desktop WGSL shader stack) + netplay / RetroAchievements / Lua on mobile are the documented next increment. |
| **v1.7.1** | Patch on "Forge" — seven fixes: a **ROM-close crash** (skip the `write_texture` upload on a pixel-slice length mismatch so the release build no longer aborts wgpu; `close_rom` presents a clean blank frame); **clean pause/unpause** (a pacing-timer `break_phase()` reset on resume + a sticky audio pause gate → no spurious `produced_max_ms` spike, zero underruns); a **Documentation pane overhaul** (word-wrap at any UI scale, a collapsible multi-level sidebar tree with intra-doc links, `[Unreleased]` ordered last); an **exhaustive README rewrite**; **Tools-menu icon** fixes for NSF Player / Pixel Inspector (glyph swap dodging egui's Ubuntu-Light `fi`/`fl` PUA-ligature collision); **removal of the vestigial "Show Debugger" checkbox** + dead plumbing; and **HD-pack tile substitution** now applying in the debugger / tool render branch. Bugfix-only; with optional features off the shipped / native / `no_std` / wasm builds behave as v1.7.0 and AccuracyCoin holds 100% (139/139). |
| **v1.7.0** | "Forge" — the tools become **writable** and **programmable**. **F** accuracy hardening (battery-save round-trip oracle + length-halt/reload + DMC even/odd-defer pins + an off-by-default PPU extra-scanlines overclock); mapper breadth → **168 families** (G1 reusable-ASIC: FK23C / COOLBOY / MINDKIDS / Sachen / Waixing / Kaiser, BestEffort honesty-gated); **A** editing-capable tools (palette / nametable / CHR / OAM writeback, an iNES / NES 2.0 header editor, an inline 6502 assembler); **C** debugger depth (CallstackManager + step modes, MemoryAccessCounter + uninit-read, ca65 / cc65 `.dbg` source maps); **B** scriptable **TAStudio** (`tastudio.*` Lua API + analysis-canvas callbacks) + full Lua parity; **E** host IPC / automation (`comm.*` / `client.*` / `userdata.*`) behind a new off-by-default `script-ipc` feature (host-mediated sandbox, ADR 0016); **D** rewind (HistoryViewer + Export-Last-30s `.rnm` + a Zwinder XOR-delta + LZ4 tiered greenzone); **G** expansion-audio (NSF router reusing VRC6/7 / FDS / MMC5 / N163 / 5B + MMC5 audio), movie import (`.fcm` / `.fmv` / `.vmv`; `.fm2` / `.bk2` export hashing), and an **HD-Pack Builder** (ADR 0017) + the HD-pack loader real-Mesen-`<tile>`-format fix (ADR 0018); a UI overhaul (consolidated Input Display, modernized menu / status bar, a polished Documentation pane); plus the **H1–H9 reach wave** — browser-RA finish + an RA HUD, spectator netplay + Game-Genie-encoder / `.srt` / `.tbl`, coverage-harness `.zip` / `.7z` / `.fds` discovery, per-game `<rom>.json` config overrides + a DIP editor + a lag counter (ADR 0019), audio depth (stereo panning / Schroeder reverb / crossfeed / output-device picker / 20-band EQ, ADR 0020), web/wasm parity (browser Lua / File-System-Access-API / Gamepad-API / PWA-offline / `?settings=` share-links, ADRs 0021+0022), an **i18n** framework (compile-time string catalog + a language picker, English + Spanish, ADR 0023), and a `full` maximal-native-feature build + `cargo full-run` alias. Additive / off-by-default; the shipped / native / `no_std` / wasm builds stay byte-identical and AccuracyCoin holds 100% (139/139). |
| **v1.6.0** | "Studio" — creator-power + accuracy-polish + reach. A **TAStudio-class piano-roll TAS editor** (save-state greenzone + lag log + markers + forkable branches + `.rnmproj` projects, with a drag-paint egui grid + deterministic seek); **`.fm2` (FCEUX) and `.bk2` (BizHawk) movie interop** ↔ `.rnm`; **Lua movie driving** (`emu.run` / `emu.frameadvance`) + data breadth (memory domains, sized reads, `joypad`); **Mesen2-class debugger depth** (expression / conditional breakpoints, R/W/X watchpoints, a watch window, conditional trace, a full hex editor, RAM watch / search); an **off-axis accuracy** verification + documentation pass (DMC/OAM-DMA controller corruption, `$2007` read-during-render, sprite-overflow + open-bus decay — verified, no engine change); mapper breadth → **150 families** (J.Y. ASIC + a discrete-multicart batch) + a **UNIF (`.unf`) loader**; **FDS-proper** (timed disk-head + `$4032` auto-insert + per-game CRC quirk table); **A/V recording** (ffmpeg → mp4 / mkv, default-off `av-record`); **HD-pack HD audio** (`<bgm>` / `<sfx>` OGG via `$4100`, default-off `hd-pack`); and a **shader / filter ecosystem** (LMP88959 NTSC/PAL, hqNx / xBRZ, a constrained `.slangp` / `.cgp` import). Additive / off-by-default; the shipped / native / `no_std` / wasm builds stay byte-identical and AccuracyCoin holds 100% (139/139). |
Expand Down Expand Up @@ -790,7 +792,7 @@ If you use RustyNES in academic research, please cite:
author = {RustyNES Contributors},
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
year = {2026},
version = {1.8.1},
version = {1.8.2},
url = {https://github.com/doublegate/RustyNES},
note = {Cycle-accurate NES emulator on a master-clock-precise scheduler;
AccuracyCoin 100\% (139/139), nestest 0-diff; 168 mapper families,
Expand Down
9 changes: 7 additions & 2 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ android {
applicationId = "com.doublegate.rustynes"
minSdk = 26 // AAudio floor.
targetSdk = 35 // Play mandate since 2025-08-31.
versionCode = 10801 // 1.8.1
versionName = "1.8.1"
versionCode = 10802 // 1.8.2
versionName = "1.8.2"
// No abiFilters here — set per buildType so release ships arm64 only
// while debug keeps x86_64 for the emulator.
// PLAY_BUILD gates the freemium (demo timer + persistence locks + Billing).
// Default false → sideload/GitHub/dev builds are full-featured; the Google
// Play AAB sets it true (v1.8.6 launch). See the v1.8.0 plan's monetization
// timing.
buildConfigField("boolean", "PLAY_BUILD", "false")
}

// Release signing reads `keystore.properties` (gitignored) or env vars; when
Expand Down
3 changes: 3 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />

<!-- Light haptic tick on on-screen button presses (the virtual controller). -->
<uses-permission android:name="android.permission.VIBRATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
Loading
Loading