Skip to content

feat(libretro): add rustysnes-libretro core crate (v1.2.0) - #63

Merged
doublegate merged 3 commits into
mainfrom
feat/v1.2.0-libretro-core
Jul 11, 2026
Merged

doublegate merged 3 commits into
mainfrom
feat/v1.2.0-libretro-core

Conversation

@doublegate

Copy link
Copy Markdown
Owner

Summary

  • Adds rustysnes-libretro: a thin C-ABI wrapper over rustysnes_core::facade::EmuCore,
    loadable by RetroArch or any other libretro-compatible frontend. Zero duplicated emulation
    logic -- every hook is a translation between the libretro C ABI and EmuCore's existing safe
    Rust API.
  • Region-aware NTSC/PAL geometry+timing (corrected via RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO
    on the first on_run after a ROM loads, once the cart header's region byte is known), the
    S-DSP's real 32 kHz sample rate, coprocessor firmware auto-resolution from the frontend's
    system directory, Game Genie/Pro Action Replay cheat support, and raw WRAM/VRAM/SRAM
    memory-map pointers for RetroArch's SRAM autosave and RetroAchievements/cheat tooling.
  • New additive Bus::wram/wram_mut, Ppu::vram/vram_mut, Cart::sram_mut accessors
    support the memory-map pointers above.
  • retro_game_info is opaque in rust-libretro-sys 0.3.2's bindgen output (verified against
    the generated bindings) -- worked around via RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + a
    hand-rolled #[repr(C)] struct, the same proven pattern the sibling rustynes-libretro core
    uses.
  • Peripheral negotiation (Mouse/Super Scope/Multitap via RETRO_DEVICE_SUBCLASS) is a
    documented follow-up, not yet wired -- see docs/libretro.md.
  • Wires a cargo build -p rustysnes-libretro step into CI's lint job (link-verification for
    the FFI crate); the existing --workspace clippy lines already cover it.

Test plan

  • cargo build -p rustysnes-libretro produces a valid ELF cdylib + staticlib with every
    required libretro C-ABI symbol exported (verified via nm -D)
  • cargo test --workspace (452 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 default/flags-off/full
  • cargo fmt --check
  • RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
  • cargo build -p rustysnes-core --target thumbv7em-none-eabihf --no-default-features (no_std)
  • Both wasm32 frontends (wasm-winit default, wasm-canvas) build clean via trunk build --release
  • Manual RetroArch smoke test (not automated -- see docs/libretro.md's verification checklist)

🤖 Generated with Claude Code

doublegate and others added 2 commits July 11, 2026 04:47
Adds Bus::wram/wram_mut, Ppu::vram/vram_mut, and Cart::sram_mut -- flat
byte/word-slice views over storage that previously only had word-at-a-time
debug accessors (peek_wram/vram_word) or a copy-based restore (load_sram).

Purely additive, zero behavior change. Needed by the upcoming
rustysnes-libretro crate's get_memory_data/get_memory_size (RETRO_MEMORY_
SYSTEM_RAM/VIDEO_RAM/SAVE_RAM), which hands RetroAchievements/cheat
tooling a raw pointer into live emulator memory rather than a copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A thin C-ABI wrapper over rustysnes_core::facade::EmuCore, loadable by
RetroArch or any other libretro-compatible frontend. Duplicates zero
emulation logic -- every hook is a translation between the libretro C
ABI and EmuCore's existing safe Rust API.

- Region-aware NTSC/PAL geometry+timing, corrected via
  RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO on the first on_run after a ROM
  loads (the true region is only known once the cart header is parsed).
- The S-DSP's real 32 kHz sample rate (matching rustysnes-frontend's
  own audio_core::SDSP_RATE convention), not a placeholder 44100.
- Coprocessor firmware (DSP-1..4/CX4) auto-resolved from the frontend's
  system directory, mirroring the native frontend's own policy.
- Game Genie/Pro Action Replay cheat support (on_cheat_set/
  on_cheat_reset -> rustysnes_core::cheat::decode -> Bus::set_cheats).
- Raw WRAM/VRAM/SRAM memory-map pointers (get_memory_data/
  get_memory_size) for RetroArch's own SRAM autosave and
  RetroAchievements/cheat tooling -- new additive Bus::wram/wram_mut,
  Ppu::vram/vram_mut, Cart::sram_mut accessors support this.
- retro_game_info is opaque in rust-libretro-sys 0.3.2's bindgen
  output (verified against the generated bindings); worked around via
  RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + a hand-rolled repr(C) struct,
  the same proven pattern the sibling rustynes-libretro core uses.

Peripheral negotiation (Mouse/Super Scope/Multitap via
RETRO_DEVICE_SUBCLASS) is a documented follow-up, not yet wired --
see docs/libretro.md.

Verified: cargo build produces a valid ELF cdylib + staticlib with
every required libretro C-ABI symbol exported (checked via nm); full
workspace suite, the ROM-oracle battery (28 tests/17 suites, zero
regressions), clippy clean across default/flags-off/full, fmt clean,
doc build clean, no_std clean, both wasm32 frontends build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 11, 2026 09:14

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces 'rustysnes-libretro', a new libretro core wrapper for the emulator, along with raw memory accessors in the core crates and updated documentation. The code review feedback highlights three important improvement opportunities in the libretro implementation: avoiding an unnecessary ROM buffer copy during loading, eliminating heap allocations in the hot path of save-state serialization, and gracefully handling a potential panic if the environment callback is missing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/rustysnes-libretro/src/lib.rs
Comment thread crates/rustysnes-libretro/src/lib.rs
Comment thread crates/rustysnes-libretro/src/lib.rs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new rustysnes-libretro crate that exposes RustySNES as a libretro-compatible core by wrapping rustysnes_core::facade::EmuCore, and updates core APIs/docs/CI to support that embedding use case.

Changes:

  • Introduces crates/rustysnes-libretro implementing the libretro lifecycle, region-aware AV info correction, SRAM/WRAM/VRAM memory maps, and cheat hooks.
  • Adds raw memory accessors (Bus::{wram,wram_mut}, Ppu::{vram,vram_mut}, Cart::sram_mut) plus unit tests for these surfaces.
  • Updates documentation/roadmap/changelog and adds a CI link/build verification step for the new FFI crate.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
to-dos/VERSION-PLAN.md Adds an in-progress v1.2.0 plan entry describing the libretro core + follow-ups.
to-dos/ROADMAP.md Updates roadmap narrative to include v1.2.0 libretro/facade work.
README.md Links new libretro docs and updates current release/in-progress sections.
docs/STATUS.md Marks current release as v1.1.0 and documents v1.2.0 work-in-progress scope.
docs/libretro.md New documentation for building and manually validating the libretro core.
docs/architecture.md Updates crate graph + crate list to include rustysnes-libretro and facade embedding API.
crates/rustysnes-ppu/src/lib.rs Adds vram()/vram_mut() accessors + unit test.
crates/rustysnes-libretro/src/lib.rs New libretro core implementation (FFI boundary) wrapping EmuCore.
crates/rustysnes-libretro/Cargo.toml New crate manifest for cdylib/staticlib libretro target.
crates/rustysnes-core/src/bus.rs Adds wram()/wram_mut() accessors + unit test.
crates/rustysnes-cart/src/lib.rs Adds sram_mut() accessor + test coverage in existing test.
CHANGELOG.md Documents the new libretro core and related accessors under “Added”.
Cargo.toml Registers crates/rustysnes-libretro as a workspace member.
Cargo.lock Adds rust-libretro* dependency graph (incl. bindgen/clang-sys) and the new crate entry.
.github/workflows/ci.yml Adds cargo build -p rustysnes-libretro in CI to catch link/export issues early.

Comment thread docs/libretro.md
Comment thread crates/rustysnes-libretro/src/lib.rs
Comment thread crates/rustysnes-libretro/src/lib.rs Outdated
Comment thread crates/rustysnes-libretro/src/lib.rs Outdated
Comment thread crates/rustysnes-libretro/Cargo.toml
- on_load_game: pass the ROM byte slice straight to EmuCore::load_rom
  instead of an intermediate .to_vec() -- load_rom already copies the
  bytes into its own retained storage, so the extra copy was pure
  waste for what can be a multi-MB ROM.
- get_serialize_size/on_serialize: assign core.save_state()'s Vec
  directly instead of extend_from_slice-ing it into a second buffer --
  save_state() already allocates fresh each call, so there was no
  capacity to reuse, just a redundant full-snapshot copy on a path
  RetroArch's own rewind feature can hit once per frame.
- on_load_game's GET_GAME_INFO_EXT environment-callback fetch: replace
  .unwrap() with a graceful Err return if the frontend provides no
  callback (a spec violation, but still external input this crate
  shouldn't panic on).
- Verified the unused-libc claim is a false positive: rust_libretro's
  input_descriptors! macro expands to code referencing libc::c_char in
  the calling crate's scope, so it must stay a direct dependency here
  even though nothing in this crate's own source names it (confirmed
  by actually removing it: cargo check immediately failed to resolve
  the macro expansion). Left in place with an explanatory comment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@doublegate
doublegate merged commit 80acb02 into main Jul 11, 2026
6 checks passed
@doublegate
doublegate deleted the feat/v1.2.0-libretro-core branch July 11, 2026 09:26
@doublegate doublegate mentioned this pull request Jul 11, 2026
5 tasks done
doublegate added a commit that referenced this pull request Jul 11, 2026
Version bump + CHANGELOG/docs sync for the v1.2.0 "Phosphor" release,
closing out the Libretro-core + CRT/HQ2x-shader-pipeline arc (PRs #62,
#63, #64).

Also fixes a review finding: reintroduced an empty [Unreleased]
CHANGELOG section (Keep a Changelog, which this project's own header
commits to, expects it to persist above the latest dated release).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants