diff --git a/include/mocha/commands.h b/include/mocha/commands.h
index b08e95f..ed6e275 100644
--- a/include/mocha/commands.h
+++ b/include/mocha/commands.h
@@ -14,7 +14,8 @@ extern "C" {
#define IPC_CUSTOM_GET_MOCHA_API_VERSION 0xF8
typedef enum LoadRPXTargetEnum {
- LOAD_RPX_TARGET_SD_CARD = 0,
+ LOAD_RPX_TARGET_SD_CARD = 0,
+ LOAD_RPX_TARGET_EXTRA_REVERT_PREPARE = 0x42424242,
} LoadRPXTargetEnum;
typedef struct __attribute((packed)) {
diff --git a/include/mocha/mocha.h b/include/mocha/mocha.h
index 87751f6..984a31b 100644
--- a/include/mocha/mocha.h
+++ b/include/mocha/mocha.h
@@ -86,7 +86,54 @@ MochaUtilsStatus Mocha_UnlockFSClient(FSClient *client);
*/
MochaUtilsStatus Mocha_UnlockFSClientEx(int clientHandle);
-MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo);
+/**
+ * Set the .rpx that will be loaded the next time the homebrew wrapper application is launched (e.g. Health & Safety or Daily Log).
+ *
+ * Loading a .rpx from within a file (archive e.g. a WUHB) is supported.
+ * To achieve this, the fileoffset (offset inside file specified via path) and filesize (size of the .rpx) need to be set.
+ * If filesize is set to 0, the whole file (starting at fileoffset) will be loaded as .rpx
+ *
+ * The path is **relative** to the root of the given target device.
+ * The target LOAD_RPX_TARGET_EXTRA_REVERT_PREPARE will revert a prepare call.
+ *
+ * To launch the prepared RPX call Mocha_LaunchHomebrewWrapper if this call was successful.
+ *
+ * @param loadInfo Information about the .rpx replacement.
+ * @return MOCHA_RESULT_SUCCESS: Loading the next RPX will be redirected.
+ * MOCHA_RESULT_INVALID_ARGUMENT: The given loadInfo was NULL
+ * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.
+ * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.
+ * MOCHA_RESULT_UNKNOWN_ERROR: Failed to setup a redirect of RPX.
+ */
+MochaUtilsStatus Mocha_PrepareRPXLaunch(MochaRPXLoadInfo *loadInfo);
+
+/**
+ * Launches the wrapper app for launching .rpx
+ * To launch a RPX call `Mocha_PrepareRPXLaunch` before this function.
+ *
+ * see: `Mocha_LaunchRPX` to prepare and launch a RPX in one command.
+ *
+ * @return MOCHA_RESULT_SUCCESS: App is launching
+ * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.
+ * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.
+ * MOCHA_RESULT_NOT_FOUND: Not application that can be used as homebrew wrapper found.
+ */
+MochaUtilsStatus Mocha_LaunchHomebrewWrapper();
+
+/**
+ * Launches a given RPX by launching a wrapper application and replacing the RPX on the fly.
+ * See Mocha_PrepareRPXLaunch for more information.
+ *
+ * Note: Combines Mocha_PrepareRPXLaunch and Mocha_LaunchHomebrewWrapper.
+ * @param loadInfo
+ * @return MOCHA_RESULT_SUCCESS: Requested RPX will be launched
+ * MOCHA_RESULT_LIB_UNINITIALIZED: Library was not initialized. Call Mocha_InitLibrary() before using this function.
+ * MOCHA_RESULT_UNSUPPORTED_COMMAND: Command not supported by the currently loaded mocha version.
+ * MOCHA_RESULT_INVALID_ARGUMENT: The given loadInfo was NULL
+ * MOCHA_RESULT_NOT_FOUND: Not application that can be used as homebrew wrapper found.
+ * MOCHA_RESULT_UNKNOWN_ERROR: Failed to setup a redirect of RPX.
+ */
+MochaUtilsStatus Mocha_LaunchRPX(MochaRPXLoadInfo *loadInfo);
typedef struct WUDDiscKey {
uint8_t key[0x10];
diff --git a/source/utils.cpp b/source/utils.cpp
index fe23eaa..ae77858 100644
--- a/source/utils.cpp
+++ b/source/utils.cpp
@@ -4,6 +4,8 @@
#include
#include
#include
+#include
+#include
int mochaInitDone = 0;
uint32_t mochaApiVersion = 0;
@@ -131,13 +133,16 @@ MochaUtilsStatus Mocha_UnlockFSClientEx(int clientHandle) {
return MOCHA_RESULT_UNKNOWN_ERROR;
}
-MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo) {
+MochaUtilsStatus Mocha_PrepareRPXLaunch(MochaRPXLoadInfo *loadInfo) {
if (!mochaInitDone) {
return MOCHA_RESULT_LIB_UNINITIALIZED;
}
if (mochaApiVersion < 1) {
return MOCHA_RESULT_UNSUPPORTED_COMMAND;
}
+ if (!loadInfo) {
+ return MOCHA_RESULT_INVALID_ARGUMENT;
+ }
MochaUtilsStatus res = MOCHA_RESULT_UNKNOWN_ERROR;
int mcpFd = IOS_Open("/dev/mcp", (IOSOpenMode) 0);
if (mcpFd >= 0) {
@@ -148,13 +153,45 @@ MochaUtilsStatus Mocha_LoadRPXOnNextLaunch(MochaRPXLoadInfo *loadInfo) {
if (IOS_Ioctl(mcpFd, 100, io_buffer, sizeof(MochaRPXLoadInfo) + 4, io_buffer, 0x4) == IOS_ERROR_OK) {
res = MOCHA_RESULT_SUCCESS;
}
-
IOS_Close(mcpFd);
}
return res;
}
+MochaUtilsStatus Mocha_LaunchRPX(MochaRPXLoadInfo *loadInfo) {
+ auto res = Mocha_PrepareRPXLaunch(loadInfo);
+ if (res == MOCHA_RESULT_SUCCESS) {
+ res = Mocha_LaunchHomebrewWrapper();
+ if (res != MOCHA_RESULT_SUCCESS) {
+ MochaRPXLoadInfo loadInfoRevert;
+ loadInfoRevert.target = LOAD_RPX_TARGET_EXTRA_REVERT_PREPARE;
+ Mocha_PrepareRPXLaunch(&loadInfoRevert);
+ }
+ }
+ return res;
+}
+
+MochaUtilsStatus Mocha_LaunchHomebrewWrapper() {
+ if (!mochaInitDone) {
+ return MOCHA_RESULT_LIB_UNINITIALIZED;
+ }
+ if (mochaApiVersion < 1) {
+ return MOCHA_RESULT_UNSUPPORTED_COMMAND;
+ }
+ uint64_t titleID = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_HEALTH_AND_SAFETY);
+ if (!SYSCheckTitleExists(titleID)) { // Fallback to daily log app if H&S doesn't exist.
+ titleID = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_DAILY_LOG);
+ }
+ if (!SYSCheckTitleExists(titleID)) {
+ return MOCHA_RESULT_NOT_FOUND;
+ }
+
+ _SYSLaunchTitleWithStdArgsInNoSplash(titleID, nullptr);
+
+ return MOCHA_RESULT_SUCCESS;
+}
+
MochaUtilsStatus Mocha_ODMGetDiscKey(WUDDiscKey *discKey) {
if (!mochaInitDone) {
return MOCHA_RESULT_LIB_UNINITIALIZED;