From 545d27870556ed487829075a450c864b2b8e344e Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 18 Sep 2022 13:04:02 +0200 Subject: [PATCH] Add a config menu with several options --- README.md | 38 +++++-- src/main.cpp | 114 ++++++++++++++++++- src/main.h | 12 +- src/modpackSelector.cpp | 246 ++++++++++++++++++++++++++++------------ src/modpackSelector.h | 12 +- 5 files changed, 325 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index d2ed841..8d1b06b 100644 --- a/README.md +++ b/README.md @@ -3,32 +3,45 @@ # SDCafiine Plugin ## What is SDCafiine -The main feature of this application is the **on-the-fly replacing of files**, which can be used used to loaded modified content from external media (**SD**). It hooks into the file system functions of the WiiU. Whenever a file is accessed, SDCafiine checks if a (modified) version of it present on the SD device, and redirect the file operations if needed. +The main feature of this plugin is the **on-the-fly replacing of files**, which can be used to load modified content from external media (**SD**). It hooks into the file system functions of the Wii U. Whenever a file is accessed, SDCafiine checks if a (modified) version is present on the SD card, and redirect the file operations if needed. -## Dependecies +## Dependencies Requires the [ContentRedirectionModule](https://github.com/wiiu-env/ContentRedirectionModule) to be loaded. -## Installation of the modules +## Installation of the plugin (`[ENVIRONMENT]` is a placeholder for the actual environment name.) 1. Copy the file `sdcafiine.wps` into `sd:/wiiu/environments/[ENVIRONMENT]/plugins`. 2. Requires the [WiiUPluginLoaderBackend](https://github.com/wiiu-env/WiiUPluginLoaderBackend) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`. 3. Requires the [ContentRedirectionModule](https://github.com/wiiu-env/ContentRedirectionModule) in `sd:/wiiu/environments/[ENVIRONMENT]/modules`. +## Usage +Via the plugin config menu (press L, DPAD Down and Minus on the gamepad) you can configure the plugin. The available options are the following: +- **Settings**: + - Enable SDCafiine: + - With this option you can globally enable/disable SDCafiine. If you're currently running a game you need to restart it before this option has an effect. +- **Advanced settings**: + - Auto apply the modpack if only one modpack exists: + - Skip the modpack selection screen if the current title only has one modpack to choose from. To boot the game without mods, you need to press X while "Preparing modpack" is shown on the screen. + - Skip "Preparing modpack..."-screen + - Skips the "Preparing modpack..."-screen which appears when auto booting into a single modpack for a title is activated. To run the game without mods you need to disable this option. + ### Installation of the mods -Before the mods can be loaded, they need to be copied to a SD Card. +Before the mods can be loaded, they need to be copied to a SD card. **In the following "root:/" is corresponding to the root of your SD card**. The basic filepath structure is this: ``` root:/sdcafiine/[TITLEID]/[MODPACK]/content/ <-- for game files. Maps to /vol/content/ +root:/sdcafiine/[TITLEID]/[MODPACK]/aoc/ <-- for DLC files. Maps to /vol/aoc/ ``` Replace the following: -- "[TITLEID]" need to be replaced the TitleID of the games that should be modded. A list of can be found [here](http://wiiubrew.org/w/index.php?title=Title_database#00050000:_eShop_and_disc_titles) (without the "-"). Example for SSBU "0005000010145000". Make sure to use the ID of the fullgame and not the update title ID. -- "[MODPACK]" name of the modpack. This folder name can be everything but "content" or "aoc". +- "[TITLEID]" need to be replaced the TitleID of the games that should be modded. A list of title ids can be found [here](http://wiiubrew.org/w/index.php?title=Title_database#00050000:_eShop_and_disc_titles) (without the "-"). Example for SSBU "0005000010145000". Make sure to use the ID of the fullgame and not the update title ID. +- "[MODPACK]" name of the modpack. Example path for the EUR version of SuperSmashBros for Wii U: ``` root:/sdcafiine/0005000010145000/SpecialChars/content/ <-- for game files. Maps to /vol/content/ +root:/sdcafiine/0005000010145000/SpecialChars/aoc/ <-- for DLC files. Maps to /vol/aoc/ ``` For replacing the file /vol/content/movie/intro.mp4, put a modified file into: @@ -36,15 +49,21 @@ For replacing the file /vol/content/movie/intro.mp4, put a modified file into: root:/sdcafiine/0005000010145000/SpecialChars/content/movie/intro.mp4 ``` -*NOTES: paths like "root:/sdcafiine/0005000010145000/content/" are still supported for compatibility, but **not recommended** * - ### Handling multiple mod packs -SDCafiine supports multiple different mods for a single game on the same SDCard. Each mod has an own subfolder. +SDCafiine supports multiple different mods for a single game on the same SD card. Each modpack has its own subdirectory. Example: ``` sd:/sdcafiine/0005000010145000/ModPack1/content/ sd:/sdcafiine/0005000010145000/ModPack2/content/ ``` +### "Delete" files via SDCafiine +If a game should not see or access a file anymore, it's possible to "delete"/"hide" it from the game without actually deleting it. +The process is similar to redirecting a file. But instead of creating a replacement file with the same name, you create an empty file with the prefix `.deleted_`. + +If you want to stop a game from accessing `/vol/content/assets/tree.bin` you need to create this file in your modpack. +``` +root:/sdcafiine/[TITLEID]/[MODPACK]/content/assets/.deleted_tree.bin +``` ## Buildflags @@ -57,7 +76,6 @@ Building via `make` only logs errors (via OSReport). To enable logging via the [ If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging. - ## Building using the Dockerfile It's possible to use a docker image for building. This way you don't need anything installed on your host system. diff --git a/src/main.cpp b/src/main.cpp index 3b27ce0..a0e8cd1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,32 +1,136 @@ +#include "main.h" #include "modpackSelector.h" #include "utils/logger.h" #include #include #include +#include +#include WUPS_PLUGIN_NAME("SDCafiine"); WUPS_PLUGIN_DESCRIPTION("SDCafiine"); -WUPS_PLUGIN_VERSION("0.1"); +WUPS_PLUGIN_VERSION(VERSION_FULL_RAW); WUPS_PLUGIN_AUTHOR("Maschell"); WUPS_PLUGIN_LICENSE("GPL"); WUPS_USE_WUT_DEVOPTAB(); +WUPS_USE_STORAGE("sdcafiine"); // Unqiue id for the storage api CRLayerHandle contentLayerHandle __attribute__((section(".data"))) = 0; +CRLayerHandle aocLayerHandle __attribute__((section(".data"))) = 0; + +bool gAutoApplySingleModpack = false; +bool gSkipPrepareIfSingleModpack = false; +bool gSDCafiineEnabled = true; INITIALIZE_PLUGIN() { // But then use libcontentredirection instead. ContentRedirectionStatus error; if ((error = ContentRedirection_InitLibrary()) != CONTENT_REDIRECTION_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE_ERR("Failed to init ContentRedirection. Error %d", error); + DEBUG_FUNCTION_LINE_ERR("Failed to init ContentRedirection. Error %s %d", ContentRedirection_GetStatusStr(error), error); OSFatal("Failed to init ContentRedirection."); } + + // Open storage to read values + WUPSStorageError storageRes = WUPS_OpenStorage(); + if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } else { + // Try to get value from storage + if ((storageRes = WUPS_GetBool(nullptr, AUTO_APPLY_SINGLE_MODPACK_STRING, &gAutoApplySingleModpack)) == WUPS_STORAGE_ERROR_NOT_FOUND) { + // Add the value to the storage if it's missing. + if (WUPS_StoreBool(nullptr, AUTO_APPLY_SINGLE_MODPACK_STRING, gAutoApplySingleModpack) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to store bool"); + } + } else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } + + if ((storageRes = WUPS_GetBool(nullptr, SDCAFIINE_ENABLED_STRING, &gSDCafiineEnabled)) == WUPS_STORAGE_ERROR_NOT_FOUND) { + // Add the value to the storage if it's missing. + if (WUPS_StoreBool(nullptr, SDCAFIINE_ENABLED_STRING, gSDCafiineEnabled) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to store bool"); + } + } else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } + + if ((storageRes = WUPS_GetBool(nullptr, SKIP_PREPARE_FOR_SINGLE_MODPACK_STRING, &gSkipPrepareIfSingleModpack)) == WUPS_STORAGE_ERROR_NOT_FOUND) { + // Add the value to the storage if it's missing. + if (WUPS_StoreBool(nullptr, SKIP_PREPARE_FOR_SINGLE_MODPACK_STRING, gSkipPrepareIfSingleModpack) != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to store bool"); + } + } else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); + } + + // Close storage + if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); + } + } + contentLayerHandle = 0; + aocLayerHandle = 0; } /* Entry point */ ON_APPLICATION_START() { initLogging(); - HandleMultiModPacks(OSGetTitleID()); + if (gSDCafiineEnabled) { + HandleMultiModPacks(OSGetTitleID()); + } else { + DEBUG_FUNCTION_LINE("SDCafiine is disabled"); + } +} + +void autoApplySingleModpackChanged(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE("New value in gAutoApplySingleModpack: %d", newValue); + gAutoApplySingleModpack = newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, AUTO_APPLY_SINGLE_MODPACK_STRING, gAutoApplySingleModpack); +} + +void skipPrepareIfSingleModpackChanged(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE("New value in gSkipPrepareIfSingleModpack: %d", newValue); + gSkipPrepareIfSingleModpack = newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, SKIP_PREPARE_FOR_SINGLE_MODPACK_STRING, gSkipPrepareIfSingleModpack); +} + +void sdCafiineEnabledChanged(ConfigItemBoolean *item, bool newValue) { + DEBUG_FUNCTION_LINE("New value in gSDCafiineEnabled: %d", newValue); + gSDCafiineEnabled = newValue; + // If the value has changed, we store it in the storage. + WUPS_StoreInt(nullptr, SDCAFIINE_ENABLED_STRING, gSDCafiineEnabled); +} + +WUPS_GET_CONFIG() { + // We open the storage, so we can persist the configuration the user did. + if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to open storage"); + return 0; + } + + WUPSConfigHandle config; + WUPSConfig_CreateHandled(&config, "SDCafiine"); + + WUPSConfigCategoryHandle setting; + WUPSConfig_AddCategoryByNameHandled(config, "Settings", &setting); + WUPSConfigCategoryHandle advanced; + WUPSConfig_AddCategoryByNameHandled(config, "Advanced settings", &advanced); + + WUPSConfigItemBoolean_AddToCategoryHandled(config, setting, SDCAFIINE_ENABLED_STRING, "Enable SDCafiine (game needs to be restarted)", gSDCafiineEnabled, &sdCafiineEnabledChanged); + WUPSConfigItemBoolean_AddToCategoryHandled(config, advanced, AUTO_APPLY_SINGLE_MODPACK_STRING, "Auto apply the modpack if only one modpack exists", gAutoApplySingleModpack, &autoApplySingleModpackChanged); + WUPSConfigItemBoolean_AddToCategoryHandled(config, advanced, SKIP_PREPARE_FOR_SINGLE_MODPACK_STRING, "Skip \"Preparing modpack...\" screen", gSkipPrepareIfSingleModpack, &skipPrepareIfSingleModpackChanged); + + return config; +} + +WUPS_CONFIG_CLOSED() { + // Save all changes + if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); + } } ON_APPLICATION_ENDS() { @@ -34,5 +138,9 @@ ON_APPLICATION_ENDS() { ContentRedirection_RemoveFSLayer(contentLayerHandle); contentLayerHandle = 0; } + if (aocLayerHandle != 0) { + ContentRedirection_RemoveFSLayer(aocLayerHandle); + aocLayerHandle = 0; + } deinitLogging(); } \ No newline at end of file diff --git a/src/main.h b/src/main.h index 2f5bb62..86699aa 100644 --- a/src/main.h +++ b/src/main.h @@ -1,3 +1,13 @@ #pragma once +#include "version.h" -#define VERSION "v0.1" +#define VERSION_RAW "0.1" +#define VERSION "v" VERSION_RAW +#define VERSION_FULL_RAW VERSION_RAW VERSION_EXTRA + +#define AUTO_APPLY_SINGLE_MODPACK_STRING "autoApplySingleModpack" +#define SKIP_PREPARE_FOR_SINGLE_MODPACK_STRING "skipPrepareForSingleModpack" +#define SDCAFIINE_ENABLED_STRING "sdCafiineEnabled" + +extern bool gAutoApplySingleModpack; +extern bool gSkipPrepareIfSingleModpack; diff --git a/src/modpackSelector.cpp b/src/modpackSelector.cpp index d2a4ea6..a5ee3b1 100644 --- a/src/modpackSelector.cpp +++ b/src/modpackSelector.cpp @@ -1,26 +1,23 @@ #include "modpackSelector.h" #include "main.h" #include "version.h" +#include +#include +#include #include #include #include +#include #include #include -#include - -#include -#include #include - -#include -#include +#include #include #include +#include #define TEXT_SEL(x, text1, text2) ((x) ? (text1) : (text2)) -void ReplaceContent(const std::string &basePath); - void HandleMultiModPacks(uint64_t titleID) { char TitleIDString[17]; snprintf(TitleIDString, 17, "%016llX", titleID); @@ -29,7 +26,7 @@ void HandleMultiModPacks(uint64_t titleID) { std::map mounting_points; - std::string modTitleIDPath = std::string("fs:/vol/external01/sdcafiine/") + TitleIDString; + const std::string modTitleIDPath = std::string("fs:/vol/external01/sdcafiine/").append(TitleIDString); DirList modTitleDirList(modTitleIDPath, nullptr, DirList::Dirs); modTitleDirList.SortList(); @@ -42,13 +39,17 @@ void HandleMultiModPacks(uint64_t titleID) { } const std::string &packageName = curFile; - modTitlePath[packageName] = modTitleIDPath.append("/").append(curFile); - DEBUG_FUNCTION_LINE_VERBOSE("Found %s", packageName.c_str()); + modTitlePath[packageName] = (modTitleIDPath + "/").append(curFile); + DEBUG_FUNCTION_LINE_VERBOSE("Found %s %s", packageName.c_str(), modTitlePath[packageName].c_str()); } if (modTitlePath.empty()) { return; } + if (modTitlePath.size() == 1 && gSkipPrepareIfSingleModpack) { + ReplaceContent(modTitlePath.begin()->second); + return; + } int selected = 0; int initScreen = 1; @@ -61,6 +62,7 @@ void HandleMultiModPacks(uint64_t titleID) { auto *screenBuffer = (uint8_t *) MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size + screen_buf1_size, 0x100); if (screenBuffer == nullptr) { DEBUG_FUNCTION_LINE_ERR("Failed to alloc screenBuffer"); + OSFatal("SDCafiine plugin: Failed to alloc screenBuffer."); return; } OSScreenSetBufferEx(SCREEN_TV, (void *) screenBuffer); @@ -79,80 +81,142 @@ void HandleMultiModPacks(uint64_t titleID) { VPADStatus vpad_data; VPADReadError error; + bool displayAutoSkipOption = modTitlePath.size() == 1; + int wantToExit = 0; int page = 0; - int per_page = 13; + int per_page = displayAutoSkipOption ? 11 : 13; int max_pages = (modTitlePath.size() / per_page) + 1; - while (true) { + int curState = 0; + if (gAutoApplySingleModpack && modTitlePath.size() == 1) { + curState = 1; + } + int durationInFrames = 60; + int frameCounter = 0; + + while (true) { error = VPAD_READ_NO_SAMPLES; VPADRead(VPAD_CHAN_0, &vpad_data, 1, &error); - if (error == VPAD_READ_SUCCESS) { - if (vpad_data.trigger & VPAD_BUTTON_A) { - wantToExit = 1; - initScreen = 1; - } else if (vpad_data.trigger & VPAD_BUTTON_B) { - break; - } else if (vpad_data.trigger & VPAD_BUTTON_DOWN) { - selected++; - initScreen = 1; - } else if (vpad_data.trigger & VPAD_BUTTON_UP) { - selected--; - initScreen = 1; - } else if (vpad_data.trigger & VPAD_BUTTON_L) { - selected -= per_page; - initScreen = 1; - } else if (vpad_data.trigger & VPAD_BUTTON_R) { - selected += per_page; - initScreen = 1; + if (curState == 1) { + if (error == VPAD_READ_SUCCESS) { + if (vpad_data.trigger & VPAD_BUTTON_X) { + curState = 0; + continue; + } + } + if (initScreen) { + OSScreenClearBufferEx(SCREEN_TV, 0); + OSScreenClearBufferEx(SCREEN_DRC, 0); + console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA); + console_print_pos(x_offset, 1, "Preparing modpack \"%s\"...", modTitlePath.begin()->first.c_str()); + console_print_pos(x_offset, 3, "Press X to open menu"); + // Flip buffers + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); } - if (selected < 0) { selected = 0; } - if (selected >= modTitlePath.size()) { selected = modTitlePath.size() - 1; } - page = selected / per_page; - } - if (initScreen) { - OSScreenClearBufferEx(SCREEN_TV, 0); - OSScreenClearBufferEx(SCREEN_DRC, 0); - console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA); - console_print_pos(x_offset, 1, "Select your options and press A to launch."); - console_print_pos(x_offset, 2, "Press B to launch without mods"); - int y_offset = 4; - int cur_ = 0; + if (frameCounter >= durationInFrames) { + ReplaceContent(modTitlePath.begin()->second); + break; + } - for (auto &it : modTitlePath) { - std::string key = it.first; - std::string value = it.second; + frameCounter++; + } else { + if (error == VPAD_READ_SUCCESS) { + if (vpad_data.trigger & VPAD_BUTTON_A) { + wantToExit = 1; + initScreen = 1; + } else if (modTitlePath.size() == 1 && (vpad_data.trigger & VPAD_BUTTON_X)) { + OSScreenClearBufferEx(SCREEN_TV, 0); + OSScreenClearBufferEx(SCREEN_DRC, 0); - if (wantToExit && cur_ == selected) { - ReplaceContent(value.append("/content")); - //snprintf(gModFolder, FS_MAX_ENTNAME_SIZE, "%s", value.c_str()); + console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA); + console_print_pos(x_offset, 1, "Save settings..."); + + // Flip buffers + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); + + // We open the storage, so we can persist the configuration the user did. + if (WUPS_OpenStorage() == WUPS_STORAGE_ERROR_SUCCESS) { + gAutoApplySingleModpack = !gAutoApplySingleModpack; + // If the value has changed, we store it in the storage. + if (WUPS_StoreInt(nullptr, AUTO_APPLY_SINGLE_MODPACK_STRING, gAutoApplySingleModpack) != WUPS_STORAGE_ERROR_SUCCESS) { + } + if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) { + DEBUG_FUNCTION_LINE_ERR("Failed to close storage"); + } + } + initScreen = 1; + } else if (vpad_data.trigger & VPAD_BUTTON_B) { + break; + } else if (vpad_data.trigger & VPAD_BUTTON_DOWN) { + selected++; + initScreen = 1; + } else if (vpad_data.trigger & VPAD_BUTTON_UP) { + selected--; + initScreen = 1; + } else if (vpad_data.trigger & VPAD_BUTTON_L) { + selected -= per_page; + initScreen = 1; + } else if (vpad_data.trigger & VPAD_BUTTON_R) { + selected += per_page; + initScreen = 1; + } + if (selected < 0) { selected = 0; } + if (selected >= modTitlePath.size()) { selected = modTitlePath.size() - 1; } + page = selected / per_page; + } + + if (initScreen) { + OSScreenClearBufferEx(SCREEN_TV, 0); + OSScreenClearBufferEx(SCREEN_DRC, 0); + console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA); + console_print_pos(x_offset, 1, "Press A to launch a modpack"); + console_print_pos(x_offset, 2, "Press B to launch without a modpack"); + if (modTitlePath.size() == 1) { + if (gAutoApplySingleModpack) { + console_print_pos(x_offset, 4, "Press X to disable autostart for a single modpack"); + } else { + console_print_pos(x_offset, 4, "Press X to enable autostart for a single modpack"); + } + } + int y_offset = displayAutoSkipOption ? 6 : 4; + int cur_ = 0; + + for (auto &it : modTitlePath) { + std::string key = it.first; + std::string value = it.second; + + if (wantToExit && cur_ == selected) { + ReplaceContent(value); + break; + } + + if (cur_ >= (page * per_page) && cur_ < ((page + 1) * per_page)) { + console_print_pos(x_offset, y_offset++, "%s %s", TEXT_SEL((selected == cur_), "--->", " "), key.c_str()); + } + cur_++; + } + + if (wantToExit) { //just in case. break; } - if (cur_ >= (page * per_page) && cur_ < ((page + 1) * per_page)) { - console_print_pos(x_offset, y_offset++, "%s %s", TEXT_SEL((selected == cur_), "--->", " "), key.c_str()); + if (max_pages > 0) { + console_print_pos(x_offset, 17, "Page %02d/%02d. Press L/R to change page.", page + 1, max_pages); } - cur_++; + + // Flip buffers + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); + + initScreen = 0; } - - if (wantToExit) { //just in case. - break; - } - - if (max_pages > 0) { - console_print_pos(x_offset, 17, "Page %02d/%02d. Press L/R to change page.", page + 1, max_pages); - } - - // Flip buffers - OSScreenFlipBuffersEx(SCREEN_TV); - OSScreenFlipBuffersEx(SCREEN_DRC); - - initScreen = 0; } - OSSleepTicks(OSMillisecondsToTicks(100)); } OSScreenClearBufferEx(SCREEN_TV, 0); @@ -165,19 +229,53 @@ void HandleMultiModPacks(uint64_t titleID) { MEMFreeToMappedMemory(screenBuffer); } extern CRLayerHandle contentLayerHandle; +extern CRLayerHandle aocLayerHandle; -void ReplaceContent(const std::string &basePath) { - auto res = ContentRedirection_AddFSLayer(&contentLayerHandle, - "SDCafiine Content", - basePath.c_str(), +bool ReplaceContentInternal(const std::string &basePath, const std::string &subdir, CRLayerHandle *layerHandle); + +bool ReplaceContent(const std::string &basePath) { + bool contentRes = ReplaceContentInternal(basePath, "content", &contentLayerHandle); + bool aocRes = ReplaceContentInternal(basePath, "aoc", &aocLayerHandle); + + if (!contentRes && !aocRes) { + DEBUG_FUNCTION_LINE_ERR("Failed to apply modpack. Starting without mods."); + OSScreenClearBufferEx(SCREEN_TV, 0); + OSScreenClearBufferEx(SCREEN_DRC, 0); + console_print_pos(-2, -1, "SDCafiine plugin " VERSION VERSION_EXTRA); + console_print_pos(-2, 1, "Failed to apply modpack. Starting without mods."); + OSScreenFlipBuffersEx(SCREEN_TV); + OSScreenFlipBuffersEx(SCREEN_DRC); + + OSSleepTicks(OSMillisecondsToTicks(3000)); + return false; + } + return true; +} + +bool ReplaceContentInternal(const std::string &basePath, const std::string &subdir, CRLayerHandle *layerHandle) { + std::string layerName = "SDCafiine /vol/" + subdir; + std::string fullPath = basePath + "/" + subdir; + struct stat st {}; + if (stat(fullPath.c_str(), &st) < 0) { + DEBUG_FUNCTION_LINE_WARN("Skip /vol/%s to %s redirection. Dir does not exist", subdir.c_str(), fullPath.c_str()); + return false; + } + + auto res = ContentRedirection_AddFSLayer(layerHandle, + layerName.c_str(), + fullPath.c_str(), FS_LAYER_TYPE_CONTENT_MERGE); if (res == CONTENT_REDIRECTION_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE("Redirect /vol/content to %s", basePath.c_str()); + DEBUG_FUNCTION_LINE("Redirect /vol/%s to %s", subdir.c_str(), fullPath.c_str()); } else { - DEBUG_FUNCTION_LINE_ERR("Failed to redirect /vol/content to %s", basePath.c_str()); + DEBUG_FUNCTION_LINE_ERR("Failed to redirect /vol/%s to %s", subdir.c_str(), fullPath.c_str()); + + return false; } + return true; } + void console_print_pos(int x, int y, const char *format, ...) { char *tmp = nullptr; diff --git a/src/modpackSelector.h b/src/modpackSelector.h index 486155c..4087e53 100644 --- a/src/modpackSelector.h +++ b/src/modpackSelector.h @@ -1,14 +1,8 @@ #pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif +#include +#include void HandleMultiModPacks(uint64_t titleid /*,bool showMenu = true*/); void console_print_pos(int x, int y, const char *format, ...); - -#ifdef __cplusplus -} -#endif \ No newline at end of file +bool ReplaceContent(const std::string &basePath); \ No newline at end of file