fix(scanner): relax PS1 memcard trailing-frame check for emulator-formatted cards (fixes #3)#4
Closed
terafin wants to merge 1 commit into
Closed
Conversation
This was referenced Jun 7, 2026
…matted cards
validate_ps1_raw_memcard required frame 63 ("write test sector") at byte
offset 8064 to start with the same MC magic as frame 0. Real-hardware-
formatted PS1 cards do this; SwanStation / Beetle PSX / Duckstation
libretro do NOT — they either leave frame 63 zero-filled or write actual
game-state continuation bytes through it.
Net effect today: every RetroArch/SwanStation-produced PSX memcard is
rejected with "Skipping non-supported save (outside allowed console
families)", breaking the PSX sync path completely for Steam Deck /
RetroDECK users.
Repro on a Steam Deck running RetroDECK + SwanStation core: Tom Clancy's
Rainbow Six - Lone Wolf saves to a 131072-byte .srm with valid frame 0
(MC + correct checksum), valid frame 1-15 directory checksums, but
frame 63 bytes "03 00 00 00 80 0C 5A 27 ..." (game data, not MC).
Fix: drop the trailing-frame requirement. Frame 0 magic + checksum +
the 15 directory-frame checksums are already strong evidence of a real
PS1 memcard. The trailing-frame "write test sector" is a real-hardware
artifact emulators don't replicate.
Applied identically to all 3 helpers (mister/steamdeck/windows) since
the validator code is duplicated across them.
Tests (in helpers/steamdeck/src/scanner.rs, mirrors the existing inline
test module pattern):
- ps1_memcard_accepted_when_trailing_frame_lacks_mc_magic
THE FIX: real SwanStation Rainbow Six bytes at frame 63 -> accepted
- ps1_memcard_accepted_when_trailing_frame_is_zero_filled
Common emulator pattern -> accepted
- ps1_memcard_still_rejected_when_size_wrong
Regression guard -> wrong size still rejects
- ps1_memcard_still_rejected_when_frame0_magic_absent
Regression guard -> bad frame 0 still rejects
- ps1_memcard_still_rejected_when_directory_frame_checksum_broken
Regression guard -> bad directory checksum still rejects
- ps1_memcard_regression_strict_format_still_accepted
Regression guard -> real-hardware-strict format still accepted
Fixes #3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c6b5056 to
5130b5e
Compare
terafin
referenced
this pull request
in intarweb/SGM-Helper
Jun 8, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 11, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 11, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 12, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 13, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 13, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 14, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 14, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 14, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
intarweb-sync-bot Bot
referenced
this pull request
in intarweb/SGM-Helper
Jun 14, 2026
…C/MPK/CPK + TG-16) Four related libretro-vs-standalone scanner gaps in one batch — all fix real data-loss / silent-skip paths reported by SGM-Helper users on Steam Deck (RetroDECK) and SS1 (libretro cores), all touch helpers/*/scanner.rs (plus helpers/*/syncer.rs for the RTC dedup-key fix), all ship with happy-path + regression-guard tests. ## Part 1 — PS1 memcard trailing-frame relax (was PR #4, fixes #3) Real PS1 hardware writes "MC" magic at memcard frame 63 (the "write test sector" — bytes 8064-8191). SwanStation (libretro Duckstation port) and Beetle PSX leave frame 63 zero-filled OR write actual game-state continuation bytes through it. Validator was rejecting every libretro PS1 save with "outside allowed console families." Drops the over-strict trailing-frame check. Frame 0 magic + frames 1-15 directory frame checksums are sufficient — these are real structural validators a corrupt memcard would fail. Tests added: zero-filled trailing-frame accepted, non-MC bytes accepted, frame 0 magic still required, directory checksum still required, strict-format memcards still accepted (regression guard). ## Part 2 — Sega RetroDECK path-hints (was PR #6, fixes #5) RetroDECK's flatpak (net.retrodeck.retrodeck) writes saves under single-word lowercase directory names. The Sega classifier had matched some (megadrive, megacd, sega32x) but was missing several: gamegear, mastersystem, megacdjp, sega32xjp, saturnjp, sega32xna, megadrivejp. Adds the missing variants. Test enumerates all 11 cases (5 fixes + 6 regression guards proving the previously-working paths still classify correctly). ## Part 3 — Preserve .rtc / .mpk / .cpk through dedup (was PR #8, fixes #7) Helper's save_selection_key collapsed Pokemon Crystal.rtc and Pokemon Crystal.srm to the same key (stem-only), so the .rtc was silently dropped — losing the in-game real-time clock state. Same class of bug exists for N64 controller-pak data (.mpk / .cpk) which sits alongside cart .srm. Suffixes the dedup key with the extension when ext is rtc/mpk/cpk: "crystal:rtc" vs "crystal:srm" no longer collide. Also adds classifier size-check arm for gameboy .rtc (1..=64 bytes) so the small clock-state payload doesn't get filtered as "implausible save." Test: classify_accepts_tiny_rtc_files_for_gameboy with 8 / 13 / 32 / 48 / 64-byte .rtc payloads, regression guard that an 8-byte .srm is still rejected as bogus (proves the relaxation is .rtc-scoped). ## Part 4 — TurboGrafx-16 / PC Engine classifier (was PR #10, fixes #9) The classify_supported_save function had branches for every other Sega/Nintendo/Sony system but no TG-16 / PCEngine / SuperGrafx block. Saves from RetroArch's Beetle PCE Fast / Beetle SuperGrafx core were returning None and getting skipped with "outside allowed console families" — but they're well-formed BRAM/SRAM saves. Adds contains_any block matching pcengine|tgfx16|turbografx|supergrafx, an infer_nec_slug returning "tgfx16", and an is_plausible_save_for_system arm for tgfx16 covering .sav 2KB / 8KB and .brm 2KB BRAM sizes. Test: classify_recognizes_pcengine_tgfx16_paths enumerates RetroDECK, RetroArch, and Beetle subdir variants. ## Scope - All 4 fixes are additive: missing branches added, over-strict checks relaxed in a single direction. No previously-accepted save shape is now rejected. - All test coverage is parallel across helpers/mister, helpers/steamdeck, helpers/windows — the triplet structure stays in sync. Consolidates and supersedes PR #4 + PR #6 + PR #8 + PR #10. All four share helpers/*/scanner.rs and the same libretro-vs-standalone lens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3.
Problem
validate_ps1_raw_memcard(helpers/{mister,steamdeck,windows}/src/scanner.rs:1970) requires the trailing frame at byte offset 8064 ("write test sector", frame 63) to start with the sameMCmagic as frame 0. Real-hardware-formatted PS1 cards do this; SwanStation / Beetle PSX / Duckstation libretro do NOT — they either zero-fill frame 63 or write game-state continuation bytes through it.Net effect today: every RetroArch-produced PSX memcard is rejected at the helper layer with
"Skipping non-supported save (outside allowed console families)", breaking the PSX sync path entirely for Steam Deck / RetroDECK users. See #3 for full evidence including hexdumps.Fix
Drop the trailing-frame requirement. Frame 0 magic + checksum + the 15 directory-frame checksums are already strong evidence of a real PS1 memcard. The trailing-frame "write test sector" is a real-hardware artifact emulators don't replicate.
Applied identically to all 3 helpers (mister / steamdeck / windows) since the validator code is duplicated across them.
Tests
Added 6 new tests in
helpers/steamdeck/src/scanner.rs(mirrors the existing inline test module pattern withbuild_valid_ps1_memcard()):ps1_memcard_accepted_when_trailing_frame_lacks_mc_magicps1_memcard_accepted_when_trailing_frame_is_zero_filledps1_memcard_still_rejected_when_size_wrongps1_memcard_still_rejected_when_frame0_magic_absentps1_memcard_still_rejected_when_directory_frame_checksum_brokenps1_memcard_regression_strict_format_still_acceptedVerification flow
After merging + building + deploying the fork release:
psx, validator now passes, payload uploads to RSMskipped=1 → 0, PSX appears in the RSM API and lands on yuna underSony - PlayStation🤖 Generated with Claude Code