diff --git a/installer/Makefile b/installer/Makefile index 96b68fc..348ba5a 100644 --- a/installer/Makefile +++ b/installer/Makefile @@ -5,7 +5,7 @@ VERSION := 1.0.0 TITLE_ID := ORPC00001 CONTENT_ID := IV0000-ORPC00001_00-ORBISRPCSETUP000 -LIBS := -lc -lkernel -lc++ -lSceMsgDialog -lSceCommonDialog -lSceImeDialog -lSceSysmodule -lSceNet -lSceUserService -lSceSystemService +LIBS := -lc -lkernel -lc++ -lSceMsgDialog -lSceCommonDialog -lSceImeDialog -lSceSysmodule -lSceNet -lSceUserService -lSceSystemService -lScePad ASSETS := $(wildcard installer/assets/**/*) LIBMODULES := $(wildcard installer/sce_module/*) diff --git a/installer/installer.c b/installer/installer.c index 6e84970..72c2210 100644 --- a/installer/installer.c +++ b/installer/installer.c @@ -3,8 +3,8 @@ * One confirm up front; everything after flows forward. * * Payload placement is the whole job: one copy of orbisrpc.bin into - * /data/GoldHEN/bin/elf. Booting it is GoldHEN's payload menu (or - * AutoRun), never this app - an installer that also injects fails in + * /data/payloads. Booting it is the payload launcher reading + * /data/payloads, never this app - an installer that also injects fails in * ways the user cannot fix from the wizard. */ #include #include @@ -27,8 +27,8 @@ #define DAEMON_ELF "/app0/assets/daemon.elf" #define INST_DIR "/data/orbisRPC" #define INST_LOG "/data/orbisRPC/install.log" -/* The one place the payload lives: GoldHEN's bin/elf loader directory. */ -#define PAYLOAD_BIN "/data/GoldHEN/bin/elf/orbisrpc.bin" +/* The one place the payload lives: the payload launcher directory. */ +#define PAYLOAD_BIN "/data/payloads/orbisrpc.bin" /* Stage log: every copy step records errno + sizes to a file we can read * back over FTP. The dialog alone can't say WHICH stage failed. */ @@ -223,14 +223,14 @@ static int step_files(void){ int ok = -1; int mk = -1; mkdir(INST_DIR, 0777); - mk = mkdirs("/data/GoldHEN/bin/elf"); + mk = mkdirs("/data/payloads"); ilog("mkdirs", mk == 0 ? 0 : errno, -1, -1); if(mk == 0) ok = copy_file(DAEMON_ELF, PAYLOAD_BIN); snprintf(report, sizeof report, "Daemon payload:\n%s : %s%s%s%s\n\n" - "Start it from GoldHEN's payload menu (bin/elf), or enable\n" - "AutoRun for orbisrpc once and it boots with every jailbreak.", + "Start it from the payload launcher (/data/payloads)\n" + "and pick orbisrpc.", PAYLOAD_BIN, ok == 0 ? "OK" : "denied", ok == 0 ? "" : " [stage ", ok == 0 ? "" : g_stage, @@ -325,34 +325,23 @@ static void finish(int code){ } int main(void){ - int q; char welcome[256]; crash_guard(); if(ui_init() != 0) finish(1); - /* The system holds a splash screen over fresh apps: dialogs opened - * under it get auto-dismissed (the half-second flash) or never - * surface. Hide it once the UI layer is ready, then let the - * foreground transition settle before the first dialog. */ + /* Dismiss the PS4 splash so dialogs aren't auto-dismissed + * (the half-second flash) or never surface. No sleep needed. */ sceSystemServiceHideSplashScreen(); - sceKernelSleep(3); snprintf(welcome, sizeof welcome, "orbisRPC Setup %s\n\nInstalls the daemon payload and saves your Discord token.", SETUP_VERSION); - q = ui_confirm(welcome); - if(q != 1){ - /* Declined install: offer status, then exit. Forward only. */ - if(ui_confirm("Show daemon status instead?") == 1) - step_status(); - finish(0); - } + ui_ok(welcome); if(step_assets() != 0) finish(1); ilogv("assets-ok", 0, 0); if(step_files() != 0) finish(1); step_token(); ilogv("post-token", 0, 0); ui_ok("Setup " SETUP_VERSION " complete.\n\n" - "The payload is in GoldHEN's bin/elf. To start it, open GoldHEN's\n" - "payload menu and pick orbisrpc, or enable AutoRun for it once\n" - "so it starts on every jailbreak."); + "The payload is in /data/payloads. To start it, open the\n" + "payload launcher and pick orbisrpc."); finish(0); } diff --git a/installer/ui.c b/installer/ui.c index 5781116..9207cd4 100644 --- a/installer/ui.c +++ b/installer/ui.c @@ -10,8 +10,16 @@ #include #include #include +#include #include #include +#include +#include + +static void ime_dbg(const char *msg){ + int fd = open("/data/ime_debug.log", O_WRONLY|O_CREAT|O_APPEND, 0666); + if(fd >= 0){ write(fd, msg, strlen(msg)); close(fd); } +} static void base_init(OrbisMsgDialogParam *param){ memset(param, 0, sizeof(*param)); @@ -23,6 +31,7 @@ static void base_init(OrbisMsgDialogParam *param){ } static int ui_ready = 0; +static int ime_dialog_running = 0; int ui_init(void){ if(ui_ready) return 0; @@ -39,6 +48,8 @@ int ui_init(void){ * dialogs and system calls misbehave when these aren't up. */ if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE) != 0) return -1; if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE) != 0) return -1; + if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_PAD) != 0) return -1; + (void)scePadInit(); if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_COMMON_DIALOG) != 0) return -1; if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG) < 0) return -1; if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_IME_DIALOG) < 0){ @@ -74,7 +85,7 @@ int ui_ok(const char *msg){ OrbisMsgDialogUserMessageParam um; OrbisMsgDialogResult res; memset(&res, 0, sizeof res); - reap_stale(); + sceMsgDialogTerminate(); if(sceMsgDialogInitialize() < 0) return -1; base_init(¶m); memset(&um, 0, sizeof um); @@ -97,14 +108,17 @@ int ui_confirm(const char *msg){ OrbisMsgDialogUserMessageParam um; OrbisMsgDialogResult res; memset(&res, 0, sizeof res); - reap_stale(); + sceMsgDialogTerminate(); if(sceMsgDialogInitialize() < 0) return -1; base_init(¶m); memset(&um, 0, sizeof um); um.msg = msg; - /* Always return X as confirm, O as back: the dialog's - * fixed layout treats Circle as confirm, so invert the id. */ - um.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO; + /* Use the official OpenOrbis sample pattern (YESNO_FOCUS_NO). + * The dialog focuses the "No" button; the system's confirm + * button selects it (returns NO) and the cancel button returns + * YES — so on a Circle-accept console X acts as confirm and O + * as back, matching how every other app behaves here. */ + um.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO_FOCUS_NO; param.userMsgParam = &um; if(sceMsgDialogOpen(¶m) < 0){ sceMsgDialogTerminate(); return -1; } while(sceMsgDialogUpdateStatus() != ORBIS_COMMON_DIALOG_STATUS_FINISHED) @@ -112,7 +126,7 @@ int ui_confirm(const char *msg){ sceMsgDialogClose(); sceMsgDialogGetResult(&res); sceMsgDialogTerminate(); - return (res.buttonId == ORBIS_MSG_DIALOG_BUTTON_ID_NO) ? 1 : 0; + return res.buttonId == ORBIS_MSG_DIALOG_BUTTON_ID_YES; } static int progress_open = 0; @@ -125,6 +139,7 @@ int ui_progress_open(const char *msg){ * leaves the flag set with no dialog behind it. Reclaim it. */ ui_progress_close(); } + sceMsgDialogTerminate(); if(sceMsgDialogInitialize() < 0) return -1; base_init(¶m); param.mode = ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR; @@ -157,7 +172,6 @@ int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ static wchar_t wtitle[64]; static wchar_t wplace[64]; OrbisImeDialogSetting st; - OrbisDialogResult res; int32_t uid = 0; size_t i; if(!out || cap == 0 || cap > 200) return -1; @@ -188,27 +202,40 @@ int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ st.verticalAlignment = ORBIS_V_CENTER; st.placeholder = wplace; st.title = wtitle; - reap_stale(); - if(sceImeDialogInit(&st, NULL) < 0) return -1; - /* Pump until the OSK stops (IME uses its own status enum). Bounded: - * a dialog stuck in NONE (failed init we couldn't see) aborts instead - * of hanging the installer forever. */ + /* apollo-ps4 pattern: guard against re-entry, terminate stale + * state, then init. The dialog owns the running flag. */ + if(ime_dialog_running){ sceImeDialogTerm(); ime_dialog_running = 0; } + ime_dbg("ime_begin\n"); + if(sceImeDialogInit(&st, NULL) < 0){ ime_dbg("ime_init_fail\n"); return -1; } + ime_dialog_running = 1; { int spins = 0; OrbisDialogStatus ds; - while((ds = sceImeDialogGetStatus()) != ORBIS_DIALOG_STATUS_STOPPED){ - if(ds == ORBIS_DIALOG_STATUS_NONE && ++spins > 250){ + ds = sceImeDialogGetStatus(); + if(ds == ORBIS_DIALOG_STATUS_RUNNING) ime_dbg("ime_status_running\n"); + else if(ds == ORBIS_DIALOG_STATUS_NONE) ime_dbg("ime_status_none\n"); + else if(ds == ORBIS_DIALOG_STATUS_STOPPED) ime_dbg("ime_status_stopped\n"); + while(ime_dialog_running){ + ds = sceImeDialogGetStatus(); + if(ds == ORBIS_DIALOG_STATUS_STOPPED){ + OrbisDialogResult res; + memset(&res, 0, sizeof res); + sceImeDialogGetResult(&res); sceImeDialogTerm(); + ime_dialog_running = 0; + if(res.endstatus != ORBIS_DIALOG_OK) return 0; + break; + } + if(ds == ORBIS_DIALOG_STATUS_NONE && ++spins > 250){ + ime_dbg("ime_timeout\n"); + sceImeDialogTerm(); + ime_dialog_running = 0; return -1; } if(ds != ORBIS_DIALOG_STATUS_NONE) spins = 0; sceKernelUsleep(20000); } } - memset(&res, 0, sizeof res); - if(sceImeDialogGetResult(&res) < 0){ sceImeDialogTerm(); return -1; } - sceImeDialogTerm(); - if(res.endstatus != ORBIS_DIALOG_OK) return 0; for(i = 0; i < cap - 1 && wbuf[i]; i++) out[i] = (wbuf[i] < 128 && wbuf[i] >= 32) ? (char)wbuf[i] : '?'; out[i] = 0;