Skip to content

fix(config): save an APA boot to its own HDD + read config on a mini APA launch#208

Merged
NathanNeurotic merged 1 commit into
masterfrom
claude/apa-save-routing
Jul 17, 2026
Merged

fix(config): save an APA boot to its own HDD + read config on a mini APA launch#208
NathanNeurotic merged 1 commit into
masterfrom
claude/apa-save-routing

Conversation

@NathanNeurotic

Copy link
Copy Markdown
Owner

TL;DR — the APA instruction is already satisfied; verifying it found two fork regressions around it

"For APA, we can use the same exact thing OPL does but save with our usual cfg differential name to prevent conflicts with them. Then the piping just works ya know."

That already ships. The APA discovery/mount layer is upstream verbatim — same +OPL partition (indirected via hdd0:__common/OPL/conf_hdd.cfg), same pfs0: mount, same path rule, same 128M auto-create. checkLoadConfigHDD differs from upstream by exactly the filename literal: CONFIG_OPL_FILENAME (settings_riptopl.cfg) + a conf_riptopl.cfg legacy read-fallback. That is "OPL's mechanism + our differential name". No work needed.

(Deliberately not adopting wOPL's approach — it uses a version-scoped +wOPL_<VER>_<SUB> partition and a pfs0:wOPL/ folder, the opposite of what was asked.)

But the save/read routing wrapped around that mechanism had two dropped-from-upstream legs:

Fix 1 — an APA boot saved to a memory card instead of its own HDD

trySaveAlternateDevice ran MC -> MMCE -> BDM -> BDM-HDD -> HDD with no boot-device leg. It only runs when gBootDir is empty — and resolveBootDirToMass deliberately blanks a hdd0:<part>:pfs:/... boot dir (unopenable launch identity) so legacy discovery can find the APA config. So it's live for exactly the APA case: config loaded from pfs0:, then saved to a memory card. Upstream has the leg; the fork dropped it.

Not a verbatim restore, deliberately: upstream sizes its buffer char pwd[8], which can't hold an APA cwd (hdd0:+OPL:pfs:/ = 15 chars) — getcwd fails and upstream then strncmps an uninitialised stack buffer, so upstream's own leg is a no-op on the exact case it exists for. Sized and initialised properly: same intent, actually reached.

The pwd[8] in tryAlternateDevice's load path is left alone on purpose — it's only a probe-order hint (checkLoadConfigHDD runs unconditionally there anyway), and byte-identical APA discovery is the point of the instruction.

Fix 2 — a mini APA launch read no config at all

miniInit dropped upstream's if (!(ret & CONFIG_OPL)) { HDD_MODE -> checkLoadConfigHDD; ... }; only the BDM leg got a replacement. So an argv mini launch on an APA HDD ran on pure defaults for gPS2Logo, gExitPath, gHDDSpindown, hddCacheSize and every Neutrino global — with pfs0: mounted a few lines earlier and the config sitting right there, unread. Restored.

Deliberately NOT touched

  • First-run config is born on a memory card whenever one is inserted. That's upstream parity (identical sysCheckMC early return + card-less fallback, with an explicit upstream comment that they don't want users in alternate mode on a first launch). Moving the APA config home to the HDD is a doctrine change, not a bug fix — your call, and it needs HW. It also can't be done by gating that early return alone: control then lands on the mass0: probe and the config would go to a USB stick instead.
  • The pfs0: one-slash asymmetry (checkLoadConfigHDD probes pfs0:settings_riptopl.cfg, configInit writes pfs0:/...) — upstream ships the identical mismatch and it's proven on hardware.

Known, not fixed here (wants its own PR + testing)

checkLoadConfigMC re-homes the config sets to a concrete card even when it finds nothing — so with two cards inserted, checkMCFolder can create mc0:OPL/ and the toast say mc0 while the write lands in mc1:OPL/. Upstream has no checkLoadConfigMC at all; its sets keep the mc?: wildcard, which checkFile resolves to the same card getmcID() reports, so location/folder/toast always agree. It's the highest-traffic path in the tree (bites any first run with two cards, not just APA).

Validation

Builds clean (make opl.elf, exit 0). HW-pending — APA/PFS can't be exercised with any fidelity on PCSX2.

🤖 Generated with Claude Code

…ni APA launch

Verifying the maintainer's APA instruction ("use the same exact thing OPL does but save with our usual cfg
differential name") found the MECHANISM already correct -- and two fork regressions in the SAVE/READ routing
wrapped around it.

ALREADY CORRECT, no change needed: the APA discovery/mount layer is upstream verbatim (same +OPL partition
via hdd0:__common/OPL/conf_hdd.cfg, same pfs0: mount, same path rule, same 128M auto-create), and
checkLoadConfigHDD differs from upstream by exactly the filename literal -- CONFIG_OPL_FILENAME
(settings_riptopl.cfg) plus a conf_riptopl.cfg legacy read-fallback. That IS "OPL's mechanism + our
differential name". Deliberately NOT adopting wOPL's approach here: it uses a version-scoped
"+wOPL_<VER>_<SUB>" partition and a pfs0:wOPL/ folder, which is the opposite of what was asked.

FIX 1 -- trySaveAlternateDevice had no boot-device leg. This function only runs with an EMPTY gBootDir, and
the common way to get there is an APA/uLE boot: resolveBootDirToMass DELIBERATELY blanks a
"hdd0:<part>:pfs:/..." boot dir (an unopenable launch identity) so legacy discovery can find the real APA
config. The order was "MC -> MMCE -> BDM -> BDM-HDD -> HDD", so an APA user's settings were written to a
MEMORY CARD in preference to their own HDD -- config LOADED from pfs0: and saved somewhere else. Upstream has
a boot-device-first leg; the fork dropped it. Restored, so "the boot device is the config home, mc is only
the fallback" holds here too.

  Not a verbatim restore, for a reason: upstream sizes its buffer `char pwd[8]`, which cannot hold an APA cwd
  ("hdd0:+OPL:pfs:/" is 15 chars). getcwd fails and upstream then strncmp's an UNINITIALISED stack buffer --
  so upstream's own leg is a no-op on the exact case it exists for. Sized and initialised properly: same
  intent, actually reached. The pwd[8] in tryAlternateDevice's LOAD path is left ALONE on purpose -- it is
  only a probe-ORDER hint (checkLoadConfigHDD runs unconditionally there anyway) and byte-identical APA
  discovery is the point of the instruction.

FIX 2 -- miniInit had no config fallback. The fork dropped upstream's
`if (!(ret & CONFIG_OPL)) { HDD_MODE -> checkLoadConfigHDD; BDM_MODE -> checkLoadConfigBDM; }` and only the
BDM leg got a replacement (resolveBootDirToMass). So an argv "mini" launch on an APA HDD read NO config and
ran on pure DEFAULTS for gPS2Logo, gExitPath, gHDDSpindown, hddCacheSize and every Neutrino global -- with
pfs0: mounted a few lines earlier and the config sitting right there, unread. Restored.

DELIBERATELY NOT TOUCHED:
- The first-run "config is born on a memory card whenever one is inserted" behaviour. That is UPSTREAM
  PARITY (upstream carries the identical sysCheckMC early return and card-less fallback, with an explicit
  comment that they don't want users in alternate mode on a first launch). Moving the APA config home to the
  HDD is a DOCTRINE change, not a bug fix, and needs the maintainer's call + HW -- it also cannot be done by
  gating that early return alone, because control then lands on the mass0: probe and the config would go to a
  USB stick instead.
- The pfs0: one-slash asymmetry (checkLoadConfigHDD probes "pfs0:settings_riptopl.cfg", configInit writes
  "pfs0:/..."): upstream ships the identical mismatch and it is proven on hardware.

KNOWN, NOT FIXED HERE (separate PR -- it is the highest-traffic path in the tree and wants its own testing):
checkLoadConfigMC re-homes the config sets to a CONCRETE card even when it finds nothing, so with TWO cards
inserted checkMCFolder can create mc0:OPL/ and the toast say mc0 while the write lands in mc1:OPL/. Upstream
has no checkLoadConfigMC at all -- its sets keep the "mc?:" wildcard, which checkFile resolves to the same
card getmcID() reports, so location/folder/toast always agree.

Builds clean (make opl.elf, exit 0). HW-pending: APA/PFS cannot be exercised with any fidelity on PCSX2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@NathanNeurotic
NathanNeurotic merged commit 1c36cc7 into master Jul 17, 2026
60 checks passed
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.

1 participant