Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ int sysGetDiscID(char *discID);
void sysInitDev9(void);
void sysShutdownDev9(void);
void sysReset();
// Deferred halves of the old sysReset module set (lazy boot, 2026-07-21). Idempotent one-shots.
void sysLoadAudioModules(void);
void sysLoadLaunchModules(void);
void sysExecExit(void);
int sysLaunchDisc(void); // boot the physical PS2 disc in the drive; <0 (stays in OPL) on failure
void sysPowerOff(void);
Expand Down
13 changes: 13 additions & 0 deletions src/opl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3000,6 +3000,12 @@ static void deferredInit(void)
guiSetBootStatusSticky(_l(_STR_BOOT_BUILDING_MENU)); // boot-step localizer (IO thread) -- reaching
// here means the device init chain cleared; see gui.c

// Launch-support modules (isofs + genvmc), moved OFF sysReset's boot-critical path (lazy boot,
// 2026-07-21). Loaded here on the IO worker: resident long before the menu becomes interactive,
// so no launch ever loads a module mid-sequence (Delta-4 doctrine). The autolaunch fork, which
// never reaches this handler, calls the same idempotent one-shot synchronously.
sysLoadLaunchModules();

// inform GUI main init part is over
struct gui_update_t *id = guiOpCreate(GUI_INIT_DONE);
if (id)
Expand Down Expand Up @@ -3049,6 +3055,9 @@ static void deferredAudioInit(void)
int ret;

guiSetBootStatusSticky(_l(_STR_BOOT_LOADING_SOUNDS)); // boot-step localizer (IO thread) -- see gui.c
// libsd + audsrv, moved OFF sysReset's boot-critical path (lazy boot, 2026-07-21): this handler
// is their consumers' natural seam -- audioInit binds audsrv right below.
sysLoadAudioModules();
audioInit();
ret = sfxInit(1);
if (ret < 0)
Expand Down Expand Up @@ -3341,6 +3350,10 @@ int main(int argc, char *argv[])
}

if (argc >= 5) {
// The autolaunch legs skip deferredInit (which loads these for the GUI path), and their
// launch prep needs isofs (ISO probing/PS2Logo) + genvmc (per-game VMC validation) resident
// BEFORE the launch sequence starts -- never load modules inside it (Delta-4 doctrine).
sysLoadLaunchModules();
/* argv[0] boot path
argv[1] game->startup
argv[2] str to u32 game->start_sector
Expand Down
52 changes: 44 additions & 8 deletions src/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ extern unsigned int size_eesync_irx;

#define MAX_MODULES 64
static void *g_sysLoadedModBuffer[MAX_MODULES];
// Deferred-loader latches (audio + launch-support one-shots below); cleared by sysReset.
static unsigned char sysAudioModsLoaded = 0;
static unsigned char sysLaunchModsLoaded = 0;
static s32 sysLoadModuleLock = -1;

#define ELF_MAGIC 0x464c457f
Expand Down Expand Up @@ -240,6 +243,10 @@ void sysReset()

// clears modules list
memset(g_sysLoadedModBuffer, 0, sizeof(g_sysLoadedModBuffer));
// ...and the deferred-loader latches: after an IOP reboot NOTHING is resident, so the audio and
// launch-support one-shots below must reload on their next call (CodeRabbit review of #252).
sysAudioModsLoaded = 0;
sysLaunchModsLoaded = 0;

// load modules
LOG("[IOMANX]:\n");
Expand All @@ -258,14 +265,13 @@ void sysReset()
sysLoadModuleBuffer(&poweroff_irx, size_poweroff_irx, 0, NULL);
LOG("[USBD]:\n");
sysLoadModuleBuffer(&usbd_irx, size_usbd_irx, 0, NULL);
LOG("[ISOFS]:\n");
sysLoadModuleBuffer(&isofs_irx, size_isofs_irx, 0, NULL);
LOG("[GENVMC]:\n");
sysLoadModuleBuffer(&genvmc_irx, size_genvmc_irx, 0, NULL);
LOG("[LIBSD]:\n");
sysLoadModuleBuffer(&libsd_irx, size_libsd_irx, 0, NULL);
LOG("[AUDSRV]:\n");
sysLoadModuleBuffer(&audsrv_irx, size_audsrv_irx, 0, NULL);
// isofs/genvmc/libsd/audsrv are NO LONGER loaded here (POPSLoader-shape lazy boot, maintainer
// directive 2026-07-21): they serialized four module DMAs onto the boot-critical path of EVERY
// flavor while nothing needs them before the menu. libsd+audsrv load in deferredAudioInit (their
// consumers' existing deferred seam); isofs+genvmc load via sysLoadLaunchModules() -- from
// deferredInit on the IO worker for the GUI path (resident long before any user launch, never AT
// launch per the Delta-4 no-module-loads-in-the-launch-sequence doctrine), and synchronously on
// the autolaunch fork which skips deferredInit entirely.

#ifdef PADEMU
int ds3pads = 1; // only one pad enabled
Expand All @@ -287,6 +293,36 @@ void sysReset()
poweroffSetCallback(&poweroffHandler, NULL);
}

// Deferred halves of the old sysReset module set (see the comment there). Both are idempotent
// one-shots that latch ONLY on full success (a transient load failure stays retryable -- the exact
// latch-on-failure class the 2026-07 atad/mmce fixes eradicated; CodeRabbit review of #252), and
// sysReset() clears the latches so an in-process IOP reboot reloads everything. Audio pair =
// deferredAudioInit's first act; launch pair = deferredInit (IO worker, GUI path) or the autolaunch
// fork (synchronous -- it skips deferredInit).
void sysLoadAudioModules(void)
{
if (sysAudioModsLoaded)
return;
LOG("[LIBSD]:\n");
int r0 = sysLoadModuleBuffer(&libsd_irx, size_libsd_irx, 0, NULL);
LOG("[AUDSRV]:\n");
int r1 = sysLoadModuleBuffer(&audsrv_irx, size_audsrv_irx, 0, NULL);
if (r0 >= 0 && r1 >= 0)
sysAudioModsLoaded = 1;
}

void sysLoadLaunchModules(void)
{
if (sysLaunchModsLoaded)
return;
LOG("[ISOFS]:\n");
int r0 = sysLoadModuleBuffer(&isofs_irx, size_isofs_irx, 0, NULL);
LOG("[GENVMC]:\n");
int r1 = sysLoadModuleBuffer(&genvmc_irx, size_genvmc_irx, 0, NULL);
if (r0 >= 0 && r1 >= 0)
sysLaunchModsLoaded = 1;
Comment on lines +302 to +323

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Propagate module-load failures to the callers.

The latch handling is now correct, but both helpers still discard failure status because they return void. deferredAudioInit() proceeds to audioInit(), and the launch paths proceed after sysLoadLaunchModules() even when either required module failed to load. Return an error and make the callers stop, report, or otherwise handle the dependent operation; otherwise the autolaunch path in particular may continue without a retry.

Suggested shape
-void sysLoadAudioModules(void)
+int sysLoadAudioModules(void)
 {
     if (sysAudioModsLoaded)
-        return;
+        return 0;
     ...
-    if (r0 >= 0 && r1 >= 0)
+    if (r0 >= 0 && r1 >= 0) {
         sysAudioModsLoaded = 1;
+        return 0;
+    }
+    return -1;
 }

Apply the same contract to sysLoadLaunchModules(), update include/system.h, and handle the return values at the src/opl.c call sites.

🧰 Tools
🪛 Cppcheck (2.21.0)

[style] 302-302: The function 'sysLoadAudioModules' is never used.

(unusedFunction)


[style] 314-314: The function 'sysLoadLaunchModules' is never used.

(unusedFunction)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/system.c` around lines 302 - 323, Change sysLoadAudioModules() and
sysLoadLaunchModules() to return a success/error status based on both
module-load results, update their declarations in include/system.h, and modify
deferredAudioInit() plus the launch-related callers in src/opl.c to check the
status and stop or report before proceeding with dependent initialization or
autolaunch.

}

static void poweroffHandler(void *arg)
{
sysPowerOff();
Expand Down