diff --git a/ios_mcp/source/Installer.c b/ios_mcp/source/Installer.c
new file mode 100644
index 0000000..0fd2860
--- /dev/null
+++ b/ios_mcp/source/Installer.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 GaryOderNichts
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "menu.h"
+#include "imports.h"
+#include "gfx.h"
+#include "utils.h"
+#include "fsa.h"
+#include "mcp_install.h"
+#include "mcp_misc.h"
+
+#include
+#include
+
+static int install_title(const char *path, uint64_t tid, int mcpHandle, int index)
+{
+ gfx_printf(16, index, GfxPrintFlag_ClearBG, "Installing title: %08lx-%08lx...",
+ (uint32_t)(tid >> 32), (uint32_t)(tid & 0xFFFFFFFFU));
+
+ gfx_draw_rect_filled(16, index + (CHAR_SIZE_DRC_Y + 4), strnlen("Installing title: 00000000-00000000... Done!", 64) * CHAR_SIZE_DRC_X, CHAR_SIZE_DRC_Y, COLOR_BACKGROUND);
+
+ int x = 16 + (strnlen("Installing title: 00000000-00000000... ", 64) * CHAR_SIZE_DRC_X);
+
+ // only install to NAND
+ int res = MCP_InstallSetTargetDevice(mcpHandle, 0);
+ if (res < 0) {
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_printf(x, index, 0, "MCP_InstallSetTargetDevice: %x", res);
+ return 1;
+ }
+ res = MCP_InstallSetTargetUsb(mcpHandle, 0);
+ if (res < 0) {
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_printf(x, index, 0, "MCP_InstallSetTargetUsb: %x", res);
+ return 1;
+ }
+
+ // TODO: async installations
+ res = MCP_InstallTitle(mcpHandle, path);
+ if (res < 0) {
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_printf(x, index, 0, "Failed: %x", res);
+ return 1;
+ }
+
+ gfx_set_font_color(COLOR_SUCCESS);
+ gfx_print(x, index, 0, "Done!");
+ gfx_set_font_color(COLOR_PRIMARY);
+ return 0;
+}
+
+static void try_batch_install(int mcpHandle)
+{
+ uint32_t index = 16 + 8 + 2 + 8;
+ int dir_handle;
+ char path[1024] = "/vol/storage_recovsd/install/";
+ int res = FSA_OpenDir(fsaHandle, path, &dir_handle);
+ if (res < 0) {
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_printf(16, index, GfxPrintFlag_ClearBG, "Failed to open install folder: %x", res);
+ return;
+ }
+
+ FSDirectoryEntry dir_entry;
+ size_t maxPathSize = strnlen(path, 64);
+ char *inPath = path + maxPathSize;
+ maxPathSize = 1023 - maxPathSize;
+ MCPInstallInfo info;
+ int ret;
+ int i = 0;
+ int j = 0;
+
+ while (FSA_ReadDir(fsaHandle, dir_handle, &dir_entry) >= 0) {
+ if (!(dir_entry.stat.flags & DIR_ENTRY_IS_DIRECTORY)) {
+ continue;
+ }
+
+ strncpy(inPath, dir_entry.name, maxPathSize);
+ ret = MCP_InstallGetInfo(mcpHandle, path, &info);
+ if (ret < 0) {
+ continue;
+ }
+
+ ret = install_title(path, info.titleId, mcpHandle, index);
+ ++i;
+ if (ret) {
+ break;
+ }
+
+ if (j < 20) {
+ ++j;
+ index += CHAR_SIZE_DRC_Y + 4;
+ } else {
+ j = 0;
+ index = 16 + 8 + 2 + 8;
+ }
+ }
+
+ if (!i) {
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_print(16, index, GfxPrintFlag_ClearBG, "No installable titles found!");
+ }
+}
+
+void option_InstallWUP(void)
+{
+ gfx_clear(COLOR_BACKGROUND);
+ drawTopBar("WUP Installer");
+
+ uint32_t index = 16 + 8 + 2 + 8;
+ gfx_print(16, index, 0, "Inititalising, please wait...");
+
+ int mcpHandle = IOS_Open("/dev/mcp", 0);
+ if (mcpHandle < 0) {
+ // TODO: Remove previous line
+ gfx_set_font_color(COLOR_ERROR);
+ gfx_printf(16, index, 0, "Failed to open /dev/mcp: %x", mcpHandle);
+ waitButtonInput();
+ return;
+ }
+
+ MCPInstallInfo info;
+ int res = MCP_InstallGetInfo(mcpHandle, "/vol/storage_recovsd/install", &info);
+ res < 0 ? try_batch_install(mcpHandle) : install_title("/vol/storage_recovsd/install", info.titleId, mcpHandle, index);
+
+ IOS_Close(mcpHandle);
+ waitButtonInput();
+}
diff --git a/ios_mcp/source/Installer.h b/ios_mcp/source/Installer.h
new file mode 100644
index 0000000..e49c20b
--- /dev/null
+++ b/ios_mcp/source/Installer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 GaryOderNichts
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+/**
+ * Install one or more titles.
+ */
+void option_InstallWUP(void);
diff --git a/ios_mcp/source/menu.c b/ios_mcp/source/menu.c
index 74e6b39..a368f7b 100644
--- a/ios_mcp/source/menu.c
+++ b/ios_mcp/source/menu.c
@@ -35,12 +35,12 @@
#include "DebugSystemRegion.h"
#include "SystemInformation.h"
#include "SubmitSystemData.h"
+#include "Installer.h"
static void option_SetColdbootTitle(void);
static void option_DumpSyslogs(void);
static void option_DumpOtpAndSeeprom(void);
static void option_LoadNetConf(void);
-static void option_InstallWUP(void);
static void option_EditParental(void);
static void option_Shutdown(void);
@@ -711,79 +711,6 @@ static void option_LoadNetConf(void)
FSA_CloseFile(fsaHandle, cfgHandle);
}
-static void option_InstallWUP(void)
-{
- gfx_clear(COLOR_BACKGROUND);
- drawTopBar("Installing WUP");
-
- uint32_t index = 16 + 8 + 2 + 8;
- gfx_print(16, index, 0, "Make sure to place a valid signed WUP directly in 'sd:/install'");
- index += CHAR_SIZE_DRC_Y + 4;
-
- int mcpHandle = IOS_Open("/dev/mcp", 0);
- if (mcpHandle < 0) {
- gfx_set_font_color(COLOR_ERROR);
- gfx_printf(16, index, 0, "Failed to open /dev/mcp: %x", mcpHandle);
- waitButtonInput();
- return;
- }
-
- gfx_print(16, index, 0, "Querying install info...");
- index += CHAR_SIZE_DRC_Y + 4;
-
- MCPInstallInfo info;
- int res = MCP_InstallGetInfo(mcpHandle, "/vol/storage_recovsd/install", &info);
- if (res < 0) {
- gfx_set_font_color(COLOR_ERROR);
- gfx_printf(16, index, 0, "Failed to get install info: %x", res);
- waitButtonInput();
-
- IOS_Close(mcpHandle);
- return;
- }
-
- gfx_printf(16, index, 0, "Installing title: %08lx-%08lx...",
- (uint32_t)(info.titleId >> 32), (uint32_t)(info.titleId & 0xFFFFFFFFU));
- index += CHAR_SIZE_DRC_Y + 4;
-
- // only install to NAND
- res = MCP_InstallSetTargetDevice(mcpHandle, 0);
- if (res < 0) {
- gfx_set_font_color(COLOR_ERROR);
- gfx_printf(16, index, 0, "MCP_InstallSetTargetDevice: %x", res);
- waitButtonInput();
-
- IOS_Close(mcpHandle);
- return;
- }
- res = MCP_InstallSetTargetUsb(mcpHandle, 0);
- if (res < 0) {
- gfx_set_font_color(COLOR_ERROR);
- gfx_printf(16, index, 0, "MCP_InstallSetTargetUsb: %x", res);
- waitButtonInput();
-
- IOS_Close(mcpHandle);
- return;
- }
-
- // TODO: async installations
- res = MCP_InstallTitle(mcpHandle, "/vol/storage_recovsd/install");
- if (res < 0) {
- gfx_set_font_color(COLOR_ERROR);
- gfx_printf(16, index, 0, "Failed to install: %x", res);
- waitButtonInput();
-
- IOS_Close(mcpHandle);
- return;
- }
-
- gfx_set_font_color(COLOR_SUCCESS);
- gfx_print(16, index, 0, "Done!");
- waitButtonInput();
-
- IOS_Close(mcpHandle);
-}
-
static void option_EditParental(void)
{
static const Menu parentalControlOptions[] = {