mirror of
https://github.com/wiiu-env/sdcafiine_plugin.git
synced 2024-11-22 03:19:17 +01:00
Add a config menu with several options
This commit is contained in:
parent
9a69476bb1
commit
545d278705
38
README.md
38
README.md
@ -3,32 +3,45 @@
|
|||||||
# SDCafiine Plugin
|
# SDCafiine Plugin
|
||||||
|
|
||||||
## What is SDCafiine
|
## 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.
|
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.)
|
(`[ENVIRONMENT]` is a placeholder for the actual environment name.)
|
||||||
|
|
||||||
1. Copy the file `sdcafiine.wps` into `sd:/wiiu/environments/[ENVIRONMENT]/plugins`.
|
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`.
|
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`.
|
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
|
### 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:
|
**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]/content/ <-- for game files. Maps to /vol/content/
|
||||||
|
root:/sdcafiine/[TITLEID]/[MODPACK]/aoc/ <-- for DLC files. Maps to /vol/aoc/
|
||||||
```
|
```
|
||||||
Replace the following:
|
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.
|
- "[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. This folder name can be everything but "content" or "aoc".
|
- "[MODPACK]" name of the modpack.
|
||||||
|
|
||||||
Example path for the EUR version of SuperSmashBros for Wii U:
|
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/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:
|
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
|
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
|
### 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:
|
Example:
|
||||||
```
|
```
|
||||||
sd:/sdcafiine/0005000010145000/ModPack1/content/
|
sd:/sdcafiine/0005000010145000/ModPack1/content/
|
||||||
sd:/sdcafiine/0005000010145000/ModPack2/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
|
## 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.
|
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
|
## 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.
|
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
|
||||||
|
112
src/main.cpp
112
src/main.cpp
@ -1,32 +1,136 @@
|
|||||||
|
#include "main.h"
|
||||||
#include "modpackSelector.h"
|
#include "modpackSelector.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
#include <content_redirection/redirection.h>
|
#include <content_redirection/redirection.h>
|
||||||
#include <coreinit/title.h>
|
#include <coreinit/title.h>
|
||||||
#include <wups.h>
|
#include <wups.h>
|
||||||
|
#include <wups/config/WUPSConfigItemBoolean.h>
|
||||||
|
#include <wups/config/WUPSConfigItemMultipleValues.h>
|
||||||
|
|
||||||
WUPS_PLUGIN_NAME("SDCafiine");
|
WUPS_PLUGIN_NAME("SDCafiine");
|
||||||
WUPS_PLUGIN_DESCRIPTION("SDCafiine");
|
WUPS_PLUGIN_DESCRIPTION("SDCafiine");
|
||||||
WUPS_PLUGIN_VERSION("0.1");
|
WUPS_PLUGIN_VERSION(VERSION_FULL_RAW);
|
||||||
WUPS_PLUGIN_AUTHOR("Maschell");
|
WUPS_PLUGIN_AUTHOR("Maschell");
|
||||||
WUPS_PLUGIN_LICENSE("GPL");
|
WUPS_PLUGIN_LICENSE("GPL");
|
||||||
|
|
||||||
WUPS_USE_WUT_DEVOPTAB();
|
WUPS_USE_WUT_DEVOPTAB();
|
||||||
|
WUPS_USE_STORAGE("sdcafiine"); // Unqiue id for the storage api
|
||||||
|
|
||||||
CRLayerHandle contentLayerHandle __attribute__((section(".data"))) = 0;
|
CRLayerHandle contentLayerHandle __attribute__((section(".data"))) = 0;
|
||||||
|
CRLayerHandle aocLayerHandle __attribute__((section(".data"))) = 0;
|
||||||
|
|
||||||
|
bool gAutoApplySingleModpack = false;
|
||||||
|
bool gSkipPrepareIfSingleModpack = false;
|
||||||
|
bool gSDCafiineEnabled = true;
|
||||||
|
|
||||||
INITIALIZE_PLUGIN() {
|
INITIALIZE_PLUGIN() {
|
||||||
// But then use libcontentredirection instead.
|
// But then use libcontentredirection instead.
|
||||||
ContentRedirectionStatus error;
|
ContentRedirectionStatus error;
|
||||||
if ((error = ContentRedirection_InitLibrary()) != CONTENT_REDIRECTION_RESULT_SUCCESS) {
|
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.");
|
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 */
|
/* Entry point */
|
||||||
ON_APPLICATION_START() {
|
ON_APPLICATION_START() {
|
||||||
initLogging();
|
initLogging();
|
||||||
|
if (gSDCafiineEnabled) {
|
||||||
HandleMultiModPacks(OSGetTitleID());
|
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() {
|
ON_APPLICATION_ENDS() {
|
||||||
@ -34,5 +138,9 @@ ON_APPLICATION_ENDS() {
|
|||||||
ContentRedirection_RemoveFSLayer(contentLayerHandle);
|
ContentRedirection_RemoveFSLayer(contentLayerHandle);
|
||||||
contentLayerHandle = 0;
|
contentLayerHandle = 0;
|
||||||
}
|
}
|
||||||
|
if (aocLayerHandle != 0) {
|
||||||
|
ContentRedirection_RemoveFSLayer(aocLayerHandle);
|
||||||
|
aocLayerHandle = 0;
|
||||||
|
}
|
||||||
deinitLogging();
|
deinitLogging();
|
||||||
}
|
}
|
12
src/main.h
12
src/main.h
@ -1,3 +1,13 @@
|
|||||||
#pragma once
|
#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;
|
||||||
|
@ -1,26 +1,23 @@
|
|||||||
#include "modpackSelector.h"
|
#include "modpackSelector.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include <content_redirection/redirection.h>
|
||||||
|
#include <coreinit/screen.h>
|
||||||
|
#include <coreinit/thread.h>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <fs/DirList.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <content_redirection/redirection.h>
|
|
||||||
#include <coreinit/thread.h>
|
|
||||||
#include <memory/mappedmemory.h>
|
#include <memory/mappedmemory.h>
|
||||||
|
#include <string>
|
||||||
#include <coreinit/screen.h>
|
|
||||||
#include <fs/DirList.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#include <vpad/input.h>
|
#include <vpad/input.h>
|
||||||
|
#include <wups/storage.h>
|
||||||
|
|
||||||
#define TEXT_SEL(x, text1, text2) ((x) ? (text1) : (text2))
|
#define TEXT_SEL(x, text1, text2) ((x) ? (text1) : (text2))
|
||||||
|
|
||||||
void ReplaceContent(const std::string &basePath);
|
|
||||||
|
|
||||||
void HandleMultiModPacks(uint64_t titleID) {
|
void HandleMultiModPacks(uint64_t titleID) {
|
||||||
char TitleIDString[17];
|
char TitleIDString[17];
|
||||||
snprintf(TitleIDString, 17, "%016llX", titleID);
|
snprintf(TitleIDString, 17, "%016llX", titleID);
|
||||||
@ -29,7 +26,7 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
|
|
||||||
std::map<std::string, std::string> mounting_points;
|
std::map<std::string, std::string> 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);
|
DirList modTitleDirList(modTitleIDPath, nullptr, DirList::Dirs);
|
||||||
|
|
||||||
modTitleDirList.SortList();
|
modTitleDirList.SortList();
|
||||||
@ -42,13 +39,17 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string &packageName = curFile;
|
const std::string &packageName = curFile;
|
||||||
modTitlePath[packageName] = modTitleIDPath.append("/").append(curFile);
|
modTitlePath[packageName] = (modTitleIDPath + "/").append(curFile);
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Found %s", packageName.c_str());
|
DEBUG_FUNCTION_LINE_VERBOSE("Found %s %s", packageName.c_str(), modTitlePath[packageName].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modTitlePath.empty()) {
|
if (modTitlePath.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (modTitlePath.size() == 1 && gSkipPrepareIfSingleModpack) {
|
||||||
|
ReplaceContent(modTitlePath.begin()->second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int selected = 0;
|
int selected = 0;
|
||||||
int initScreen = 1;
|
int initScreen = 1;
|
||||||
@ -61,6 +62,7 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
auto *screenBuffer = (uint8_t *) MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size + screen_buf1_size, 0x100);
|
auto *screenBuffer = (uint8_t *) MEMAllocFromMappedMemoryForGX2Ex(screen_buf0_size + screen_buf1_size, 0x100);
|
||||||
if (screenBuffer == nullptr) {
|
if (screenBuffer == nullptr) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc screenBuffer");
|
DEBUG_FUNCTION_LINE_ERR("Failed to alloc screenBuffer");
|
||||||
|
OSFatal("SDCafiine plugin: Failed to alloc screenBuffer.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
OSScreenSetBufferEx(SCREEN_TV, (void *) screenBuffer);
|
OSScreenSetBufferEx(SCREEN_TV, (void *) screenBuffer);
|
||||||
@ -79,20 +81,76 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
VPADStatus vpad_data;
|
VPADStatus vpad_data;
|
||||||
VPADReadError error;
|
VPADReadError error;
|
||||||
|
|
||||||
|
bool displayAutoSkipOption = modTitlePath.size() == 1;
|
||||||
|
|
||||||
int wantToExit = 0;
|
int wantToExit = 0;
|
||||||
int page = 0;
|
int page = 0;
|
||||||
int per_page = 13;
|
int per_page = displayAutoSkipOption ? 11 : 13;
|
||||||
int max_pages = (modTitlePath.size() / per_page) + 1;
|
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;
|
error = VPAD_READ_NO_SAMPLES;
|
||||||
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &error);
|
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &error);
|
||||||
|
|
||||||
|
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 (frameCounter >= durationInFrames) {
|
||||||
|
ReplaceContent(modTitlePath.begin()->second);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameCounter++;
|
||||||
|
} else {
|
||||||
if (error == VPAD_READ_SUCCESS) {
|
if (error == VPAD_READ_SUCCESS) {
|
||||||
if (vpad_data.trigger & VPAD_BUTTON_A) {
|
if (vpad_data.trigger & VPAD_BUTTON_A) {
|
||||||
wantToExit = 1;
|
wantToExit = 1;
|
||||||
initScreen = 1;
|
initScreen = 1;
|
||||||
|
} else if (modTitlePath.size() == 1 && (vpad_data.trigger & VPAD_BUTTON_X)) {
|
||||||
|
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, "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) {
|
} else if (vpad_data.trigger & VPAD_BUTTON_B) {
|
||||||
break;
|
break;
|
||||||
} else if (vpad_data.trigger & VPAD_BUTTON_DOWN) {
|
} else if (vpad_data.trigger & VPAD_BUTTON_DOWN) {
|
||||||
@ -117,9 +175,16 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
OSScreenClearBufferEx(SCREEN_TV, 0);
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
||||||
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
||||||
console_print_pos(x_offset, -1, "SDCafiine plugin " VERSION VERSION_EXTRA);
|
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, 1, "Press A to launch a modpack");
|
||||||
console_print_pos(x_offset, 2, "Press B to launch without mods");
|
console_print_pos(x_offset, 2, "Press B to launch without a modpack");
|
||||||
int y_offset = 4;
|
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;
|
int cur_ = 0;
|
||||||
|
|
||||||
for (auto &it : modTitlePath) {
|
for (auto &it : modTitlePath) {
|
||||||
@ -127,8 +192,7 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
std::string value = it.second;
|
std::string value = it.second;
|
||||||
|
|
||||||
if (wantToExit && cur_ == selected) {
|
if (wantToExit && cur_ == selected) {
|
||||||
ReplaceContent(value.append("/content"));
|
ReplaceContent(value);
|
||||||
//snprintf(gModFolder, FS_MAX_ENTNAME_SIZE, "%s", value.c_str());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +216,7 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
|
|
||||||
initScreen = 0;
|
initScreen = 0;
|
||||||
}
|
}
|
||||||
OSSleepTicks(OSMillisecondsToTicks(100));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OSScreenClearBufferEx(SCREEN_TV, 0);
|
OSScreenClearBufferEx(SCREEN_TV, 0);
|
||||||
@ -165,19 +229,53 @@ void HandleMultiModPacks(uint64_t titleID) {
|
|||||||
MEMFreeToMappedMemory(screenBuffer);
|
MEMFreeToMappedMemory(screenBuffer);
|
||||||
}
|
}
|
||||||
extern CRLayerHandle contentLayerHandle;
|
extern CRLayerHandle contentLayerHandle;
|
||||||
|
extern CRLayerHandle aocLayerHandle;
|
||||||
|
|
||||||
void ReplaceContent(const std::string &basePath) {
|
bool ReplaceContentInternal(const std::string &basePath, const std::string &subdir, CRLayerHandle *layerHandle);
|
||||||
auto res = ContentRedirection_AddFSLayer(&contentLayerHandle,
|
|
||||||
"SDCafiine Content",
|
bool ReplaceContent(const std::string &basePath) {
|
||||||
basePath.c_str(),
|
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);
|
FS_LAYER_TYPE_CONTENT_MERGE);
|
||||||
if (res == CONTENT_REDIRECTION_RESULT_SUCCESS) {
|
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 {
|
} 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, ...) {
|
void console_print_pos(int x, int y, const char *format, ...) {
|
||||||
char *tmp = nullptr;
|
char *tmp = nullptr;
|
||||||
|
|
||||||
|
@ -1,14 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void HandleMultiModPacks(uint64_t titleid /*,bool showMenu = true*/);
|
void HandleMultiModPacks(uint64_t titleid /*,bool showMenu = true*/);
|
||||||
void console_print_pos(int x, int y, const char *format, ...);
|
void console_print_pos(int x, int y, const char *format, ...);
|
||||||
|
bool ReplaceContent(const std::string &basePath);
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user