Repository navigation
feat(libretro): add rustysnes-libretro core crate (v1.2.0) - #63
Conversation
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>
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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-libretroimplementing 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. |
- 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>
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).
Summary
rustysnes-libretro: a thin C-ABI wrapper overrustysnes_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 safeRust API.
RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFOon the first
on_runafter a ROM loads, once the cart header's region byte is known), theS-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.
Bus::wram/wram_mut,Ppu::vram/vram_mut,Cart::sram_mutaccessorssupport the memory-map pointers above.
retro_game_infois opaque inrust-libretro-sys0.3.2's bindgen output (verified againstthe generated bindings) -- worked around via
RETRO_ENVIRONMENT_GET_GAME_INFO_EXT+ ahand-rolled
#[repr(C)]struct, the same proven pattern the siblingrustynes-libretrocoreuses.
RETRO_DEVICE_SUBCLASS) is adocumented follow-up, not yet wired -- see
docs/libretro.md.cargo build -p rustysnes-libretrostep into CI'slintjob (link-verification forthe FFI crate); the existing
--workspaceclippy lines already cover it.Test plan
cargo build -p rustysnes-libretroproduces a valid ELFcdylib+staticlibwith everyrequired 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 warningsacross default/flags-off/fullcargo fmt --checkRUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-depscargo build -p rustysnes-core --target thumbv7em-none-eabihf --no-default-features(no_std)wasm-winitdefault,wasm-canvas) build clean viatrunk build --releasedocs/libretro.md's verification checklist)🤖 Generated with Claude Code