Skip to content

Releases: doublegate/RustySNES

v1.11.0 "Podium"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 07:44
aca1ee3

Seventh release of the RustyNES-parity roadmap: RetroAchievements never loaded a game.

Fixed

  • RetroAchievements never actually loaded a game. No code path ever called
    RaClient::begin_load_game — login worked, CheevosState::do_frame ran every
    emulated frame, and AchievementTriggered events were wired all the way to
    status-bar toasts, but with no game ever identified/loaded into rc_client,
    there was no achievement set to evaluate memory against, so achievements
    could never actually trigger. CheevosState::load_game/unload_game now
    wrap the missing calls, invoked from app.rs's MenuAction::OpenRom/
    CloseRom handlers (a no-op unless a user is logged in); a poll()-drained
    toast surfaces success/failure so the fix is observably verifiable, not just
    type-checked. This is the actual prerequisite bug blocking hardcore mode,
    leaderboards, and rich presence from meaning anything — found while scoping
    those features for this release.

Deferred (honestly scoped, not silently dropped)

  • Splitting a new rustysnes-ra session/UI crate out of frontend/src/cheevos.rs's
    informal state, hardcore mode gating rewind/save-load/cheats/TAS, and
    leaderboard/rich-presence UI are all real, substantial features that were
    meaningless without the game-load fix above landing first. Pushed to a
    later, explicitly-scoped release. See to-dos/VERSION-PLAN.md's v1.11.0
    section.
  • A ROM loaded via the CLI at startup, followed by a later login through the
    Tools window, is not retroactively announced to rc_client — the common
    path (launch, log in, then open a ROM via the File menu) is unaffected. See
    cheevos.rs's module doc.

v1.10.0 "Atelier"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 07:10
59abec2

Sixth release of the RustyNES-parity roadmap: HD-pack emu-thread wiring.

Fixed

  • HD texture packs (v1.3.0) were never wired into the emu-thread build
    app.rs's synchronous render path composited an active pack via
    hd_compositor::composite before its own drop(emu), but the threaded build's
    emu_thread::drive_one had no equivalent step, so a threaded build with a pack
    selected silently rendered the native (uncomposited) framebuffer
    (docs/frontend.md's documented scope cut, closed here). drive_one's
    plain-frame (run-ahead-disabled) branch now composites before publishing to
    PresentBuffer; the common no-pack-active case stays exactly as fast as before
    (a cheap hd_pack_name() &self pre-check, no extra allocation) since the real
    compositing cost only applies once a pack is actually selected. Found in review
    (#90): the lock is now released before the PresentBuffer copy in this branch
    too, matching the run-ahead branch's existing drop(emu)-before-publish pattern.

Deferred (honestly scoped, not silently dropped)

  • The in-app HD-pack Builder GUI (browsing the live TileTag stream,
    assigning replacement PNGs, writing pack.toml + assets) needs a new
    core-side "reconstruct RGBA pixels for a given tile hash" API that doesn't
    exist yet — the tile-identity hash doesn't reverse to a VRAM location, so
    authoring support is a genuinely separate, substantial piece of work from the
    emu-thread wiring fix above. Pushed to a later, explicitly-scoped release.
    See to-dos/VERSION-PLAN.md's v1.10.0 section.
  • HD-pack compositing is deliberately NOT applied to emu-thread's run-ahead
    branch.
    Found in review (#90): step_with_run_ahead's returned frame is a
    PEEKED frame (captured, then rolled back so emu's persisted state only
    advances by one real frame), but EmuCore::hd_pack_composite_inputs reads
    Ppu::tile_tags() from emu's CURRENT (post-rollback) state — a different
    frame than the peeked bytes. Compositing with a mismatched (fb, tags) pair
    would silently apply replacement tiles keyed to the wrong frame, corrupting
    the picture rather than just showing native art, so this rung skips
    compositing there instead. The same desync already exists, unfixed, in
    app.rs's synchronous render path
    (pre-existing since run-ahead and
    HD-pack were first combined; not introduced by this release, not touched by
    it either) — both are tracked together as a v1.10.x/later follow-up in
    to-dos/VERSION-PLAN.md. Run-ahead and HD-pack are each off by default, so
    this only affects the narrow case where a user has both features enabled
    simultaneously.

v1.9.0 "Marionette"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 06:31
22323d2

Fifth release of the RustyNES-parity roadmap: Lua scripting bus-widening.

Added

  • Lua scripting: full-bus readsrustysnes-script's emu.read(addr) now reaches
    [Bus::peek] (the full 24-bit bus: WRAM, cart ROM/SRAM; I/O register space still reads back as
    0, Bus::peek's own documented behavior, matching the debugger's Memory panel), widened from
    [Bus::peek_wram] (WRAM-only). emu.write(addr, val) stays scoped to
    [Bus::poke_wram] (WRAM only) — a side-effect-free "poke" has no clean semantic for register
    space (a real PPU/APU/DMA register write has hardware side effects a silent poke can't model
    without either faking them or breaking the determinism contract), so widening reads while
    keeping writes WRAM-scoped is a deliberate, asymmetric choice, not an oversight.

Deferred (honestly scoped, not silently dropped)

  • A wasm piccolo Lua backend (scripting is currently native-only, mlua) and TAStudio-style
    piano-roll movie editing are both substantial standalone efforts, comparable in size to a full
    release rung on their own — pushed to a later, explicitly-scoped release rather than folded into
    this one. See to-dos/VERSION-PLAN.md's v1.9.0 section.

v1.8.0 "Tracepoint"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 05:19
b489657

Fourth release of the RustyNES-parity roadmap: debugger depth II.

Added

  • Memory Compare panel — captures a baseline snapshot of the Memory panel's current window
    and diffs it against the live window on every frame, showing only the rows that changed
    (before -> after hex). Flags a mismatch instead of a misleading diff if the window has
    scrolled since the baseline was captured (no scroll control exists yet — same gap the Memory
    panel itself carries).
  • Docs panel — an in-app SNES-terminology glossary (docs/glossary.md, embedded via
    include_str!, ~3KB) for quick lookup mid-session, plus a link to the full MkDocs handbook
    (v1.6.0 "Lighthouse"). Deliberately scoped to the glossary alone, not the full 10-50KB
    subsystem-spec docs, to keep wasm size impact negligible (verified: +2KB gzip, still ~2.05 MiB
    under the 5 MiB budget).

Deferred (honestly scoped, not silently dropped)

  • A call-stack view, an instruction/event trace buffer, and an inline 65816 assembler all need
    new core-side instrumentation (tracking call/return events or recording a trace log as they
    happen — not inferable from a point-in-time memory snapshot the way this rung's two panels are).
    A larger cross-crate change than this rung's frontend-only scope; tracked as follow-up work.
  • A dedicated per-coprocessor-type register panel (DSP-2/4, S-DD1, CX4, OBC1, ST018, S-RTC beyond
    the SA-1/GSU state the existing Cart panel already shows) needs new Board-trait debug-state
    accessors — also deferred.

v1.7.1

Choose a tag to compare

@github-actions github-actions released this 12 Jul 04:55
6a9778f

Patch release: a single user-reported bugfix, no new scope.

Fixed

  • The wasm demo's canvas rendered at a smaller, fixed 2x scale instead of the 3x
    INITIAL_SCALE native launches at.
    App::create_window special-cased wasm32 to a hardcoded
    (512.0, 448.0) LogicalSize, deferring to web/index.html's own CSS (512x448) — but
    winit's web backend actually resizes the attached <canvas> to match the requested inner size,
    overriding that CSS regardless. RustyNES's own create_window requests NES_W * INITIAL_SCALE
    unconditionally (native and wasm alike), which is why its own demo already rendered at 3x — a
    user comparing the two demos side by side noticed the size difference. web/index.html's CSS
    updated to 896x728 (the new pre-JS fallback appearance, matching the actual chrome-padded 3x
    size) so there's no flash of the old size before winit applies the real one. Found in review
    (#82): the fallback CSS's fixed height paired with max-width: 96vw would have distorted the
    canvas on narrow viewports — switched to aspect-ratio + height: auto instead.

v1.7.0 "Telemetry"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 04:40
0614e42

Third release of the RustyNES-parity roadmap.

Added

  • Debugger foundation — the 4-panel debugger overlay (previously inline in ui_shell.rs,
    ~600 lines) moved into a dedicated debugger/ module (mod.rs + cpu_panel.rs/ppu_panel.rs/
    apu_panel.rs/cart_panel.rs/watch_panel.rs) — a pure structural extraction, zero behavior
    change, that later debugger-depth rungs (v1.8.0 onward) plug new panels into. lib.rs's
    stale "the deep debugger panels are still TODO stubs" doc comment corrected (the panels have
    existed since v0.8.0; this rung gives them a real module).
  • Memory panel — the Watch panel (renamed "Memory/Watch" in the panel selector) gained a
    read-only hex dump of a 512-byte window of WRAM/cart space (DebugSnapshot::memory_window,
    read via the same non-intrusive Bus::peek the disassembler already uses; I/O register space
    reads back as 00 rather than a live register value — Bus::peek intentionally doesn't model
    registers, so this is a memory dump, not a register viewer). Fixed at $7E0000 (WRAM bank 0)
    by default — no UI scroll control yet (EmuCore::set_debug_memory_scroll exists for a future
    one to call), the same honestly-tracked gap the existing VRAM viewer already carries. Write
    support and a RAM-search tool are explicitly not included in this rung — deferred, not
    overclaimed.

Fixed

  • The workspace version was stuck at 1.4.0 since v1.5.0env!("CARGO_PKG_VERSION")
    feeds the egui Help window's version label and the CLI's --version output (including on the
    deployed GitHub Pages wasm demo), so both silently under-reported the running version through
    the v1.5.0/v1.6.0 releases. Every prior chore(release) commit back to v0.7.0 bumped
    [workspace.package] version (and each crate's own pinned version field) as part of the
    closeout — a step that isn't spelled out in docs/adr/0007's decision list and got missed
    starting at v1.5.0. Reported by a user testing the live demo; bumped to 1.7.0 across the
    workspace and all 11 non-workspace-inherited crates, and this ceremony gap is now called out
    explicitly in to-dos/VERSION-PLAN.md's standing release checklist.
  • Findings from review (#80): the memory panel's doc comment, panel label, and CHANGELOG
    entry all overclaimed "full 24-bit CPU bus" without noting that Bus::peek returns 0 for
    I/O register space — corrected in all three places.

v1.6.0 "Lighthouse"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 03:54
a762412

Second release of the RustyNES-parity roadmap.

Added

  • Documentation site + PWA + accuracy ledger — a Material for MkDocs handbook (mkdocs.yml)
    is now published at https://doublegate.github.io/RustySNES/docs/, alongside the wasm demo
    (/) and rustdoc (/api/), replacing pages.yml with a combined web.yml that also enforces
    the existing <5MiB gzip wasm size budget (scripts/wasm_size_budget.sh) on every PR. The wasm
    demo gained PWA/offline support (manifest.webmanifest, a stale-while-revalidate sw.js
    service worker, a real icon.svg). New docs/accuracy-ledger.md maps every known
    approximation/divergence to an explicit disposition (Remediated / No-stricter-oracle-available /
    Deferred / Out-of-scope), the "why" companion to docs/STATUS.md's pass-count dashboard.
    docs/DOCUMENTATION_INDEX.md refreshed (was still stamped v0.4.0, referenced a nonexistent
    SALVAGE_MANIFEST.md).

Fixed (caught in PR review, #78)

  • web.yml's cancel-in-progress was inverted relative to its own comment — PR size-budget
    checks could be cancelled mid-flight while stale main pushes weren't; corrected to cancel on
    push only.
  • sw.js's fetch handler could resolve respondWith() to undefined on a truly offline first
    visit (network fails and nothing is cached yet); now falls back to a synthetic 503 response.

v1.5.0 "Bedrock"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 03:33
5a07ad3

First release of the RustyNES-parity roadmap: closes the gap between this project's own
feature/UX/accuracy maturity and its sibling NES emulator RustyNES, tracked in lockstep rather
than a frozen snapshot. This rung is CI safety net only — see to-dos/VERSION-PLAN.md's
"RustyNES-parity ladder" section for the full v1.5.0-v1.19.0 plan.

Added

  • CI safety netcargo test --workspace now runs on every PR/push to main (new
    test-light job), not only on a tagged release. A new changes/setup job pair computes a
    light-vs-full run mode per push (mirroring RustyNES's own pattern), and full-test/no_std/
    bench now also run on every push to main (previously tag-only), plus a weekly drift-net cron
    and manual dispatch. A new ci-success job is the one stable required-check name; branch
    protection on main now requires it. See docs/adr/0011.
  • A shared .github/actions/rust-setup composite action factors the pinned toolchain version and
    cache-key convention out of ci.yml/pages.yml into one place.
  • to-dos/LOCKSTEP-CHECKLIST.md — the process for re-checking RustyNES's own continuing
    development before scoping each subsequent rung in the parity ladder.

Fixed (caught in PR review, #76)

  • The rust-setup composite action pinned dtolnay/rust-toolchain@master (a floating ref);
    changed to @1.96, matching what ci.yml's jobs already used before this release.
  • The composite action's Linux frontend dependency list was missing libxkbcommon-x11-dev
    (present in CONTRIBUTING.md's documented list but never actually installed by the old inline
    per-job steps this action replaces) — added, and CONTRIBUTING.md reconciled to match exactly.

v1.4.0 "Convergence"

Choose a tag to compare

@github-actions github-actions released this 12 Jul 02:12
658e48a

Added

  • Window Size presets (native only) — View → Window Size offers 1x/2x/3x/4x (100%-400%) of
    the SNES native resolution, matching RustyNES; the app now launches at 3x by default instead of
    a fixed 512x448 window.
  • Libretro peripheral negotiationrustysnes-libretro now offers Mouse (both ports) and
    Super Multitap / Super Scope (port 2) via RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, mirroring
    bsnes's own libretro core's per-port device menu.
  • emu-thread mechanical re-sync — cheats, watchpoints, breakpoints, port2-peripheral
    selection, and per-voice audio mutes now apply in the threaded build too (previously only the
    synchronous drive path saw these changes).
  • emu-thread run-ahead + netplay-aware pause — run-ahead now runs on the emu thread via
    crate::rewind::step_with_run_ahead, only when actually configured (matching the synchronous
    path's own run_ahead > 0 branch, avoiding an avoidable per-frame allocation in the common
    disabled case — caught in PR review); netplay now actually functions under emu-thread (its
    NetplayState::drive call was previously dead code there, so netplay was silently
    non-functional in threaded builds), pausing the emu thread TOCTOU-safely via a new
    EmuControl::netplay_paused flag re-checked under the shared EmuCore lock. PresentBuffer
    now carries the framebuffer's (width, height) alongside its bytes, and the present path tracks
    the dims that actually match its staging buffer (Active::present_dims) rather than the emu's
    live (possibly-moved-on) resolution, so a run-ahead-peeked frame can never publish bytes for one
    resolution against dims from another (also caught in review). emu-thread is now clippy- and
    test-gated in CI for the first time (previously referenced only in a comment). Movies, Lua
    scripting, RetroAchievements, and rewind-recording remain intentionally unported to emu-thread
    — confirmed via RustyNES's own reference implementation, which doesn't port these to its thread
    either.

Fixed

  • Fullscreen crash on monitors wider/taller than 2048pxGfx requested
    wgpu::Limits::downlevel_webgl2_defaults() unconditionally on every target, capping
    max_texture_dimension_2d at 2048 even on native GPUs that support far more. Fullscreening on
    e.g. a 3440x1368 ultrawide made Surface::configure receive an out-of-range request and
    panic/abort (wgpu::Surface::configure has no recoverable error path here). Native now requests
    downlevel_defaults() and both targets call .using_resolution(adapter.limits()), raising the
    floor preset to match the real adapter; the granted limit is tracked at runtime and enforced
    everywhere the old hardcoded 2048 constant was.
  • Open bus during DMA/HDMA transfers (the "Speedy Gonzales stage 6-1" mechanism) — DMA/HDMA
    reads now update the open-bus latch, matching real hardware; writes deliberately do not, per a
    direct cross-check against ares' and bsnes' own CPU::Channel DMA implementation.
    superfx_boots_live_and_deterministic's 24 golden hashes were re-blessed with that citation
    trail as justification — see docs/scheduler.md §Open bus via DMA/HDMA.

v1.3.0 "Palimpsest"

Choose a tag to compare

@github-actions github-actions released this 11 Jul 19:00
23a0be5

Oracle/golden suites: all held, no regressions. The full workspace test suite (455 tests,
44 suites) plus the full --features test-roms ROM-oracle battery (28 tests, 17 suites via
-p rustysnes-test-harness --release) are green; no golden hash changed. fmt/clippy clean
across every feature combination this feature touches (default, hd-pack, full,
emu-thread,hd-pack, plus the pre-existing debug-hooks/scripting/cheats/netplay/
retroachievements lanes); no_std build clean; both wasm32 frontends (wasm-winit default,
wasm-canvas) build clean via real trunk build --release runs; rustysnes-libretro builds
clean.

Added

  • HD texture packs (hd-pack feature, off by default) — replace individual 8×8 tiles with
    higher-resolution PNG art while the accuracy-critical core stays completely pack-agnostic
    (docs/adr/0010). rustysnes-ppu computes a palette-inclusive XXH3-64 tile-identity hash
    (hdtag::hash_tile, hashed into a fixed stack buffer — no heap allocation on the rendering hot
    path) and records it per composited pixel into a write-only Ppu::tile_tags() side-buffer,
    populated only when Ppu::set_hd_pack_tagging(true) is on; leaving it at its default false
    is proven byte-identical to every prior release
    (hd_pack_tagging_toggle_does_not_alter_framebuffer_output), and the whole mechanism compiles
    out entirely — not just runtime-disabled — when the hd-pack feature is off. The frontend owns
    everything pack-specific: a versioned pack.toml manifest + PNG loader (crate::hd_pack,
    pure-Rust png decode, path-traversal-safe, duplicate-hash-rejecting), a pure CPU compositor
    (crate::hd_compositor::composite, fully unit-testable without a GPU adapter), a Settings →
    Video pack selector (dynamic ComboBox, populated per-ROM via the same SHA-256 identity
    save-states already use), and config.video.hd_pack_name persistence with automatic
    re-selection on ROM load. The compositor is wired into the live wgpu present path: Gfx's
    previously fixed MAX_W × MAX_H streaming texture now grows on demand
    (Gfx::ensure_texture_capacity, capped at this device's actual downlevel-WebGL2
    max_texture_dimension_2d) to fit the composited output at a fixed 2× upscale, with the
    no-pack-active path staying pixel-identical to before (the texture never grows past its
    original allocation unless a pack is actually active). Not yet built, honestly tracked:
    a user-configurable upscale factor (fixed at 2× for now) and emu-thread-build compositing
    (that build's framebuffer arrives via a lock-free handoff with no equivalent TileTag channel
    yet). See docs/ppu.md §HD texture pack TileTag recording hook, docs/frontend.md §HD
    texture packs, and docs/adr/0010.

Process note: all three feature PRs (#66, #67, #68) went through the full branch → CI →
automated bot review → fix → reply → resolve → green → squash-merge ceremony. Real findings
addressed along the way: a heap allocation on the PPU rendering hot path, a path-traversal
vulnerability in the pack loader, a memory-pre-allocation DoS vector sized off an untrusted PNG
header, an integer-overflow risk in the compositor's coordinate math, a stale-tile-tags bug when
tagging was toggled off mid-session, an active-pack-cleared-on-a-failed-ROM-load bug, a
redundant per-frame ROM re-hash, and a texture-capacity cap that was enforced by the caller but
not the function itself. Two Gemini suggestions were investigated and found to not actually
compile as proposed (a borrow-checker conflict in the Settings pack-selector closure) — verified
by trying them, not just trusting the diff, and documented with the reasoning inline.

Files changed: 15 files across 3 PRs — crates/rustysnes-ppu/src/hdtag.rs (new),
crates/rustysnes-ppu/src/{lib,render}.rs, crates/rustysnes-frontend/src/hd_pack.rs (new),
crates/rustysnes-frontend/src/hd_compositor.rs (new), crates/rustysnes-frontend/src/{emu, gfx,app,config,ui_shell,cli,save_states}.rs, crates/rustysnes-core/Cargo.toml (new hd-pack
feature propagation), docs/{ppu,frontend}.md, docs/adr/0010-hd-texture-pack-system.md (new).

Testing evidence: cargo test --workspace (455 tests, 44 suites), cargo test -p rustysnes-test-harness --features test-roms --release (28 tests, 17 suites, zero regressions),
cargo clippy --workspace --all-targets -- -D warnings across every feature combination this
work touches, cargo fmt --all --check, RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps (both default and --features hd-pack), the no_std gate, two real trunk build --release runs (wasm-winit default + wasm-canvas), and cargo build -p rustysnes-libretro.
Manual verification: real headless (xvfb-run) launches of the native binary against a staged
ROM — with no pack configured (unaffected path), with a real generated pack at the default 2×
scale, and with the scale temporarily forced to 3× specifically to exercise the texture-growth
path — all ran clean with no panics or wgpu validation errors.