Skip to content

fix(validators): libretro/RetroDECK format compat (GBA signature + GB rtc)#11

Open
terafin wants to merge 1 commit into
joeblack2k:mainfrom
intarweb:feat/libretro-format-validators
Open

fix(validators): libretro/RetroDECK format compat (GBA signature + GB rtc)#11
terafin wants to merge 1 commit into
joeblack2k:mainfrom
intarweb:feat/libretro-format-validators

Conversation

@terafin

@terafin terafin commented Jun 8, 2026

Copy link
Copy Markdown

Summary

Two related libretro-vs-standalone validator gaps in one batch — both fix real data-loss paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores). Both stay strict for anonymous uploads, both ship with happy-path + regression-guard tests.

Consolidates and supersedes PR #8 + PR #9 — same files (nintendo_validation.go, raw_save_validation.go), same lens (libretro-vs-standalone format gaps), squashed to one commit.

Part 1 — GBA library-signature advisory under helper trust (was #8, fixes #7)

Standalone mGBA / VBA-M write a library version footer (EEPROM_V, SRAM_V, FLASH_V, FLASH1M_V, FLASH512_V) into the .srm. RetroArch's libretro-mGBA core does NOT — confirmed across both EA Sports games (007 - Everything or Nothing, header 3500DNOB) and canonical mGBA-targeted titles (Pokemon Emerald, sparse FRAM data). RSM's hasGBASignature() rejects every libretro GBA save → HTTP 422.

Adds SignatureAdvisoryWithHelperTrust bool to strictRawSaveValidationProfile. When true, a failing RequireSignature check is downgraded from reject → warning IFF HelperTrusted AND rom_sha1 present AND payload non-blank. Anonymous uploads or uploads without rom_sha1 still hard-reject. GBA's profile opts in.

Part 2 — Game Boy .rtc sidecar size validation (was #9)

RetroArch / libretro Game Boy cores write a small .rtc sidecar next to the canonical .srm for cartridges with real-time clock state (Pokemon Crystal, Pokemon Gold/Silver, Harvest Moon GBC). Observed sizes 8–48 bytes. The strict gameboy raw-save profile's AllowedSizes only listed canonical SRAM sizes (512..65536), so every .rtc upload rejected with HTTP 422 → silent data loss for clock state.

Adds optional `AllowedSizesByExt map[string]func(int) bool` to `strictRawSaveValidationProfile`. When the incoming extension matches a registered predicate, that predicate replaces the canonical size-set check for that extension only. Gameboy opts in for `.rtc` with `1..=64` bytes.

`.sav` / `.srm` / `.ram` / `.gme` still validate against `strictRawGBSizes` exactly as before — scoped relaxation, not a blanket loosening.

Tests (7 new)

  • `AcceptsLibretroGBASaveUnderHelperTrust` — happy path
  • `RejectsLibretroGBASaveWithoutHelperTrust` — anonymous upload still rejected
  • `RejectsLibretroGBASaveWithoutROMSHA1` — same
  • `RejectsBlankLibretroGBASave` — blank payload still rejected even under trust
  • `AcceptsTinyGameBoyRTCFile` — 8/13/32/48/64-byte `.rtc` happy path
  • `RejectsTinyGameBoySRMFile` — regression guard: 8-byte `.srm` still rejected, proves `.rtc`-scoped relaxation
  • `RejectsOversizedGameBoyRTCFile` — 65-byte `.rtc` rejected with new per-ext message

Scope

  • Only GBA opts into `SignatureAdvisoryWithHelperTrust`; only gameboy opts into `AllowedSizesByExt`. Every other system's behavior is byte-identical to before.
  • Pre-existing trust / blank / executable-payload / text-noise checks still run — only the size predicate and signature requirement get the per-extension / per-trust-context overrides.

🤖 Generated with Claude Code

… rtc)

Two related libretro-vs-standalone validator gaps in one batch — both fix
real data-loss paths reported by SGM-Helper users on Steam Deck (RetroDECK)
and SS1 (libretro cores), both stay strict for anonymous uploads, both
ship with happy-path + regression-guard tests.

## Part 1 — GBA library-signature advisory under helper trust  (was PR joeblack2k#8, fixes joeblack2k#7)

Standalone mGBA / VBA-M write a library version footer (`EEPROM_V`,
`SRAM_V`, `FLASH_V`, `FLASH1M_V`, `FLASH512_V`) into the .srm. RetroArch's
libretro-mGBA core does NOT — confirmed across both EA Sports games
(007 - Everything or Nothing, header `3500DNOB`) and canonical
mGBA-targeted titles (Pokemon Emerald, sparse FRAM data). RSM's
`hasGBASignature()` rejects every libretro GBA save → HTTP 422.

Adds `SignatureAdvisoryWithHelperTrust bool` to
`strictRawSaveValidationProfile`. When true, a failing `RequireSignature`
check is downgraded from reject → warning IFF (a) `HelperTrusted` AND
(b) `rom_sha1` present AND (c) payload non-blank. Anonymous uploads or
uploads without `rom_sha1` still hard-reject.

GBA's profile opts in. NES / SNES / Master System / Genesis are unchanged
because their `RequireSignature` is nil (no signature required at all).

## Part 2 — Game Boy .rtc sidecar size validation  (was PR joeblack2k#9)

RetroArch / libretro Game Boy cores write a small `.rtc` sidecar next
to the canonical `.srm` for cartridges with real-time clock state
(Pokemon Crystal, Pokemon Gold/Silver, Harvest Moon GBC). Observed
sizes 8–48 bytes. The strict gameboy raw-save profile's `AllowedSizes`
only listed canonical SRAM sizes (512..65536), so every `.rtc` upload
rejected with "game boy raw save size N is not recognized" → silent
data loss for clock state.

Adds optional `AllowedSizesByExt map[string]func(int) bool` to
`strictRawSaveValidationProfile`. When the incoming extension matches
a registered predicate, that predicate replaces the canonical size-set
check for that extension only. Other extensions fall through to
`AllowedSizes` unchanged. Gameboy opts in for `.rtc` with `1..=64` bytes.

`.sav` / `.srm` / `.ram` / `.gme` still validate against `strictRawGBSizes`
exactly as before — scoped relaxation, not a blanket loosening.

## Tests

- `TestNormalizeSaveInputAcceptsLibretroGBASaveUnderHelperTrust` — accepts
  libretro GBA save without library footer when HelperTrusted + rom_sha1
- `TestNormalizeSaveInputRejectsLibretroGBASaveWithoutHelperTrust` —
  regression guard: anonymous upload still rejected
- `TestNormalizeSaveInputRejectsLibretroGBASaveWithoutROMSHA1` — same
- `TestNormalizeSaveInputRejectsBlankLibretroGBASave` — blank payload
  still rejected even under helper trust
- `TestNormalizeSaveInputAcceptsTinyGameBoyRTCFile` — accepts 8/13/32/48/64
  byte gameboy `.rtc` payloads
- `TestNormalizeSaveInputRejectsTinyGameBoySRMFile` — regression guard:
  8-byte `.srm` still rejected, proves relaxation is `.rtc`-scoped
- `TestNormalizeSaveInputRejectsOversizedGameBoyRTCFile` — 65-byte
  `.rtc` rejected with new per-ext message

## Scope

- Only GBA opts into `SignatureAdvisoryWithHelperTrust`; only gameboy
  opts into `AllowedSizesByExt`. Every other system's behavior is
  byte-identical to before.
- Pre-existing trust / blank / executable-payload / text-noise checks
  still run — only the size predicate and signature requirement get
  the per-extension / per-trust-context overrides.

Consolidates and supersedes PR joeblack2k#8 (GBA advisory) + PR joeblack2k#9 (gameboy .rtc).
Both are scanner.rs/validator-relaxation fixes from the same lens
(libretro-vs-standalone format gaps).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

GBA validator rejects all RetroArch/RetroDECK saves — libretro-mGBA omits library signature footer

1 participant