Skip to content

fix(scanner): add missing RetroDECK single-word Sega dir hints (fixes #5)#6

Closed
terafin wants to merge 1 commit into
joeblack2k:mainfrom
intarweb:feat/sega-retrodeck-path-hints
Closed

fix(scanner): add missing RetroDECK single-word Sega dir hints (fixes #5)#6
terafin wants to merge 1 commit into
joeblack2k:mainfrom
intarweb:feat/sega-retrodeck-path-hints

Conversation

@terafin

@terafin terafin commented Jun 7, 2026

Copy link
Copy Markdown

Fixes #5.

Problem

The Sega path-hint substring list in classify_supported_save (scanner.rs ~line 656) had some RetroDECK single-word directory names (megadrive, megacd, sega32x) but was missing gamegear and mastersystem. RetroDECK uses single-word lowercase directory names for ALL Sega systems by default, so saves at /saves/gamegear/ and /saves/mastersystem/ were being skipped at classification with "Skipping non-supported save (outside allowed console families)".

Live repro (2026-06-07): Defenders of Oasis on Game Gear via Genesis Plus GX core saved a 65,536-byte .srm to /run/media/mmcblk0p1/retrodeck/saves/gamegear/. Helper log:

Skipping non-supported save (outside allowed console families):
  /run/media/mmcblk0p1/retrodeck/saves/gamegear/Defenders of Oasis (USA, Europe).srm

Fix

Add the missing single-word variants to the Sega path-hint substring list:

  • gamegear (Game Gear)
  • mastersystem (Master System)
  • megacdjp, sega32xjp, sega32xna, saturnjp, megadrivejp (regional RetroDECK variants of already-recognized systems)

Applied identically to all 3 helpers (mister/steamdeck/windows) since the classifier code is duplicated across them.

Tests

New classify_recognizes_retrodeck_sega_single_word_dirs covers:

Input Expected slug Why
/saves/gamegear/Test.srm game-gear THE FIX — was being skipped
/saves/mastersystem/Test.srm master-system THE FIX — was being skipped
/saves/megadrive/Test.srm genesis regression guard
/saves/megacd/Test.srm sega-cd regression guard
/saves/sega32x/Test.srm sega-32x regression guard
/saves/genesis/Test.srm genesis regression guard (English variant)
/saves/megacdjp/Test.srm sega-cd FIX — JP variant
/saves/saturnjp/Test.srm saturn FIX — JP variant

Related

PCEngine / TGFX16 has no path-hint block at all in classify_supported_save — separate gap, will file as a follow-up issue if you want.

This is the 3rd helper-side PR in a session shaking out RetroDECK-related skips. Other PRs in flight: #4 (PS1 trailing-frame relax).

🤖 Generated with Claude Code

@terafin terafin force-pushed the feat/sega-retrodeck-path-hints branch from 15ea53c to a338066 Compare June 8, 2026 05:35
terafin referenced this pull request in intarweb/SGM-Helper Jun 8, 2026
classify_supported_save in scanner.rs has dedicated path-hint blocks
for Nintendo, Sega, NeoGeo and Sony families, but is MISSING any block
for NEC's TurboGrafx-16 / PC Engine family. Result: every TGFX16 save
from any source (RetroDECK / Mednafen / libretro Beetle PCE / standalone
emulators / MiSTer's own /media/fat/saves/TGFX16/ path on the SS1) is
silently skipped at classification with "outside allowed console
families" before the helper even attempts to upload.

This is the same family of gap as PR #6 (Sega RetroDECK single-word
dirs) — the classifier was never extended to recognize NEC paths at all.

Fix:
  - New if-block matching "pc engine" / "pcengine" / "pc-engine" /
    "pcenginecd" / "pc-engine-cd" / "turbografx" / "turbo grafx" /
    "tgfx16" / "/tg16/" / "/tg-cd/" / "supergrafx" / "/sgx/" / "/pce/" /
    "/pce-cd/" / "/pcecd/" / "nec - pc engine" / "nec - turbografx" /
    "mednafen-pce" / "beetle pce" — the substring set covers all
    common conventions across RetroDECK, MiSTer, libretro core names,
    and standalone emulator output.
  - New infer_nec_slug helper that returns "tgfx16" (single slug —
    MiSTer's TurboGrafx-16 core handles HuCard + CD under one core).
  - is_plausible_save_for_system gets a "tgfx16" extension entry
    (sav/srm/ram/bkr/brm) and a size match (HuCard 2KB SRAM, BRAM
    extensions up to 32KB).

Applied identically to all 3 helpers (mister/steamdeck/windows).

Tests (helpers/steamdeck/src/scanner.rs inline test module):
  - classify_recognizes_pcengine_tgfx16_paths covers 7 path variants:
    pcengine / pcenginecd / tg16 / tg-cd / supergrafx / "PC Engine" /
    "TurboGrafx" — each must classify as "tgfx16".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Sega path-hint substring list in classify_supported_save had some
single-word RetroDECK-style directory names (megadrive, megacd, sega32x)
but was missing gamegear and mastersystem. RetroDECK uses single-word
lowercase directory names for ALL Sega systems by default, so saves in
/saves/gamegear/ and /saves/mastersystem/ were being skipped at
classification with "Skipping non-supported save (outside allowed
console families)".

Net effect today: Game Gear and Master System saves from Steam Deck /
RetroDECK users are silently dropped, never uploaded.

Live repro: Defenders of Oasis on Game Gear via Genesis Plus GX core
saved a 65,536-byte .srm to /run/media/mmcblk0p1/retrodeck/saves/gamegear/
which the helper logged as 'outside allowed console families' and skipped.

Fix: add the missing single-word variants:
  - gamegear (Game Gear)
  - mastersystem (Master System)
  - megacdjp, sega32xjp, sega32xna, saturnjp, megadrivejp
    (regional RetroDECK variants of already-recognized systems)

Applied identically to all 3 helpers (mister/steamdeck/windows).

Tests (helpers/steamdeck/src/scanner.rs inline test module):
  - classify_recognizes_retrodeck_sega_single_word_dirs covers gamegear,
    mastersystem (the FIX), plus megadrive/megacd/sega32x/genesis as
    regression guards, plus megacdjp/saturnjp for the JP variants.

PCEngine/TGFX16 has no classifier block at all — separate gap, will file
follow-up issue.

Fixes #5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@terafin terafin force-pushed the feat/sega-retrodeck-path-hints branch from a338066 to cfb9064 Compare June 8, 2026 06:19
terafin referenced this pull request in intarweb/SGM-Helper Jun 8, 2026
classify_supported_save in scanner.rs has dedicated path-hint blocks
for Nintendo, Sega, NeoGeo and Sony families, but is MISSING any block
for NEC's TurboGrafx-16 / PC Engine family. Result: every TGFX16 save
from any source (RetroDECK / Mednafen / libretro Beetle PCE / standalone
emulators / MiSTer's own /media/fat/saves/TGFX16/ path on the SS1) is
silently skipped at classification with "outside allowed console
families" before the helper even attempts to upload.

This is the same family of gap as PR #6 (Sega RetroDECK single-word
dirs) — the classifier was never extended to recognize NEC paths at all.

Fix:
  - New if-block matching "pc engine" / "pcengine" / "pc-engine" /
    "pcenginecd" / "pc-engine-cd" / "turbografx" / "turbo grafx" /
    "tgfx16" / "/tg16/" / "/tg-cd/" / "supergrafx" / "/sgx/" / "/pce/" /
    "/pce-cd/" / "/pcecd/" / "nec - pc engine" / "nec - turbografx" /
    "mednafen-pce" / "beetle pce" — the substring set covers all
    common conventions across RetroDECK, MiSTer, libretro core names,
    and standalone emulator output.
  - New infer_nec_slug helper that returns "tgfx16" (single slug —
    MiSTer's TurboGrafx-16 core handles HuCard + CD under one core).
  - is_plausible_save_for_system gets a "tgfx16" extension entry
    (sav/srm/ram/bkr/brm) and a size match (HuCard 2KB SRAM, BRAM
    extensions up to 32KB).

Applied identically to all 3 helpers (mister/steamdeck/windows).

Tests (helpers/steamdeck/src/scanner.rs inline test module):
  - classify_recognizes_pcengine_tgfx16_paths covers 7 path variants:
    pcengine / pcenginecd / tg16 / tg-cd / supergrafx / "PC Engine" /
    "TurboGrafx" — each must classify as "tgfx16".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot referenced this pull request in intarweb/SGM-Helper Jun 8, 2026
classify_supported_save in scanner.rs has dedicated path-hint blocks
for Nintendo, Sega, NeoGeo and Sony families, but is MISSING any block
for NEC's TurboGrafx-16 / PC Engine family. Result: every TGFX16 save
from any source (RetroDECK / Mednafen / libretro Beetle PCE / standalone
emulators / MiSTer's own /media/fat/saves/TGFX16/ path on the SS1) is
silently skipped at classification with "outside allowed console
families" before the helper even attempts to upload.

This is the same family of gap as PR #6 (Sega RetroDECK single-word
dirs) — the classifier was never extended to recognize NEC paths at all.

Fix:
  - New if-block matching "pc engine" / "pcengine" / "pc-engine" /
    "pcenginecd" / "pc-engine-cd" / "turbografx" / "turbo grafx" /
    "tgfx16" / "/tg16/" / "/tg-cd/" / "supergrafx" / "/sgx/" / "/pce/" /
    "/pce-cd/" / "/pcecd/" / "nec - pc engine" / "nec - turbografx" /
    "mednafen-pce" / "beetle pce" — the substring set covers all
    common conventions across RetroDECK, MiSTer, libretro core names,
    and standalone emulator output.
  - New infer_nec_slug helper that returns "tgfx16" (single slug —
    MiSTer's TurboGrafx-16 core handles HuCard + CD under one core).
  - is_plausible_save_for_system gets a "tgfx16" extension entry
    (sav/srm/ram/bkr/brm) and a size match (HuCard 2KB SRAM, BRAM
    extensions up to 32KB).

Applied identically to all 3 helpers (mister/steamdeck/windows).

Tests (helpers/steamdeck/src/scanner.rs inline test module):
  - classify_recognizes_pcengine_tgfx16_paths covers 7 path variants:
    pcengine / pcenginecd / tg16 / tg-cd / supergrafx / "PC Engine" /
    "TurboGrafx" — each must classify as "tgfx16".

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

terafin commented Jun 8, 2026

Copy link
Copy Markdown
Author

Consolidated into #11 (squashed with the other 3 scanner.rs format-gap fixes under the same libretro-vs-standalone lens). Closing in favor of #11 — same fixes, single commit, fewer threads for the maintainer to track.

@terafin terafin closed this Jun 8, 2026
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>
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>
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.

Sega classifier path-hints miss RetroDECK single-word dirs ('gamegear', 'mastersystem')

1 participant