Use a separate Wii U Menu save copy per console

This commit is contained in:
Maschell 2023-04-02 21:26:13 +02:00
parent b1e0696a01
commit 14dee3b422
9 changed files with 119 additions and 21 deletions

View File

@ -1,4 +1,4 @@
FROM ghcr.io/wiiu-env/devkitppc:20230218
FROM ghcr.io/wiiu-env/devkitppc:20230402
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20230316 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/librpxloader:20230316 /artifacts $DEVKITPRO

View File

@ -47,8 +47,9 @@ CoolGame.wuhb
```
## Save data redirection
In order to preserve the order of homebrew apps even when you run the Wii U Menu without this plugin, this plugin will redirect the Wii U Menu save data to `sd:/wiiu/homebrew_on_menu_plugin`.
When no save data is found on the sd card, the current save data is copied from the console, but after that it's never updated.
In order to preserve the order of homebrew apps even when you run the Wii U Menu without this plugin, this plugin will redirect the Wii U Menu save data to
`sd:/wiiu/homebrew_on_menu_plugin/[SerialNumberOfTheConsole]/save`. When no save data is found on the sd card, the current save data is copied from the console,
but after that it's never updated.
**If the plugin is configured to hide any homebrew except a Homebrew Launcher, the redirection is disabled.**

View File

@ -1,5 +1,7 @@
#include "SaveRedirection.h"
#include "globals.h"
#include <content_redirection/redirection.h>
#include <coreinit/mcp.h>
#include <coreinit/title.h>
#include <fs/FSUtils.h>
#include <functional>
@ -13,9 +15,25 @@
bool gInWiiUMenu __attribute__((section(".data"))) = false;
CRLayerHandle saveLayer __attribute__((section(".data"))) = 0;
static inline std::string getBaseSavePathLegacy() {
return std::string(HOMEBREW_ON_MENU_PLUGIN_DATA_PATH) + "/save";
}
static inline std::string getBaseSavePathLegacyFS() {
return std::string("fs:") + getBaseSavePathLegacy();
}
static inline std::string getBaseSavePath() {
return string_format(HOMEBREW_ON_MENU_PLUGIN_DATA_PATH "/%s/save", gSerialId.c_str());
}
static inline std::string getBaseSavePathFS() {
return "fs:" + getBaseSavePath();
}
void SaveRedirectionCleanUp() {
if (saveLayer != 0) {
DEBUG_FUNCTION_LINE("Remove save redirection: %s -> %s", "/vol/save", "fs:" SAVE_REPLACEMENT_PATH "/save/");
DEBUG_FUNCTION_LINE("Remove save redirection: %s -> %s", "/vol/save", getBaseSavePathFS().c_str());
auto res = ContentRedirection_RemoveFSLayer(saveLayer);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to remove save FSLayer");
@ -29,9 +47,11 @@ void CopyExistingFiles() {
nn::act::PersistentId persistentId = nn::act::GetPersistentId();
nn::act::Finalize();
std::string common = "fs:" SAVE_REPLACEMENT_PATH "/save/common";
std::string common = getBaseSavePathFS() + "/common";
std::string commonLegacy = getBaseSavePathLegacyFS() + "/common";
std::string commonOriginal = "fs:/vol/save/common";
std::string user = string_format("fs:" SAVE_REPLACEMENT_PATH "/save/%08X", 0x80000000 | persistentId);
std::string user = string_format("%s/%08X", getBaseSavePathFS().c_str(), 0x80000000 | persistentId);
std::string userLegacy = string_format("%s/%08X", getBaseSavePathLegacyFS().c_str(), 0x80000000 | persistentId);
std::string userOriginal = string_format("fs:/vol/save/%08X", 0x80000000 | persistentId);
FSUtils::CreateSubfolder(common.c_str());
@ -39,28 +59,64 @@ void CopyExistingFiles() {
auto BaristaAccountSaveFilePathNew = user + "/BaristaAccountSaveFile.dat";
auto BaristaAccountSaveFilePathOriginal = userOriginal + "/BaristaAccountSaveFile.dat";
auto BaristaAccountSaveFilePathLegacy = userLegacy + "/BaristaAccountSaveFile.dat";
if (!FSUtils::CheckFile(BaristaAccountSaveFilePathNew.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaAccountSaveFilePathOriginal.c_str(), BaristaAccountSaveFilePathNew.c_str());
if (!FSUtils::copyFile(BaristaAccountSaveFilePathOriginal, BaristaAccountSaveFilePathNew)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaAccountSaveFilePathOriginal.c_str(), BaristaAccountSaveFilePathNew.c_str());
if (FSUtils::CheckFile(BaristaAccountSaveFilePathLegacy.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaAccountSaveFilePathLegacy.c_str(), BaristaAccountSaveFilePathNew.c_str());
if (!FSUtils::copyFile(BaristaAccountSaveFilePathLegacy, BaristaAccountSaveFilePathNew)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaAccountSaveFilePathLegacy.c_str(), BaristaAccountSaveFilePathNew.c_str());
} else {
if (remove(BaristaAccountSaveFilePathLegacy.c_str()) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to delete %s", BaristaAccountSaveFilePathLegacy.c_str());
}
}
} else {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaAccountSaveFilePathOriginal.c_str(), BaristaAccountSaveFilePathNew.c_str());
if (!FSUtils::copyFile(BaristaAccountSaveFilePathOriginal, BaristaAccountSaveFilePathNew)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaAccountSaveFilePathOriginal.c_str(), BaristaAccountSaveFilePathNew.c_str());
}
}
}
auto BaristaCommonSaveFile = common + "/BaristaCommonSaveFile.dat";
auto BaristaCommonSaveFileOriginal = commonOriginal + "/BaristaCommonSaveFile.dat";
auto BaristaCommonSaveFileLegacy = commonLegacy + "/BaristaCommonSaveFile.dat";
if (!FSUtils::CheckFile(BaristaCommonSaveFile.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaCommonSaveFileOriginal.c_str(), BaristaCommonSaveFile.c_str());
if (!FSUtils::copyFile(BaristaCommonSaveFileOriginal, BaristaCommonSaveFile)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaCommonSaveFileOriginal.c_str(), BaristaCommonSaveFile.c_str());
if (FSUtils::CheckFile(BaristaCommonSaveFileLegacy.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaCommonSaveFileLegacy.c_str(), BaristaCommonSaveFile.c_str());
if (!FSUtils::copyFile(BaristaCommonSaveFileLegacy, BaristaCommonSaveFile)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaCommonSaveFileLegacy.c_str(), BaristaCommonSaveFile.c_str());
} else {
if (remove(BaristaCommonSaveFileLegacy.c_str()) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to delete %s", BaristaCommonSaveFileLegacy.c_str());
}
}
} else {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaCommonSaveFileOriginal.c_str(), BaristaCommonSaveFile.c_str());
if (!FSUtils::copyFile(BaristaCommonSaveFileOriginal, BaristaCommonSaveFile)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaCommonSaveFileOriginal.c_str(), BaristaCommonSaveFile.c_str());
}
}
}
auto BaristaIconDataBase = common + "/BaristaIconDataBase.dat";
auto BaristaIconDataBaseOriginal = commonOriginal + "/BaristaIconDataBase.dat";
auto BaristaIconDataBaseLegacy = commonLegacy + "/BaristaIconDataBase.dat";
if (!FSUtils::CheckFile(BaristaIconDataBase.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaIconDataBaseOriginal.c_str(), BaristaIconDataBase.c_str());
if (!FSUtils::copyFile(BaristaIconDataBaseOriginal, BaristaIconDataBase)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaIconDataBaseOriginal.c_str(), BaristaIconDataBase.c_str());
if (FSUtils::CheckFile(BaristaIconDataBaseLegacy.c_str())) {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaIconDataBaseLegacy.c_str(), BaristaIconDataBase.c_str());
if (!FSUtils::copyFile(BaristaIconDataBaseLegacy, BaristaIconDataBase)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaIconDataBaseLegacy.c_str(), BaristaIconDataBase.c_str());
} else {
if (remove(BaristaIconDataBaseLegacy.c_str()) < 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to delete %s", BaristaIconDataBaseLegacy.c_str());
}
}
} else {
DEBUG_FUNCTION_LINE("Copy %s to %s", BaristaIconDataBaseOriginal.c_str(), BaristaIconDataBase.c_str());
if (!FSUtils::copyFile(BaristaIconDataBaseOriginal, BaristaIconDataBase)) {
DEBUG_FUNCTION_LINE_ERR("Failed to copy file: %s -> %s", BaristaIconDataBaseOriginal.c_str(), BaristaIconDataBase.c_str());
}
}
}
}
@ -69,8 +125,9 @@ void initSaveData() {
SaveRedirectionCleanUp();
CopyExistingFiles();
DEBUG_FUNCTION_LINE("Setup save redirection: %s -> %s", "/vol/save", "fs:" SAVE_REPLACEMENT_PATH "/save/");
auto res = ContentRedirection_AddFSLayer(&saveLayer, "homp_save_redirection", "fs:" SAVE_REPLACEMENT_PATH "/save/", FS_LAYER_TYPE_SAVE_REPLACE);
std::string replaceDir = getBaseSavePathFS();
DEBUG_FUNCTION_LINE("Setup save redirection: %s -> %s", "/vol/save", replaceDir.c_str());
auto res = ContentRedirection_AddFSLayer(&saveLayer, "homp_save_redirection", replaceDir.c_str(), FS_LAYER_TYPE_SAVE_REPLACE);
if (res != CONTENT_REDIRECTION_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add save FS Layer: %d", res);
}
@ -92,13 +149,13 @@ DECL_FUNCTION(SAVEStatus, SAVEGetSharedSaveDataPath, uint64_t titleID, const cha
titleID == 0x0005001010040100L || // Wii U Menu USA
titleID == 0x0005001010040200L) { // Wii U Menu EUR
if (buffer != nullptr) {
std::string commonReplacement = SAVE_REPLACEMENT_PATH "/save/common";
std::string commonReplacement = getBaseSavePath() + "/common";
auto BaristaCommonSaveFile = "fs:" + commonReplacement + "/BaristaCommonSaveFile.dat";
auto BaristaIconDataBase = "fs:" + commonReplacement + "/BaristaIconDataBase.dat";
if (FSUtils::CheckFile(BaristaCommonSaveFile.c_str()) &&
FSUtils::CheckFile(BaristaIconDataBase.c_str())) {
snprintf(buffer, bufferSize, "%s/%s", commonReplacement.c_str(), path);
DEBUG_FUNCTION_LINE("Redirect Wii U Menu common path with %s", buffer);
DEBUG_FUNCTION_LINE("Redirect Wii U Menu common path to %s", commonReplacement.c_str());
return SAVE_STATUS_OK;
}
}

View File

@ -2,6 +2,6 @@
extern bool gInWiiUMenu;
#define SAVE_REPLACEMENT_PATH "/vol/external01/wiiu/homebrew_on_menu_plugin"
#define HOMEBREW_ON_MENU_PLUGIN_DATA_PATH "/vol/external01/wiiu/homebrew_on_menu_plugin"
void SaveRedirectionCleanUp();

3
src/globals.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "globals.h"
std::string gSerialId;

4
src/globals.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <string>
extern std::string gSerialId;

View File

@ -6,6 +6,7 @@
#include "fs/FSUtils.h"
#include "fs/FileReader.h"
#include "fs/FileReaderWUHB.h"
#include "globals.h"
#include "utils/StringTools.h"
#include "utils/ini.h"
#include <algorithm>
@ -94,6 +95,12 @@ INITIALIZE_PLUGIN() {
memset((void *) &gLaunchXML, 0, sizeof(gLaunchXML));
gHomebrewLaunched = FALSE;
gSerialId = {};
if (!Utils::GetSerialId(gSerialId) || gSerialId.empty()) {
DEBUG_FUNCTION_LINE_ERR("Homebrew on Menu Plugin: Failed to get the serial id");
OSFatal("Homebrew on Menu Plugin: Failed to get the serial id");
}
// Use libwuhbutils.
WUHBUtilsStatus error;
if ((error = WUHBUtils_InitLibrary()) != WUHB_UTILS_RESULT_SUCCESS) {

View File

@ -1,4 +1,7 @@
#include <stdint.h>
#include "utils.h"
#include "logger.h"
#include <coreinit/mcp.h>
#include <string>
/* hash: compute hash value of string */
unsigned int hash_string(const char *str) {
@ -11,3 +14,21 @@ unsigned int hash_string(const char *str) {
}
return h; // or, h % ARRAY_SIZE;
}
bool Utils::GetSerialId(std::string &serialID) {
bool result = false;
alignas(0x40) MCPSysProdSettings settings{};
auto handle = MCP_Open();
if (handle >= 0) {
if (MCP_GetSysProdSettings(handle, &settings) == 0) {
serialID = std::string(settings.code_id) + settings.serial_id;
result = true;
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to get SerialId");
}
MCP_Close(handle);
} else {
DEBUG_FUNCTION_LINE_ERR("MCP_Open failed");
}
return result;
}

View File

@ -5,6 +5,7 @@
#include <malloc.h>
#include <memory>
#include <mutex>
#include <string>
uint32_t hash_string(const char *);
@ -31,3 +32,7 @@ bool remove_locked_first_if(std::mutex &mutex, std::forward_list<T, Allocator> &
}
return false;
}
namespace Utils {
bool GetSerialId(std::string &serialID);
}