Skip to content

Commit 4a88a80

Browse files
Merge pull request #60 from NathanNeurotic/codex/locate-and-update-system.loadelf
Fix PopStarter argv0 selector handling
2 parents c5386a8 + 079676f commit 4a88a80

File tree

4 files changed

+227
-23
lines changed

4 files changed

+227
-23
lines changed

bin/POPSLDR/system.lua

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ end
7575
local APP_DIR_LOCAL = NormalizeDirPath(APP_DIR or BOOT_PATH_RAW)
7676
LOG("APP_DIR_NORM="..APP_DIR_LOCAL)
7777
LOG("APP_DIR_POPSTARTER_JOIN="..JoinPath(APP_DIR_LOCAL, "POPSTARTER.ELF"))
78+
local SELECTOR_MODE = "basename"
7879

7980
local function ResolveAsset(rel)
8081
return System.resolveAsset(rel) or JoinPath(APP_DIR_LOCAL, rel)
@@ -287,9 +288,9 @@ function PLDR.CheckPOPStarterDEPS(device)
287288
if device == UI.SCENES.GUSB then
288289
return doesFileExist("mass:/POPS/POPS_IOX.PAK")
289290
elseif device == UI.SCENES.GHDD then
290-
local a = HDD.MountPartition("hdd0:__common", 1, FIO_MT_RDONLY)
291+
local a = HDD.MountPartition("hdd0:__common", 0, FIO_MT_RDONLY)
291292
if a then
292-
return a, doesFileExist("pfs1:/POPS/POPS.ELF"), doesFileExist("pfs1:/POPS/IOPRP252.IMG")
293+
return a, doesFileExist("pfs0:/POPS/POPS.ELF"), doesFileExist("pfs0:/POPS/IOPRP252.IMG")
293294
else
294295
return a, false, false
295296
end
@@ -361,17 +362,17 @@ end
361362
function PLDR.HDD.CheckAvailableHddPopsParts()
362363
if not PLDR.HDD.HAS_CHECKED then --HDD is checked only once since it cannot be removed/replaced without damaging the console
363364
LOG("Checking available __.POPS Partitions")
364-
if HDD.MountPartition("hdd0:__.POPS", 1, FIO_MT_RDONLY) then
365+
if HDD.MountPartition("hdd0:__.POPS", 0, FIO_MT_RDONLY) then
365366
PLDR.HDD.MAINPART = true
366-
HDD.UMountPartition(1)
367+
HDD.UMountPartition(0)
367368
end
368369
LOG("__.POPS", PLDR.HDD.MAINPART)
369370
PLDR.HDD.FOUNDANY = PLDR.HDD.MAINPART
370371
for i=1, 9 do
371-
if HDD.MountPartition(("hdd0:__.POPS%d"):format(i), 1, FIO_MT_RDONLY) then
372+
if HDD.MountPartition(("hdd0:__.POPS%d"):format(i), 0, FIO_MT_RDONLY) then
372373
PLDR.HDD.EXTRAPARTS[i] = true
373374
PLDR.HDD.FOUNDANY = true
374-
HDD.UMountPartition(1)
375+
HDD.UMountPartition(0)
375376
end
376377
LOG("__.POPS"..i, PLDR.HDD.EXTRAPARTS[i])
377378
end
@@ -385,25 +386,25 @@ function PLDR.HDD.BuildGameList()
385386
PLDR.HDD.GAMEPARTS = {}
386387
if not PLDR.HDD.FOUNDANY then return end
387388
if PLDR.HDD.MAINPART then
388-
if HDD.MountPartition("hdd0:__.POPS", 1, FIO_MT_RDONLY) then
389+
if HDD.MountPartition("hdd0:__.POPS", 0, FIO_MT_RDONLY) then
389390
local start_index = #PLDR.GAMES
390-
PLDR.GetPS1GameLists("pfs1:/", true)
391+
PLDR.GetPS1GameLists("pfs0:/", true)
391392
for i = start_index + 1, #PLDR.GAMES do
392393
PLDR.HDD.GAMEPARTS[PLDR.GAMES[i]] = "hdd0:__.POPS"
393394
end
394-
HDD.UMountPartition(1)
395+
HDD.UMountPartition(0)
395396
end
396397
end
397398
for i=1, 9 do
398399
if PLDR.HDD.EXTRAPARTS[i] then
399-
if HDD.MountPartition("hdd0:__.POPS"..i, 1, FIO_MT_RDONLY) then
400+
if HDD.MountPartition("hdd0:__.POPS"..i, 0, FIO_MT_RDONLY) then
400401
local start_index = #PLDR.GAMES
401402
local partition = "hdd0:__.POPS"..i
402-
PLDR.GetPS1GameLists("pfs1:/", true)
403+
PLDR.GetPS1GameLists("pfs0:/", true)
403404
for j = start_index + 1, #PLDR.GAMES do
404405
PLDR.HDD.GAMEPARTS[PLDR.GAMES[j]] = partition
405406
end
406-
HDD.UMountPartition(1)
407+
HDD.UMountPartition(0)
407408
end
408409
end
409410
end
@@ -545,6 +546,34 @@ local function BuildPopstarterSelector(prefix, vcd_filename)
545546
return prefix..vcd_filename..".ELF"
546547
end
547548

549+
local function SelectPopstarterSelectorPrefix(device_page)
550+
if device_page == "USB" or device_page == "MMCE" or device_page == "SMB/MMCE" then
551+
return "XX."
552+
end
553+
if device_page == "HDD" then
554+
return ""
555+
end
556+
return "XX."
557+
end
558+
559+
local function BuildPopstarterSelectorPath(device_page, game_name)
560+
if game_name == nil or game_name == "" then
561+
return ""
562+
end
563+
if device_page == "HDD" then
564+
return "hdd0:__.POPS/"..game_name..".ELF"
565+
end
566+
if device_page == "USB" or device_page == "MMCE" or device_page == "SMB/MMCE" then
567+
return "mass:/POPS/XX."..game_name..".ELF"
568+
end
569+
return game_name..".ELF"
570+
end
571+
572+
local function DeriveGameNameFromSelection(raw_selection)
573+
local vcd_filename = ExtractVcdFilename(raw_selection or "")
574+
return SanitizeGameName(StripVcdExtension(vcd_filename))
575+
end
576+
548577
local function HasBootPrefix(basename, desired_prefix)
549578
if basename == nil or basename == "" or desired_prefix == nil or desired_prefix == "" then
550579
return false
@@ -627,7 +656,7 @@ local function EnsureHDDReadyForLaunch(game)
627656
end
628657
local partition = PLDR.HDD.GAMEPARTS[game] or "hdd0:__.POPS"
629658
result.mount_partition = partition
630-
result.mount_ok = HDD.MountPartition(partition, 1, FIO_MT_RDONLY)
659+
result.mount_ok = HDD.MountPartition(partition, 0, FIO_MT_RDONLY)
631660
return result
632661
end
633662

@@ -860,6 +889,12 @@ local function LaunchEngine(popstarter, argv, reboot_iop, context)
860889
context and context.bootparam or "unknown"
861890
)
862891
LaunchLog("LAUNCH: stage A argv_count:", exec_args and #exec_args or 0)
892+
LaunchLog(
893+
"LAUNCH: selector="..tostring(argv0),
894+
"popstarter="..tostring(popstarter),
895+
"reboot_iop="..tostring(reboot_iop)
896+
)
897+
LaunchLog("LAUNCH: loadELF argc (caller):", exec_args and #exec_args or 0)
863898
local rc
864899
if exec_args ~= nil and #exec_args > 0 and unpack_fn ~= nil then
865900
rc = System.loadELF(popstarter, reboot_iop, unpack_fn(exec_args))
@@ -984,10 +1019,37 @@ function PLDR.RunPOPStarterGame(gamelocation, game)
9841019
local fallback_exists = false
9851020
local bootparam_basename_used = normalized_basename
9861021
local prefix_used = HasBootPrefix(normalized_basename, prefix) and prefix or ""
987-
local vcd_filename = ExtractVcdFilename(game)
988-
local game_name = SanitizeGameName(StripVcdExtension(vcd_filename))
989-
local selector_prefix = "XX."
990-
local argv0_selector = BuildPopstarterSelector(selector_prefix, game_name)
1022+
local game_name = DeriveGameNameFromSelection(game)
1023+
if game_name == "" or string.upper(game_name) == "POPSTARTER" then
1024+
LaunchLog("LAUNCH: GameName derivation failed for selection:", game)
1025+
BlockLaunchFailure(
1026+
"GameName derivation failed",
1027+
popstarter,
1028+
device_page,
1029+
nil,
1030+
game,
1031+
APP_DIR_LOCAL,
1032+
nil,
1033+
nil
1034+
)
1035+
return
1036+
end
1037+
local selector_prefix = SelectPopstarterSelectorPrefix(device_page)
1038+
local argv0_selector = BuildPopstarterSelectorPath(device_page, game_name)
1039+
if selector_prefix == "" and string.upper(game_name) == "POPSTARTER" then
1040+
LaunchLog("LAUNCH: Internal error: game_base derived as POPSTARTER; refusing to launch.", game)
1041+
BlockLaunchFailure(
1042+
"Internal error: game_base derived as POPSTARTER; refusing to launch.",
1043+
popstarter,
1044+
device_page,
1045+
nil,
1046+
game,
1047+
APP_DIR_LOCAL,
1048+
nil,
1049+
nil
1050+
)
1051+
return
1052+
end
9911053
if boot_source_mode == "mass" and prefix_added and not bootparam_exists then
9921054
fallback_bootparam = EnsureTrailingSlash(pops_root)..game
9931055
fallback_exists = doesFileExist(fallback_bootparam)
@@ -1010,7 +1072,10 @@ function PLDR.RunPOPStarterGame(gamelocation, game)
10101072
LaunchLog("LAUNCH: vcd basename used:", bootparam_basename_used)
10111073
LaunchLog("LAUNCH: bootparam candidate:", bootparam, "exists:", tostring(bootparam_exists))
10121074
LaunchLog("LAUNCH: derived GameName:", game_name)
1075+
LaunchLog("LAUNCH: selector mode:", SELECTOR_MODE)
1076+
LaunchLog("LAUNCH: selector prefix:", selector_prefix)
10131077
LaunchLog("LAUNCH: argv0 selector:", argv0_selector)
1078+
LaunchLog("LAUNCH: loadELF argc (caller):", #argv)
10141079
if fallback_bootparam ~= nil then
10151080
LaunchLog("LAUNCH: bootparam fallback:", fallback_bootparam, "exists:", tostring(fallback_exists))
10161081
end

src/elf_loader/include/elf-loader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern "C" {
2222

2323
// Before call this method be sure that you have previously called sbv_patch_disable_prefix_check();
2424
int LoadELFFromFile(const char *filename, int argc, char *argv[]);
25+
int LoadELFFromFileExecPS2(const char *filename, int argc, char *argv[]);
2526

2627
/** Modify argv[0] when partition info should be kept
2728
*

src/elf_loader/src/elf.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,67 @@ static char *store_arg(const char *src, char *storage, size_t storage_size, size
5858
return dest;
5959
}
6060

61+
static void copy_span(char *dest, size_t dest_size, const char *start, size_t len) {
62+
if (!dest || dest_size == 0) {
63+
return;
64+
}
65+
if (!start) {
66+
dest[0] = '\0';
67+
return;
68+
}
69+
if (len >= dest_size) {
70+
len = dest_size - 1;
71+
}
72+
memcpy(dest, start, len);
73+
dest[len] = '\0';
74+
}
75+
76+
static void parse_selector_parts(const char *argv0, char *out_prefix, size_t out_prefix_size, char *out_game, size_t out_game_size) {
77+
const char *last_dot;
78+
const char *prefix = "XX.";
79+
size_t prefix_len = strlen(prefix);
80+
if (out_prefix_size > 0) {
81+
out_prefix[0] = '\0';
82+
}
83+
if (out_game_size > 0) {
84+
out_game[0] = '\0';
85+
}
86+
if (!argv0) {
87+
return;
88+
}
89+
last_dot = strrchr(argv0, '.');
90+
if (!last_dot) {
91+
copy_span(out_game, out_game_size, argv0, strlen(argv0));
92+
return;
93+
}
94+
if (strncmp(argv0, prefix, prefix_len) == 0) {
95+
copy_span(out_prefix, out_prefix_size, "XX", 2);
96+
copy_span(out_game, out_game_size, argv0 + prefix_len, (size_t)(last_dot - (argv0 + prefix_len)));
97+
return;
98+
}
99+
copy_span(out_game, out_game_size, argv0, (size_t)(last_dot - argv0));
100+
}
101+
102+
static void append_launch_log_line(const char *line) {
103+
int fd = open("launch.log", O_WRONLY | O_CREAT, 0666);
104+
if (fd < 0) {
105+
return;
106+
}
107+
lseek(fd, 0, SEEK_END);
108+
write(fd, line, strlen(line));
109+
close(fd);
110+
}
111+
112+
static void append_launch_log_fmt(const char *label, int index, const char *value) {
113+
char buffer[256];
114+
if (index >= 0) {
115+
snprintf(buffer, sizeof(buffer), "LAUNCH: %s[%d]=%s\n", label, index, value ? value : "(null)");
116+
} else {
117+
snprintf(buffer, sizeof(buffer), "LAUNCH: %s=%s\n", label, value ? value : "(null)");
118+
}
119+
append_launch_log_line(buffer);
120+
}
121+
61122
/* IMPORTANT: This method wipe memory where the loader is going to be allocated
62123
* This values come from the linkfile used by the loader.c
63124
MEMORY {
@@ -109,9 +170,17 @@ int LoadELFFromFileWithPartition(const char *filename, int argc, char *argv[]) {
109170
} else {
110171
return fd;
111172
}
173+
DPRINTF("LAUNCH: argc_in=%d argv_ptr=%s\n", argc, argv ? "set" : "null");
174+
{
175+
char argc_buf[32];
176+
snprintf(argc_buf, sizeof(argc_buf), "%d", argc);
177+
append_launch_log_fmt("argc_in", -1, argc_buf);
178+
}
179+
append_launch_log_fmt("argv_ptr", -1, argv ? "set" : "null");
112180
use_default_argv0 = (argc <= 0 || argv == NULL || argv[0] == NULL);
113181
new_argc = use_default_argv0 ? 1 : argc;
114182
DPRINTF("LAUNCH: argv0 source: %s\n", use_default_argv0 ? "resolved path" : "caller");
183+
append_launch_log_fmt("argv0_source", -1, use_default_argv0 ? "resolved path" : "caller");
115184
// Preparing filename and partition to be sent in the argv
116185
if (new_argc + 1 > kMaxArgc) {
117186
return -2;
@@ -134,18 +203,79 @@ int LoadELFFromFileWithPartition(const char *filename, int argc, char *argv[]) {
134203
launch_argv[new_argc] = NULL;
135204

136205
DPRINTF("LAUNCH: Using LoadExecPS2\n");
206+
DPRINTF("LAUNCH: exec path=%s\n", resolved_path);
137207
DPRINTF("LAUNCH: argc=%d\n", new_argc);
208+
DPRINTF("LAUNCH: use_default_argv0=%s argv0=%s\n",
209+
use_default_argv0 ? "true" : "false",
210+
launch_argv[0] ? launch_argv[0] : "(null)");
211+
DPRINTF("LAUNCH: argv0_final=%s\n", use_default_argv0 ? resolved_path : (launch_argv[0] ? launch_argv[0] : "(null)"));
212+
DPRINTF("LAUNCH: argv1=%s\n", launch_argv[1] ? launch_argv[1] : "(null)");
213+
DPRINTF("LAUNCH: argv2_is_null=%s\n", launch_argv[2] == NULL ? "yes" : "no");
214+
{
215+
char selector_prefix[32];
216+
char selector_game[128];
217+
parse_selector_parts(launch_argv[0], selector_prefix, sizeof(selector_prefix), selector_game, sizeof(selector_game));
218+
DPRINTF("LAUNCH: selector_prefix=%s selector_game=%s\n",
219+
selector_prefix[0] ? selector_prefix : "(none)",
220+
selector_game[0] ? selector_game : "(unknown)");
221+
DPRINTF("LAUNCH: selector_mode=%s\n", selector_prefix[0] ? "XX" : "NO_PREFIX");
222+
append_launch_log_fmt("selector_prefix", -1, selector_prefix[0] ? selector_prefix : "(none)");
223+
append_launch_log_fmt("selector_game", -1, selector_game[0] ? selector_game : "(unknown)");
224+
append_launch_log_fmt("selector_mode", -1, selector_prefix[0] ? "XX" : "NO_PREFIX");
225+
}
226+
append_launch_log_fmt("exec path", -1, resolved_path);
227+
{
228+
char argc_buf[32];
229+
snprintf(argc_buf, sizeof(argc_buf), "%d", new_argc);
230+
append_launch_log_fmt("argc", -1, argc_buf);
231+
}
232+
append_launch_log_fmt("use_default_argv0", -1, use_default_argv0 ? "true" : "false");
233+
append_launch_log_fmt("argv0", -1, launch_argv[0]);
234+
append_launch_log_fmt("argv1", -1, launch_argv[1]);
235+
append_launch_log_fmt("argv2_is_null", -1, launch_argv[2] == NULL ? "yes" : "no");
236+
append_launch_log_fmt("argv0_final", -1, use_default_argv0 ? resolved_path : launch_argv[0]);
138237
for (i = 0; i < new_argc; i++) {
139238
DPRINTF("LAUNCH: argv[%d]=%s\n", i, launch_argv[i] ? launch_argv[i] : "(null)");
239+
append_launch_log_fmt("argv", i, launch_argv[i]);
140240
}
141241
DPRINTF("LAUNCH: argv[%d] is NULL: %s\n", new_argc, launch_argv[new_argc] == NULL ? "yes" : "no");
242+
append_launch_log_fmt("argv_null", new_argc, launch_argv[new_argc] == NULL ? "yes" : "no");
142243
/* LoadExecPS2 should not return on success. */
143244
LoadExecPS2(resolved_path, new_argc, launch_argv);
144245
DPRINTF("LAUNCH: RETURNED rc=%d\n", -1);
246+
append_launch_log_line("LAUNCH: RETURNED rc=-1\n");
145247
return -1;
146248
}
147249

148250
int LoadELFFromFile(const char *filename, int argc, char *argv[])
149251
{
150252
return LoadELFFromFileWithPartition(filename, argc, argv);
151253
}
254+
255+
int LoadELFFromFileExecPS2(const char *filename, int argc, char *argv[])
256+
{
257+
t_ExecData elfdata;
258+
char resolved_path[256];
259+
int ret;
260+
261+
if (argc <= 0 || argv == NULL || argv[0] == NULL) {
262+
return -4;
263+
}
264+
if (resolve_exec_path(filename, resolved_path, sizeof(resolved_path)) < 0) {
265+
return -1;
266+
}
267+
DPRINTF("LAUNCH: Using ExecPS2\n");
268+
DPRINTF("POPSTARTER ExecPS2 argv0=%s\n", argv[0]);
269+
270+
SifInitRpc(0);
271+
SifLoadFileInit();
272+
ret = SifLoadElf(resolved_path, &elfdata);
273+
SifLoadFileExit();
274+
275+
if (ret != 0 || elfdata.epc == 0) {
276+
return -2;
277+
}
278+
279+
ExecPS2((void *)elfdata.epc, (void *)elfdata.gp, argc, argv);
280+
return -1;
281+
}

src/luasystem.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ static int lua_checkexist(lua_State *L){
491491
}
492492
extern "C" {
493493
int LoadELFFromFile(const char *filename, int argc, char *argv[]);
494+
int LoadELFFromFileExecPS2(const char *filename, int argc, char *argv[]);
494495
}
495496
static int lua_loadELF(lua_State *L)
496497
{
@@ -500,14 +501,21 @@ static int lua_loadELF(lua_State *L)
500501
const char *elftoload = luaL_checklstring(L, 1, &size);
501502
int rebootIOP = luaL_checkinteger(L, 2);
502503
int extra_args = argc - 2;
503-
char** p = (char**)malloc((extra_args + 1) * sizeof(const char*));
504+
static char selector_buf[256];
505+
static char *argv_static[2];
504506
printf("# Loading ELF '%s' iop_reboot=%d, extra_args=%d\n", elftoload, rebootIOP, extra_args);
505-
for (int x = 3; x <= argc; x++) {
506-
p[x-3] = (char*)luaL_checkstring(L, x);
507+
if (extra_args > 0) {
508+
const char *selector = luaL_checkstring(L, 3);
509+
snprintf(selector_buf, sizeof(selector_buf), "%s", selector ? selector : "");
510+
argv_static[0] = selector_buf;
511+
argv_static[1] = NULL;
512+
printf("# Loading ELF argv0='%s' argc=1\n", argv_static[0]);
513+
int rc = LoadELFFromFileExecPS2(elftoload, 1, argv_static);
514+
lua_pushinteger(L, rc);
515+
return 1;
507516
}
508-
p[extra_args] = NULL;
509-
int rc = LoadELFFromFile(elftoload, extra_args, p);
510-
free(p);
517+
printf("# Loading ELF argv0 default (argc=0)\n");
518+
int rc = LoadELFFromFile(elftoload, 0, NULL);
511519
lua_pushinteger(L, rc);
512520
return 1;
513521
}

0 commit comments

Comments
 (0)