Reimplement/refactor everything

This commit is contained in:
Maschell 2024-03-01 16:29:37 +01:00
parent a7b9626818
commit a6619ab33d
12 changed files with 647 additions and 575 deletions

View File

@ -2,5 +2,6 @@ FROM ghcr.io/wiiu-env/devkitppc:20231112
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:0.8.0-dev-20231221-ca17105 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libnotifications:20230621 /artifacts $DEVKITPRO
WORKDIR project

View File

@ -50,7 +50,7 @@ CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif
LIBS := -lwups -lwut -lmappedmemory
LIBS := -lwups -lwut -lnotifications -lmappedmemory
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level

68
src/function_patches.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "utils/SettingsUtils.h"
#include "utils/logger.h"
#include <coreinit/debug.h>
#include <coreinit/userconfig.h>
#include <nn/acp/title.h>
#include <string_view>
#include <sysapp/switch.h>
#include <sysapp/title.h>
#include <wups/function_patching.h>
DECL_FUNCTION(int32_t, ACPGetTitleMetaXmlByDevice, uint64_t titleid, ACPMetaXml *metaxml, uint32_t device) {
int result = real_ACPGetTitleMetaXmlByDevice(titleid, metaxml, device);
if (metaxml != nullptr) {
metaxml->region = 0xFFFFFFFF;
}
return result;
}
DECL_FUNCTION(int32_t, ACPGetLaunchMetaXml, ACPMetaXml *metaxml) {
int result = real_ACPGetLaunchMetaXml(metaxml);
if (metaxml != nullptr) {
metaxml->region = 0xFFFFFFFF;
}
return result;
}
DECL_FUNCTION(int, UCReadSysConfig, int IOHandle, int count, UCSysConfig *settings) {
int result = real_UCReadSysConfig(IOHandle, count, settings);
auto upid = OSGetUPID();
if (!gRegionFreeValuesSetupDone || result != 0 || (upid != SYSAPP_PFID_WII_U_MENU && upid != SYSAPP_PFID_DOWNLOAD_GAME && upid != SYSAPP_PFID_EMANUAL)) {
return result;
}
if (std::string_view("cafe.language") == settings->name) {
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.language found!");
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing language...");
DEBUG_FUNCTION_LINE("UCReadSysConfig: original lang %d, new %d", *((int *) settings->data), gCurrentLanguage);
*((int *) settings->data) = gCurrentLanguage;
} else if (std::string_view("cafe.cntry_reg") == settings->name) {
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.cntry_reg found!");
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing cntry_reg...");
DEBUG_FUNCTION_LINE("UCReadSysConfig: original cntry_reg %d, new %d", *((int *) settings->data), gCurrentCountry);
*((int *) settings->data) = gCurrentCountry;
}
return result;
}
DECL_FUNCTION(int, MCP_GetSysProdSettings, int IOHandle, MCPSysProdSettings *settings) {
int result = real_MCP_GetSysProdSettings(IOHandle, settings);
auto upid = OSGetUPID();
if (!gRegionFreeValuesSetupDone || result != 0 || (upid != SYSAPP_PFID_WII_U_MENU && upid != SYSAPP_PFID_DOWNLOAD_GAME && upid != SYSAPP_PFID_EMANUAL)) {
return result;
}
DEBUG_FUNCTION_LINE_VERBOSE("MCP_GetSysProdSettings: forcing platform region...");
DEBUG_FUNCTION_LINE("MCP_GetSysProdSettings: original region %d, new %d...", settings->product_area, gCurrentProductArea);
settings->product_area = gCurrentProductArea;
return result;
}
WUPS_MUST_REPLACE(ACPGetTitleMetaXmlByDevice, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetTitleMetaXmlByDevice);
WUPS_MUST_REPLACE(ACPGetLaunchMetaXml, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetLaunchMetaXml);
WUPS_MUST_REPLACE_FOR_PROCESS(MCP_GetSysProdSettings, WUPS_LOADER_LIBRARY_COREINIT, MCP_GetSysProdSettings, WUPS_FP_TARGET_PROCESS_ALL);
WUPS_MUST_REPLACE_FOR_PROCESS(UCReadSysConfig, WUPS_LOADER_LIBRARY_COREINIT, UCReadSysConfig, WUPS_FP_TARGET_PROCESS_ALL);

7
src/function_patches.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <coreinit/mcp.h>
#include <coreinit/userconfig.h>
extern "C" int (*real_UCReadSysConfig)(int IOHandle, int count, UCSysConfig *settings) __attribute__((section(".data")));
extern "C" int (*real_MCP_GetSysProdSettings)(int IOHandle, MCPSysProdSettings *settings) __attribute__((section(".data")));

View File

@ -1,21 +1,22 @@
#include "globals.h"
std::optional<MCPRegion> gRealRegionOpt = {};
std::optional<int32_t> gRealCountryOpt = {};
std::optional<Languages> gRealLanguageOpt = {};
bool gRegionFreeValuesSetupDone = false;
bool gPreferSystemSettings = DEFAULT_PREFER_SYSTEM_SETTINGS;
bool gSkipOwnRegion = DEFAULT_SKIP_OWN_REGION;
bool gAutoDetection = DEFAULT_AUTO_DETECTION_VALUE;
int gForceSettingsEnabled = 0;
Lanuages gDefaultLanguage = LANG_ENGLISH;
int32_t gDefaultCountry = 78;
MCPRegion gDefaultProductArea = MCP_REGION_EUROPE;
Languages gCurrentLanguage = LANG_ENGLISH;
int32_t gCurrentCountry = 0;
MCPRegion gCurrentProductArea = MCP_REGION_USA;
Lanuages gCurrentLanguage = gDefaultLanguage;
int32_t gCurrentCountry = gDefaultCountry;
MCPRegion gCurrentProductArea = gDefaultProductArea;
Lanuages gDefaultLangForEUR = DEFAULT_LANG_FOR_EUR;
Languages gDefaultLangForEUR = DEFAULT_LANG_FOR_EUR;
int32_t gDefaultCountryForEUR = DEFAULT_COUNTRY_FOR_EUR;
Lanuages gDefaultLangForUSA = DEFAULT_LANG_FOR_USA;
Languages gDefaultLangForUSA = DEFAULT_LANG_FOR_USA;
int32_t gDefaultCountryForUSA = DEFAULT_COUNTRY_FOR_USA;
Lanuages gDefaultLangForJPN = DEFAULT_LANG_FOR_JPN;
Languages gDefaultLangForJPN = DEFAULT_LANG_FOR_JPN;
int32_t gDefaultCountryForJPN = DEFAULT_COUNTRY_FOR_JPN;

View File

@ -2,13 +2,12 @@
#include "version.h"
#include <coreinit/mcp.h>
#include <optional>
#define VERSION "v0.2.3"
#define VERSION "v0.2.4"
#define VERSION_FULL VERSION VERSION_EXTRA
extern int gForceSettingsEnabled;
enum Lanuages {
enum Languages {
LANG_JAPANESE = 0,
LANG_ENGLISH = 1,
LANG_FRANCAIS = 2,
@ -20,6 +19,12 @@ enum Lanuages {
LANG_RUSSKI = 10,
};
extern std::optional<MCPRegion> gRealRegionOpt;
extern std::optional<int32_t> gRealCountryOpt;
extern std::optional<Languages> gRealLanguageOpt;
extern bool gRegionFreeValuesSetupDone;
#define DEFAULT_AUTO_DETECTION_VALUE true
extern bool gAutoDetection;
@ -29,11 +34,7 @@ extern bool gPreferSystemSettings;
#define DEFAULT_SKIP_OWN_REGION true
extern bool gSkipOwnRegion;
extern Lanuages gDefaultLanguage;
extern int32_t gDefaultCountry;
extern MCPRegion gDefaultProductArea;
extern Lanuages gCurrentLanguage;
extern Languages gCurrentLanguage;
extern int32_t gCurrentCountry;
extern MCPRegion gCurrentProductArea;
@ -46,11 +47,11 @@ extern MCPRegion gCurrentProductArea;
#define DEFAULT_LANG_FOR_JPN LANG_JAPANESE
#define DEFAULT_COUNTRY_FOR_JPN 1
extern Lanuages gDefaultLangForEUR;
extern Languages gDefaultLangForEUR;
extern int32_t gDefaultCountryForEUR;
extern Lanuages gDefaultLangForUSA;
extern Languages gDefaultLangForUSA;
extern int32_t gDefaultCountryForUSA;
extern Lanuages gDefaultLangForJPN;
extern Languages gDefaultLangForJPN;
extern int32_t gDefaultCountryForJPN;

View File

@ -1,19 +1,12 @@
#include "globals.h"
#include "utils/ConfigUtils.h"
#include "utils/SettingsUtils.h"
#include "utils/WUPSConfigUtils.h"
#include "utils/logger.h"
#include <coreinit/debug.h>
#include <coreinit/mcp.h>
#include <coreinit/title.h>
#include <coreinit/userconfig.h>
#include <cstdio>
#include <malloc.h>
#include <map>
#include <nn/acp.h>
#include <sysapp/switch.h>
#include <sysapp/title.h>
#include <nn/acp/client.h>
#include <notifications/notifications.h>
#include <wups.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemMultipleValues.h>
WUPS_PLUGIN_NAME("Region Free Plugin");
WUPS_PLUGIN_DESCRIPTION("Allows the user to load titles from other regions");
@ -24,574 +17,156 @@ WUPS_PLUGIN_LICENSE("GPL");
WUPS_USE_WUT_DEVOPTAB();
WUPS_USE_STORAGE("region_free_plugin");
bool getRealProductArea(MCPRegion *out);
void SetupRegionSpoofing() {
ACPInitialize(); // We need to this for getting the region of the current title
DECL_FUNCTION(int32_t, ACPGetLaunchMetaXml, ACPMetaXml *metaxml) {
int result = real_ACPGetLaunchMetaXml(metaxml);
if (metaxml != nullptr) {
metaxml->region = 0xFFFFFFFF;
}
// By default, always use the real values
gCurrentLanguage = gRealLanguageOpt.value();
gCurrentProductArea = gRealRegionOpt.value();
gCurrentCountry = gRealCountryOpt.value();
return result;
}
/**
* 1. No do any region/language/country changes for the Wii U Menu
* 2. Try to get the region from the xml of the title that is currently running
* 2a. If we are able to determine the region of the title, set the current region/language/country to the default region/language/country of that region
* 2b. If we are NOT able to determine the region of the title, make sure to force open the config menu
* If reading the XML of the current title fails, make sure to force open the config menu
* 3. If the current region is equal to the real region
* - make sure to reset the country to the real country
* - set the language back to the system language if requested (see "Prefer system settings for in region title"-setting)
* 4. Try to read the region/language for the currently running title from the storage.
* - If the settings for these settings do no exists, create an entry with the current values (unless the menu is forced opened)
* - The values from the storage always have the highest priority
* - Currently the country is NOT loaded from the storage (is not stored to storage).
* 5. Open the region free config menu if the auto detection is enabled, or we failed to determine the region of the currently running title.
*/
DECL_FUNCTION(int, UCReadSysConfig, int IOHandle, int count, struct UCSysConfig *settings) {
int result = real_UCReadSysConfig(IOHandle, count, settings);
auto upid = OSGetUPID();
if (upid != SYSAPP_PFID_WII_U_MENU && upid != SYSAPP_PFID_DOWNLOAD_GAME && upid != SYSAPP_PFID_EMANUAL) {
return result;
}
if (gForceSettingsEnabled) {
if (result != 0) {
return result;
}
if (std::string_view("cafe.language") == settings->name) {
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.language found!");
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing language...");
DEBUG_FUNCTION_LINE("UCReadSysConfig: original lang %d, new %d", *((int *) settings->data), gCurrentLanguage);
*((int *) settings->data) = gCurrentLanguage;
}
if (std::string_view("cafe.cntry_reg") == settings->name) {
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: cafe.cntry_reg found!");
DEBUG_FUNCTION_LINE_VERBOSE("UCReadSysConfig: forcing cntry_reg...");
DEBUG_FUNCTION_LINE("UCReadSysConfig: original cntry_reg %d, new %d", *((int *) settings->data), gCurrentCountry);
*((int *) settings->data) = gCurrentCountry;
}
}
return result;
}
#define CAT_GENERAL_ROOT "root"
#define CAT_GENERAL_SETTINGS "general_settings"
#define CAT_TITLE_SETTINGS "title_settings"
#define VAL_LANGUAGE "language"
#define VAL_COUNTRY "cntry_reg"
#define VAL_PRODUCT_AREA "product_area"
#define VAL_SKIP_OWN_REGION "skip_own_region"
#define VAL_PREFER_SYSTEM_SETTINGS "prefer_system_settings"
#define VAL_AUTO_DETECTION "auto_detection"
#define VAL_DEFAULT_LANG_EUR "default_lang_eur"
#define VAL_DEFAULT_LANG_USA "default_lang_usa"
#define VAL_DEFAULT_LANG_JPN "default_lang_jpn"
#define VAL_DEFAULT_COUNTRY_EUR "default_cntry_reg_eur"
#define VAL_DEFAULT_COUNTRY_USA "default_cntry_reg_usa"
#define VAL_DEFAULT_COUNTRY_JPN "default_cntry_reg_jpn"
DECL_FUNCTION(int32_t, ACPGetTitleMetaXmlByDevice, uint32_t titleid_upper, uint32_t titleid_lower, ACPMetaXml *metaxml, uint32_t device) {
int result = real_ACPGetTitleMetaXmlByDevice(titleid_upper, titleid_lower, metaxml, device);
if (metaxml != nullptr) {
metaxml->region = 0xFFFFFFFF;
}
return result;
}
void bootStuff() {
MCPRegion real_product_area;
auto real_product_area_valid = getRealProductArea(&real_product_area);
if (real_product_area_valid) {
if (real_product_area == MCP_REGION_EUROPE) {
gDefaultProductArea = MCP_REGION_EUROPE;
gDefaultLanguage = gDefaultLangForEUR;
gDefaultCountry = gDefaultCountryForEUR;
} else if (real_product_area == MCP_REGION_JAPAN) {
gDefaultProductArea = MCP_REGION_JAPAN;
gDefaultLanguage = gDefaultLangForJPN;
gDefaultCountry = gDefaultCountryForJPN;
}
if (real_product_area == MCP_REGION_USA) {
gDefaultProductArea = MCP_REGION_USA;
gDefaultLanguage = gDefaultLangForUSA;
gDefaultCountry = gDefaultCountryForUSA;
}
}
bool forceConfigMenu = false;
auto *acpMetaXml = (ACPMetaXml *) memalign(0x40, sizeof(ACPMetaXml));
uint32_t regionFromXML = 0;
if (acpMetaXml) {
memset(acpMetaXml, 0, sizeof(ACPMetaXml));
ACPInitialize();
auto res = ACPGetTitleMetaXml(OSGetTitleID(), acpMetaXml);
if (res >= 0) {
regionFromXML = acpMetaXml->region;
if (real_product_area_valid && (regionFromXML & real_product_area) == real_product_area) {
gCurrentProductArea = real_product_area;
} else {
auto curTitleId = OSGetTitleID();
if (curTitleId == 0x0005001010040000L || regionFromXML == 1) {
DEBUG_FUNCTION_LINE("Set default to JAPAN");
gDefaultProductArea = MCP_REGION_JAPAN;
gDefaultLanguage = gDefaultLangForJPN;
gDefaultCountry = gDefaultCountryForJPN;
} else if (curTitleId == 0x0005001010040100L || regionFromXML == 2) {
DEBUG_FUNCTION_LINE("Set default to USA");
gDefaultProductArea = MCP_REGION_USA;
gDefaultLanguage = gDefaultLangForUSA;
gDefaultCountry = gDefaultCountryForUSA;
} else if (curTitleId == 0x0005001010040200L || curTitleId == 0x0005001001004E200L || regionFromXML == 4) {
DEBUG_FUNCTION_LINE("Set default to EUR");
gDefaultProductArea = MCP_REGION_EUROPE;
gDefaultLanguage = gDefaultLangForEUR;
gDefaultCountry = gDefaultCountryForEUR;
} else {
DEBUG_FUNCTION_LINE_ERR("Unknown area %08X, force menu", acpMetaXml->region);
forceConfigMenu = true;
}
}
} else {
DEBUG_FUNCTION_LINE_ERR("ACPGetTitleMetaXml failed");
forceConfigMenu = true;
}
ACPFinalize();
free(acpMetaXml);
} else {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate acpMetaXml");
forceConfigMenu = true;
}
// Get region and lang from console and set these as default.
gCurrentLanguage = gDefaultLanguage;
gCurrentCountry = gDefaultCountry;
gCurrentProductArea = gDefaultProductArea;
if (gPreferSystemSettings && real_product_area_valid) {
if ((regionFromXML & real_product_area) == real_product_area) {
gCurrentProductArea = real_product_area;
auto ucHandle = UCOpen();
if (ucHandle >= 0) {
UCSysConfig sysConfig;
memset((void *) &sysConfig, 0, sizeof(sysConfig));
uint32_t data = 0xFFFFFFFF;
sysConfig.dataType = UC_DATATYPE_UNSIGNED_INT;
sysConfig.dataSize = 4;
sysConfig.data = &data;
strncpy(sysConfig.name, "cafe.language", 64);
int ucRes = real_UCReadSysConfig(ucHandle, 1, &sysConfig);
if (ucRes >= 0) {
DEBUG_FUNCTION_LINE("Force default language to system language for own region");
gCurrentLanguage = static_cast<Lanuages>(*(uint32_t *) sysConfig.data);
gDefaultLanguage = static_cast<Lanuages>(*(uint32_t *) sysConfig.data);
} else {
DEBUG_FUNCTION_LINE_ERR("UCReadSysConfig failed");
}
memset((void *) &sysConfig, 0, sizeof(sysConfig));
data = 0xFFFFFFFF;
sysConfig.dataType = UC_DATATYPE_UNSIGNED_INT;
sysConfig.dataSize = 4;
sysConfig.data = &data;
strncpy(sysConfig.name, "cafe.cntry_reg", 64);
ucRes = real_UCReadSysConfig(ucHandle, 1, &sysConfig);
if (ucRes >= 0) {
DEBUG_FUNCTION_LINE("Force default country to system country for own region");
gCurrentCountry = (int32_t) * (uint32_t *) sysConfig.data;
gDefaultCountry = (int32_t) * (uint32_t *) sysConfig.data;
} else {
DEBUG_FUNCTION_LINE_ERR("UCReadSysConfig failed");
}
UCClose(ucHandle);
} else {
DEBUG_FUNCTION_LINE_ERR("UCOpen failed");
}
}
}
WUPSStorageError storageError;
auto rootStorage = WUPSStorageAPI::GetSubItem(CAT_GENERAL_ROOT, storageError);
if (!rootStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to read %s subitem", CAT_GENERAL_ROOT);
return;
}
auto titleSettings = rootStorage->GetOrCreateSubItem(CAT_TITLE_SETTINGS, storageError);
if (!titleSettings) {
DEBUG_FUNCTION_LINE_ERR("WUPS_CreateSubItem %s failed", CAT_TITLE_SETTINGS);
return;
}
char buffer[18];
snprintf(buffer, 17, "%016llX", OSGetTitleID());
auto curTitle = titleSettings->GetOrCreateSubItem(buffer, storageError);
if (!curTitle) {
DEBUG_FUNCTION_LINE_ERR("WUPS_CreateSubItem %s failed", buffer);
return;
}
storageError = curTitle->GetOrStoreDefault(VAL_LANGUAGE, gCurrentLanguage, gCurrentLanguage);
if (storageError != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or store current Language");
}
storageError = curTitle->GetOrStoreDefault(VAL_PRODUCT_AREA, gCurrentProductArea, gCurrentProductArea);
if (storageError != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or store current Language");
}
bool isWiiUMenu = false;
bool forceOpenConfigMenu = false;
bool isWiiUMenu = false;
if (OSGetTitleID() == 0x0005001010040000L || // Wii U Menu JPN
OSGetTitleID() == 0x0005001010040100L || // Wii U Menu USA
OSGetTitleID() == 0x0005001010040200L) { // Wii U Menu EUR
isWiiUMenu = true;
}
bool showMenu = !gAutoDetection;
if (!isWiiUMenu) {
// Set current values based on title xml
auto regionForCurrentTitleOpt = getRegionForTitle(OSGetTitleID());
if (regionForCurrentTitleOpt) {
auto region_from_xml = regionForCurrentTitleOpt.value();
if ((region_from_xml & gCurrentProductArea) == gCurrentProductArea) {
// get current product area
} else if (region_from_xml == 1) {
gCurrentProductArea = MCP_REGION_JAPAN;
gCurrentLanguage = gDefaultLangForJPN;
gCurrentCountry = gDefaultCountryForJPN;
} else if (region_from_xml == 2) {
gCurrentProductArea = MCP_REGION_USA;
gCurrentLanguage = gDefaultLangForUSA;
gCurrentCountry = gDefaultCountryForUSA;
} else if (region_from_xml == 4) {
gCurrentProductArea = MCP_REGION_EUROPE;
gCurrentLanguage = gDefaultLangForEUR;
gCurrentCountry = gDefaultCountryForEUR;
} else {
// Unknown region in xml, force open the config menu
forceOpenConfigMenu = true;
}
} else {
// Failed to get region for current tile, force open the config menu
forceOpenConfigMenu = true;
}
if (real_product_area_valid && (regionFromXML & real_product_area) == real_product_area) {
if (gSkipOwnRegion && !forceConfigMenu) {
// If the launched title is for real region, force the country (and maybe language) to be the real one
if (gRealRegionOpt.value() == gCurrentProductArea) {
// Check if we want to keep the system language for in-region titles.
if (gPreferSystemSettings) {
gCurrentLanguage = gRealLanguageOpt.value();
}
// For now always use the real country
gCurrentCountry = gRealCountryOpt.value();
}
// Read per-title settings. Give current lang/region as default values.
// If we don't have values yet for this title, we only create them if we could get the region for the current title
bool allowDefaultValues = !forceOpenConfigMenu;
auto titleRegionInfoOpt = getTitleRegionInfo(OSGetTitleID(), gCurrentLanguage, gCurrentProductArea, allowDefaultValues);
if (titleRegionInfoOpt) {
gCurrentProductArea = titleRegionInfoOpt->product_area;
gCurrentLanguage = titleRegionInfoOpt->language;
}
}
// show menu if we don't use auto-detection
bool showMenu = !gAutoDetection;
if (gCurrentProductArea == gRealRegionOpt.value()) {
if (gSkipOwnRegion && !forceOpenConfigMenu) {
// If the want to skip checks for own region, and we were able
// to tell the region of the current title don't show the menu.
showMenu = false;
return;
}
}
// this overrides the current settings
if (forceConfigMenu || (!isWiiUMenu && showMenu)) {
if (forceOpenConfigMenu || (!isWiiUMenu && showMenu)) {
ConfigUtils::openConfigMenu();
// Save settings to storage
curTitle->Store(VAL_LANGUAGE, gCurrentLanguage);
curTitle->Store(VAL_PRODUCT_AREA, gCurrentProductArea);
// Save (updated) title settings to the storage
if (!saveTitleRegionInfo({OSGetTitleID(), gCurrentLanguage, gCurrentProductArea})) {
DEBUG_FUNCTION_LINE_ERR("Failed to save current title region info to storage");
}
}
DEBUG_FUNCTION_LINE("Language will be force to %d", gCurrentLanguage);
DEBUG_FUNCTION_LINE("Country will be force to %d", gDefaultCountry);
DEBUG_FUNCTION_LINE("Language will be forced to %d", gCurrentLanguage);
DEBUG_FUNCTION_LINE("Country will be forced to %d", gCurrentCountry);
DEBUG_FUNCTION_LINE("Product Area will be forced to %d", gCurrentProductArea);
}
std::optional<WUPSStorageSubItem> rootSettingsStorage = {};
std::optional<WUPSStorageSubItem> generalSettingsStorage = {};
static bool disableRegionFreePlugin = false;
ON_APPLICATION_START() {
initLogging();
WUPSStorageError storageError;
rootSettingsStorage = WUPSStorageAPI::GetOrCreateSubItem(CAT_GENERAL_ROOT, storageError);
if (!rootSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create %s subitem", CAT_GENERAL_ROOT);
}
generalSettingsStorage = rootSettingsStorage->GetOrCreateSubItem(CAT_GENERAL_SETTINGS, storageError);
if (!generalSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create %s subitem", CAT_GENERAL_SETTINGS);
if (disableRegionFreePlugin) {
gRegionFreeValuesSetupDone = false;
} else {
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_AUTO_DETECTION, gAutoDetection, DEFAULT_AUTO_DETECTION_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings, DEFAULT_PREFER_SYSTEM_SETTINGS)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_SKIP_OWN_REGION, gSkipOwnRegion, DEFAULT_SKIP_OWN_REGION)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_EUR, gDefaultLangForEUR, DEFAULT_LANG_FOR_EUR)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_EUR, gDefaultCountryForEUR, DEFAULT_COUNTRY_FOR_EUR)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_USA, gDefaultLangForUSA, DEFAULT_LANG_FOR_USA)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_USA, gDefaultCountryForUSA, DEFAULT_COUNTRY_FOR_USA)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_JPN, gDefaultLangForJPN, DEFAULT_LANG_FOR_JPN)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_JPN, gDefaultCountryForJPN, DEFAULT_COUNTRY_FOR_JPN)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
SetupRegionSpoofing();
gRegionFreeValuesSetupDone = true;
}
gForceSettingsEnabled = 1;
if (OSGetTitleID() == 0x0005001010040000L || // Wii U Menu JPN
OSGetTitleID() == 0x0005001010040100L || // Wii U Menu USA
OSGetTitleID() == 0x0005001010040200L) { // Wii U Menu EUR
gForceSettingsEnabled = 0;
}
bootStuff();
WUPSStorageAPI::SaveStorage();
}
void bool_item_changed(ConfigItemBoolean *item, bool newValue) {
DEBUG_FUNCTION_LINE("Value changed for %s: %d", item->identifier ? item->identifier : "[UNKNOWN]", newValue);
if (std::string_view(VAL_DEFAULT_LANG_USA) == item->identifier) {
gAutoDetection = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_AUTO_DETECTION, gAutoDetection);
}
} else if (std::string_view(VAL_PREFER_SYSTEM_SETTINGS) == item->identifier) {
gPreferSystemSettings = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings);
}
} else if (std::string_view(VAL_SKIP_OWN_REGION) == item->identifier) {
gPreferSystemSettings = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_SKIP_OWN_REGION, gSkipOwnRegion);
}
}
}
void default_lang_changed(ConfigItemMultipleValues *item, uint32_t newValue) {
DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->configId, newValue);
if (std::string_view(VAL_DEFAULT_LANG_EUR) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default eur lang");
gDefaultLangForEUR = (Lanuages) newValue;
} else if (std::string_view(VAL_DEFAULT_LANG_USA) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default usa lang");
gDefaultLangForUSA = (Lanuages) newValue;
} else if (std::string_view(VAL_DEFAULT_LANG_JPN) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default jpn lang");
gDefaultLangForJPN = (Lanuages) newValue;
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected identifier for default_lang_changed");
return;
}
if (generalSettingsStorage) {
generalSettingsStorage->Store(item->identifier, (int32_t) newValue);
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to store into general settings: sub-item was nullopt");
}
}
static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) {
try {
WUPSConfigCategory root = WUPSConfigCategory(rootHandle);
root.add(WUPSConfigItemBoolean::Create(VAL_DEFAULT_LANG_USA, "Auto detect region/language",
DEFAULT_AUTO_DETECTION_VALUE, gAutoDetection,
&bool_item_changed));
root.add(WUPSConfigItemBoolean::Create(VAL_SKIP_OWN_REGION, "Force auto detection for in-region titles",
DEFAULT_SKIP_OWN_REGION, gSkipOwnRegion,
&bool_item_changed));
root.add(WUPSConfigItemBoolean::Create(VAL_PREFER_SYSTEM_SETTINGS, "Prefer system settings for in-region titles",
DEFAULT_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings,
&bool_item_changed));
constexpr WUPSConfigItemMultipleValues::ValuePair eur_lang_map[] = {
{LANG_ENGLISH, "English"},
{LANG_FRANCAIS, "Francais"},
{LANG_DEUTSCH, "Deutsch"},
{LANG_ITALIANO, "Italiano"},
{LANG_ESPANOL, "Espanol"},
{LANG_NEDERLANDS, "Nederlands"},
{LANG_PORTUGUES, "Portugues"},
{LANG_RUSSKI, "Russki"},
};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_LANG_EUR, "Default language for EUR",
DEFAULT_LANG_FOR_EUR, gDefaultLangForEUR,
eur_lang_map,
default_lang_changed));
constexpr WUPSConfigItemMultipleValues::ValuePair usa_lang_map[] = {
{LANG_ENGLISH, "English"},
{LANG_FRANCAIS, "Francais"},
{LANG_ESPANOL, "Espanol"},
{LANG_PORTUGUES, "Portugues"}};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_LANG_USA, "Default language for USA",
DEFAULT_LANG_FOR_USA, gDefaultLangForUSA,
usa_lang_map,
default_lang_changed));
} catch (std::exception &e) {
OSReport("Exception T_T : %s\n", e.what());
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
static void ConfigMenuClosedCallback() {
// Save all changes
WUPSStorageAPI::SaveStorage();
}
INITIALIZE_PLUGIN() {
initLogging();
WUPSConfigAPIOptionsV1 configOptions = {.name = "Region Free Plugin"};
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to init config api");
if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("NotificationModule_InitLibrary failed");
} else {
NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_ERROR, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 10.0f);
}
gRegionFreeValuesSetupDone = false;
gRealRegionOpt = getRealRegion();
gRealCountryOpt = getRealCountry();
gRealLanguageOpt = getRealLanguage();
if (!gRealRegionOpt.has_value() || !gRealCountryOpt.has_value() || !gRealLanguageOpt.has_value()) {
DEBUG_FUNCTION_LINE_ERR("Failed to get the real region, country or language. Disabling the region free plugin");
disableRegionFreePlugin = true;
NotificationModuleStatus err;
if ((err = NotificationModule_AddErrorNotification("Failed to init Region Free Plugin. The plugin will be disabled until next boot")) != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add error notification: %s", NotificationModule_GetStatusStr(err));
}
}
InitConfigAndStorage();
deinitLogging();
}
DECL_FUNCTION(int, MCP_GetSysProdSettings, int IOHandle, struct MCPSysProdSettings *settings) {
int result = real_MCP_GetSysProdSettings(IOHandle, settings);
auto upid = OSGetUPID();
if (upid != SYSAPP_PFID_WII_U_MENU && upid != SYSAPP_PFID_DOWNLOAD_GAME && upid != SYSAPP_PFID_EMANUAL) {
return result;
}
if (gForceSettingsEnabled) {
if (result != 0) {
return result;
}
DEBUG_FUNCTION_LINE_VERBOSE("MCP_GetSysProdSettings: forcing platform region...");
DEBUG_FUNCTION_LINE("MCP_GetSysProdSettings: original region %d, new %d...", settings->product_area, gCurrentProductArea);
settings->product_area = gCurrentProductArea;
}
return result;
}
static const uint64_t
sSysAppTitleId[][3] =
{
{
// Updater
0x0005001010040000ull,
0x0005001010040100ull,
0x0005001010040200ull,
},
{
// System Settings
0x0005001010047000ull,
0x0005001010047100ull,
0x0005001010047200ull,
},
{
// Parental Controls
0x0005001010048000ull,
0x0005001010048100ull,
0x0005001010048200ull,
},
{
// User Settings
0x0005001010049000ull,
0x0005001010049100ull,
0x0005001010049200ull,
},
{
// Mii Maker
0x000500101004A000ull,
0x000500101004A100ull,
0x000500101004A200ull,
},
{
// Account Settings
0x000500101004B000ull,
0x000500101004B100ull,
0x000500101004B200ull,
},
{
// Daily log
0x000500101004C000ull,
0x000500101004C100ull,
0x000500101004C200ull,
},
{
// Notifications
0x000500101004D000ull,
0x000500101004D100ull,
0x000500101004D200ull,
},
{
// Health and Safety Information
0x000500101004E000ull,
0x000500101004E100ull,
0x000500101004E200ull,
},
{
// Electronic Manual
0x0005001B10059000ull,
0x0005001B10059100ull,
0x0005001B10059200ull,
},
{
// Wii U Chat
0x000500101005A000ull,
0x000500101005A100ull,
0x000500101005A200ull,
},
{
// "Software/Data Transfer"
0x0005001010062000ull,
0x0005001010062100ull,
0x0005001010062200ull,
},
};
bool getRealProductArea(MCPRegion *out) {
if (out == nullptr) {
return false;
}
auto handle = MCP_Open();
if (handle >= 0) {
auto data = (struct MCPSysProdSettings *) memalign(0x40, sizeof(struct MCPSysProdSettings));
auto res = real_MCP_GetSysProdSettings(handle, data);
if (res >= 0) {
*out = data->product_area;
}
MCP_Close(handle);
free(data);
return res >= 0;
}
return false;
}
DECL_FUNCTION(uint64_t, _SYSGetSystemApplicationTitleIdByProdArea, SYSTEM_APP_ID param_1, MCPRegion param_2) {
auto upid = OSGetUPID();
if (upid != SYSAPP_PFID_WII_U_MENU && upid != SYSAPP_PFID_DOWNLOAD_GAME && upid != SYSAPP_PFID_EMANUAL) {
return real__SYSGetSystemApplicationTitleIdByProdArea(param_1, param_2);
}
MCPRegion cur_product_area;
if (!getRealProductArea(&cur_product_area)) {
DEBUG_FUNCTION_LINE("Failed to get real region");
cur_product_area = param_2;
}
auto regionIdx = 1u;
if (cur_product_area == MCP_REGION_JAPAN) {
regionIdx = 0u;
} else if (cur_product_area == MCP_REGION_EUROPE || cur_product_area == 0x08) {
regionIdx = 2u;
}
uint64_t res = sSysAppTitleId[param_1][regionIdx];
return res;
DEINITIALIZE_PLUGIN() {
NotificationModule_DeInitLibrary();
}
ON_APPLICATION_ENDS() {
gCurrentLanguage = gDefaultLanguage;
gCurrentCountry = gDefaultCountry;
gCurrentProductArea = gDefaultProductArea;
gRegionFreeValuesSetupDone = false;
deinitLogging();
}
WUPS_MUST_REPLACE(ACPGetTitleMetaXmlByDevice, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetTitleMetaXmlByDevice);
WUPS_MUST_REPLACE(ACPGetLaunchMetaXml, WUPS_LOADER_LIBRARY_NN_ACP, ACPGetLaunchMetaXml);
WUPS_MUST_REPLACE_FOR_PROCESS(MCP_GetSysProdSettings, WUPS_LOADER_LIBRARY_COREINIT, MCP_GetSysProdSettings, WUPS_FP_TARGET_PROCESS_ALL);
WUPS_MUST_REPLACE_FOR_PROCESS(UCReadSysConfig, WUPS_LOADER_LIBRARY_COREINIT, UCReadSysConfig, WUPS_FP_TARGET_PROCESS_ALL);
WUPS_MUST_REPLACE_FOR_PROCESS(_SYSGetSystemApplicationTitleIdByProdArea, WUPS_LOADER_LIBRARY_SYSAPP, _SYSGetSystemApplicationTitleIdByProdArea, WUPS_FP_TARGET_PROCESS_ALL);

View File

@ -142,7 +142,6 @@ void ConfigUtils::displayMenu() {
{MCP_REGION_EUROPE, 2},
};
std::map<int32_t, MCPRegion> region_index_to_map{
{0, MCP_REGION_JAPAN},
{1, MCP_REGION_USA},
@ -150,9 +149,9 @@ void ConfigUtils::displayMenu() {
};
auto curSelectedRegion = gCurrentProductArea;
DEBUG_FUNCTION_LINE("Current %d", curSelectedRegion);
DEBUG_FUNCTION_LINE_VERBOSE("Current %d", curSelectedRegion);
std::map<Lanuages, const char *> lang_map{
std::map<Languages, const char *> lang_map{
{LANG_JAPANESE, "Japanese"},
{LANG_ENGLISH, "English"},
{LANG_FRANCAIS, "Francais"},
@ -163,7 +162,7 @@ void ConfigUtils::displayMenu() {
{LANG_PORTUGUES, "Portugues"},
{LANG_RUSSKI, "Russki"},
};
std::map<Lanuages, int32_t> lang_map_to_index{
std::map<Languages, int32_t> lang_map_to_index{
{LANG_JAPANESE, 0},
{LANG_ENGLISH, 1},
{LANG_FRANCAIS, 2},
@ -174,7 +173,7 @@ void ConfigUtils::displayMenu() {
{LANG_PORTUGUES, 7},
{LANG_RUSSKI, 8},
};
std::map<int32_t, Lanuages> lang_index_to_map{
std::map<int32_t, Languages> lang_index_to_map{
{0, LANG_JAPANESE},
{1, LANG_ENGLISH},
{2, LANG_FRANCAIS},
@ -247,8 +246,8 @@ void ConfigUtils::displayMenu() {
if (curRegionIndex < 0) {
curRegionIndex = 0;
}
if (curRegionIndex >= region_map.size()) {
curRegionIndex = region_map.size() - 1;
if (curRegionIndex >= (int32_t) region_map.size()) {
curRegionIndex = (int32_t) region_map.size() - 1;
}
gCurrentProductArea = region_index_to_map[curRegionIndex];
curSelectedRegion = gCurrentProductArea;
@ -263,8 +262,8 @@ void ConfigUtils::displayMenu() {
if (curLangIndex < 0) {
curLangIndex = 0;
}
if (curLangIndex >= lang_map.size()) {
curLangIndex = lang_map.size() - 1;
if (curLangIndex >= (int32_t) lang_map.size()) {
curLangIndex = (int32_t) lang_map.size() - 1;
}
gCurrentLanguage = lang_index_to_map[curLangIndex];
curSelectedLanguage = gCurrentLanguage;
@ -293,7 +292,7 @@ void ConfigUtils::displayMenu() {
std::string regionText = region_map[curSelectedRegion];
if (selectedBtn == 0) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
regionText = string_format("%s%s%s", curRegionIndex > 0 ? "< " : " ", regionText.c_str(), curRegionIndex < region_map.size() - 1 ? " >" : "").c_str();
regionText = string_format("%s%s%s", curRegionIndex > 0 ? "< " : " ", regionText.c_str(), curRegionIndex < (int32_t) region_map.size() - 1 ? " >" : "");
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}
@ -305,7 +304,7 @@ void ConfigUtils::displayMenu() {
std::string languageText = lang_map[curSelectedLanguage];
if (selectedBtn == 1) {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
languageText = string_format("%s%s%s", curLangIndex > 0 ? "< " : " ", languageText.c_str(), curLangIndex < lang_map.size() - 1 ? " >" : "").c_str();
languageText = string_format("%s%s%s", curLangIndex > 0 ? "< " : " ", languageText.c_str(), curLangIndex < (int32_t) lang_map.size() - 1 ? " >" : "");
} else {
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, COLOR_BORDER);
}

View File

@ -0,0 +1,67 @@
#include "SettingsUtils.h"
#include "function_patches.h"
#include "globals.h"
#include "logger.h"
#include <coreinit/userconfig.h>
#include <cstring>
#include <malloc.h>
#include <nn/acp/title.h>
#include <optional>
#include <string_view>
template<typename T>
static std::optional<T> readU32FromConfig(std::string_view config_name) {
static_assert(sizeof(T) == sizeof(uint32_t), "T must be an enum of size sizeof(uint32_t)");
auto ucHandle = UCOpen();
std::optional<T> result = {};
if (ucHandle >= 0) {
UCSysConfig sysConfig = {};
uint32_t data = 0xFFFFFFFF;
sysConfig.dataType = UC_DATATYPE_UNSIGNED_INT;
sysConfig.dataSize = 4;
sysConfig.data = &data;
strncpy(sysConfig.name, config_name.data(), 64);
if (real_UCReadSysConfig(ucHandle, 1, &sysConfig) >= 0) {
result = static_cast<T>(*(uint32_t *) sysConfig.data);
}
UCClose(ucHandle);
}
return result;
}
std::optional<int32_t> getRealCountry() {
return readU32FromConfig<uint32_t>("cafe.cntry_reg");
}
std::optional<Languages> getRealLanguage() {
return readU32FromConfig<Languages>("cafe.language");
}
std::optional<MCPRegion> getRealRegion() {
std::optional<MCPRegion> result = {};
auto handle = MCP_Open();
if (handle >= 0) {
__attribute__((aligned(0x40))) MCPSysProdSettings data;
auto res = real_MCP_GetSysProdSettings(handle, &data);
if (res >= 0) {
result = (MCPRegion) data.product_area;
}
MCP_Close(handle);
}
return result;
}
std::optional<uint32_t> getRegionForTitle(uint64_t titleId) {
auto *acpMetaXml = (ACPMetaXml *) memalign(0x40, sizeof(ACPMetaXml));
if (!acpMetaXml) {
DEBUG_FUNCTION_LINE_ERR("Failed to alloc memory for ACPMetaXml");
return {};
}
std::optional<uint32_t> result = {};
auto res = ACPGetTitleMetaXml(titleId, acpMetaXml);
if (res >= 0) {
result = (uint32_t) acpMetaXml->region;
}
free(acpMetaXml);
return result;
}

13
src/utils/SettingsUtils.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "globals.h"
#include <cstdint>
#include <optional>
std::optional<int32_t> getRealCountry();
std::optional<Languages> getRealLanguage();
std::optional<MCPRegion> getRealRegion();
std::optional<uint32_t> getRegionForTitle(uint64_t titleId);

View File

@ -0,0 +1,322 @@
#include "WUPSConfigUtils.h"
#include "globals.h"
#include "logger.h"
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemMultipleValues.h>
#include <wups/storage.h>
#define CAT_GENERAL_ROOT "root"
#define CAT_GENERAL_SETTINGS "general_settings"
#define CAT_TITLE_SETTINGS "title_settings"
#define VAL_LANGUAGE "language"
#define VAL_COUNTRY "cntry_reg"
#define VAL_PRODUCT_AREA "product_area"
#define VAL_SKIP_OWN_REGION "skip_own_region"
#define VAL_PREFER_SYSTEM_SETTINGS "prefer_system_settings"
#define VAL_AUTO_DETECTION "auto_detection"
#define VAL_DEFAULT_LANG_EUR "default_lang_eur"
#define VAL_DEFAULT_LANG_USA "default_lang_usa"
#define VAL_DEFAULT_LANG_JPN "default_lang_jpn"
#define VAL_DEFAULT_COUNTRY_EUR "default_cntry_reg_eur"
#define VAL_DEFAULT_COUNTRY_USA "default_cntry_reg_usa"
#define VAL_DEFAULT_COUNTRY_JPN "default_cntry_reg_jpn"
const char *countryCodeMap[] = {
NULL, "JP", NULL, NULL, NULL, NULL, NULL, NULL, "AI", "AG", "AR",
"AW", "BS", "BB", "BZ", "BO", "BR", "VG", "CA", "KY", "CL",
"CO", "CR", "DM", "DO", "EC", "SV", "GF", "GD", "GP", "GT",
"GY", "HT", "HN", "JM", "MQ", "MX", "MS", "AN", "NI", "PA",
"PY", "PE", "KN", "LC", "VC", "SR", "TT", "TC", "US", "UY",
"VI", "VE", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "AL", "AU", "AT", "BE", "BA", "BW", "BG",
"HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU",
"IS", "IE", "IT", "LV", "LS", "LI", "LT", "LU", "MK", "MT",
"ME", "MZ", "NA", "NL", "NZ", "NO", "PL", "PT", "RO", "RU",
"RS", "SK", "SI", "ZA", "ES", "SZ", "SE", "CH", "TR", "GB",
"ZM", "ZW", "AZ", "MR", "ML", "NE", "TD", "SD", "ER", "DJ",
"SO", "AD", "GI", "GG", "IM", "JE", "MC", "TW", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, "KR", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "HK", "MO", NULL, NULL, NULL, NULL, NULL,
NULL, "ID", "SG", "TH", "PH", "MY", NULL, NULL, NULL, "CN",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "AE", NULL, "EG",
"OM", "QA", "KW", "SA", "SY", "BH", "JO", NULL, NULL, NULL,
NULL, NULL, NULL, "SM", "VA", "BM", "IN", NULL, NULL, NULL,
NULL, "NG", "AO", "GH", NULL, NULL, NULL, NULL, NULL, NULL};
static std::optional<WUPSStorageSubItem> rootSettingsStorage = {};
static std::optional<WUPSStorageSubItem> titleSettingsStorage = {};
static std::optional<WUPSStorageSubItem> generalSettingsStorage = {};
std::optional<TitleRegionInfo> getTitleRegionInfo(uint64_t titleId, Languages default_language, MCPRegion default_product_area, bool allow_default) {
if (!titleSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to read title settings from storage");
return {};
}
char buffer[18];
snprintf(buffer, 17, "%016llX", titleId);
WUPSStorageError storageError;
auto curTitle = titleSettingsStorage->GetSubItem(buffer, storageError);
if (!curTitle) {
if (storageError == WUPS_STORAGE_ERROR_NOT_FOUND) {
if (allow_default) {
curTitle = titleSettingsStorage->CreateSubItem(buffer, storageError);
} else {
// If we don't allow default values, we return now.
return {};
}
}
if (!curTitle) {
DEBUG_FUNCTION_LINE_ERR("titleSettingsStorage->GetOrCreateSubItem(%s) failed: %s", buffer, WUPSStorageAPI_GetStatusStr(storageError));
return {};
}
}
Languages languages = default_language;
MCPRegion product_area = default_product_area;
storageError = curTitle->GetOrStoreDefault(VAL_LANGUAGE, languages, gCurrentLanguage);
if (storageError != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or store language for title %s: %s", buffer, WUPSStorageAPI_GetStatusStr(storageError));
return {};
}
storageError = curTitle->GetOrStoreDefault(VAL_PRODUCT_AREA, product_area, gCurrentProductArea);
if (storageError != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or store product area for title %s: %s", buffer, WUPSStorageAPI_GetStatusStr(storageError));
return {};
}
return TitleRegionInfo(titleId, languages, product_area);
}
bool saveTitleRegionInfo(const TitleRegionInfo &titleRegionInfo) {
if (!titleSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to read title settings from storage");
return false;
}
char buffer[18];
snprintf(buffer, 17, "%016llX", titleRegionInfo.titleId);
WUPSStorageError storageError;
auto curTitle = titleSettingsStorage->GetOrCreateSubItem(buffer, storageError);
if (!curTitle) {
DEBUG_FUNCTION_LINE_ERR("titleSettingsStorage->GetOrCreateSubItem(%s) failed: %s", buffer, WUPSStorageAPI_GetStatusStr(storageError));
return false;
}
if ((storageError = curTitle->Store(VAL_LANGUAGE, titleRegionInfo.language)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("curTitle->Store: VAL_LANGUAGE, %s, %d failed: %s", buffer, titleRegionInfo.language, WUPSStorageAPI_GetStatusStr(storageError));
}
if ((storageError = curTitle->Store(VAL_PRODUCT_AREA, titleRegionInfo.product_area)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("curTitle->Store: VAL_PRODUCT_AREA, %s, %d failed: %s", buffer, titleRegionInfo.product_area, WUPSStorageAPI_GetStatusStr(storageError));
}
return true;
}
static void bool_item_changed(ConfigItemBoolean *item, bool newValue) {
DEBUG_FUNCTION_LINE("Value changed for %s: %d", item->identifier ? item->identifier : "[UNKNOWN]", newValue);
if (std::string_view(VAL_AUTO_DETECTION) == item->identifier) {
gAutoDetection = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_AUTO_DETECTION, gAutoDetection);
}
} else if (std::string_view(VAL_PREFER_SYSTEM_SETTINGS) == item->identifier) {
gPreferSystemSettings = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings);
}
} else if (std::string_view(VAL_SKIP_OWN_REGION) == item->identifier) {
gSkipOwnRegion = newValue;
if (generalSettingsStorage) {
generalSettingsStorage->Store(VAL_SKIP_OWN_REGION, gSkipOwnRegion);
}
}
}
static void default_lang_changed(ConfigItemMultipleValues *item, uint32_t newValue) {
DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->identifier, newValue);
if (std::string_view(VAL_DEFAULT_LANG_EUR) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default eur lang");
gDefaultLangForEUR = (Languages) newValue;
} else if (std::string_view(VAL_DEFAULT_LANG_USA) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default usa lang");
gDefaultLangForUSA = (Languages) newValue;
} else if (std::string_view(VAL_DEFAULT_LANG_JPN) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default jpn lang");
gDefaultLangForJPN = (Languages) newValue;
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected identifier for default_lang_changed");
return;
}
if (generalSettingsStorage) {
generalSettingsStorage->Store(item->identifier, (int32_t) newValue);
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to store into general settings: sub-item was nullopt");
}
}
/*
static void default_country_changed(ConfigItemMultipleValues *item, uint32_t newValue) {
DEBUG_FUNCTION_LINE("New value in %s changed: %d", item->identifier, newValue);
if (std::string_view(VAL_DEFAULT_COUNTRY_EUR) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default eur country");
gDefaultCountryForEUR = (int32_t) newValue;
} else if (std::string_view(VAL_DEFAULT_COUNTRY_USA) == item->identifier) {
DEBUG_FUNCTION_LINE("Updated default usa country");
gDefaultCountryForUSA = (int32_t) newValue;
} else {
DEBUG_FUNCTION_LINE_WARN("Unexpected identifier for default_lang_changed");
return;
}
if (generalSettingsStorage) {
generalSettingsStorage->Store(item->identifier, (int32_t) newValue);
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to store into general settings: sub-item was nullopt");
}
}
*/
static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) {
try {
WUPSConfigCategory root = WUPSConfigCategory(rootHandle);
root.add(WUPSConfigItemBoolean::Create(VAL_AUTO_DETECTION, "Auto detect region/language",
DEFAULT_AUTO_DETECTION_VALUE, gAutoDetection,
&bool_item_changed));
root.add(WUPSConfigItemBoolean::Create(VAL_SKIP_OWN_REGION, "Force auto detection for in-region titles",
DEFAULT_SKIP_OWN_REGION, gSkipOwnRegion,
&bool_item_changed));
root.add(WUPSConfigItemBoolean::Create(VAL_PREFER_SYSTEM_SETTINGS, "Prefer system settings for in-region titles",
DEFAULT_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings,
&bool_item_changed));
constexpr WUPSConfigItemMultipleValues::ValuePair eur_lang_map[] = {
{LANG_ENGLISH, "English"},
{LANG_FRANCAIS, "Francais"},
{LANG_DEUTSCH, "Deutsch"},
{LANG_ITALIANO, "Italiano"},
{LANG_ESPANOL, "Espanol"},
{LANG_NEDERLANDS, "Nederlands"},
{LANG_PORTUGUES, "Portugues"},
{LANG_RUSSKI, "Russki"},
};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_LANG_EUR, "Default language for EUR",
DEFAULT_LANG_FOR_EUR, gDefaultLangForEUR,
eur_lang_map,
default_lang_changed));
constexpr WUPSConfigItemMultipleValues::ValuePair usa_lang_map[] = {
{LANG_ENGLISH, "English"},
{LANG_FRANCAIS, "Francais"},
{LANG_ESPANOL, "Espanol"},
{LANG_PORTUGUES, "Portugues"}};
root.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_LANG_USA, "Default language for USA",
DEFAULT_LANG_FOR_USA, gDefaultLangForUSA,
usa_lang_map,
default_lang_changed));
/*
auto expertCat = WUPSConfigCategory::Create("Expert parameters");
std::vector<WUPSConfigItemMultipleValues::ValuePair> valid_country_codes_map;
for (uint32_t i = 0; i < sizeof(countryCodeMap) / sizeof(countryCodeMap[0]); i++) {
if (countryCodeMap[i] == nullptr) {
continue;
}
valid_country_codes_map.push_back({i, countryCodeMap[i]});
}
expertCat.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_COUNTRY_USA, "Default country for USA (out-of-region titles only)",
DEFAULT_COUNTRY_FOR_USA, gDefaultCountryForUSA,
valid_country_codes_map,
default_country_changed));
expertCat.add(WUPSConfigItemMultipleValues::CreateFromValue(VAL_DEFAULT_COUNTRY_EUR, "Default country for EUR (out-of-region titles only)",
DEFAULT_COUNTRY_FOR_EUR, gDefaultCountryForEUR,
valid_country_codes_map,
default_country_changed));
root.add(std::move(expertCat));
*/
} catch (std::exception &e) {
OSReport("Exception: %s\n", e.what());
return WUPSCONFIG_API_CALLBACK_RESULT_ERROR;
}
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
static void ConfigMenuClosedCallback() {
// Save all changes
WUPSStorageError storageError;
if ((storageError = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to save storage: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
}
void InitConfigAndStorage() {
WUPSConfigAPIOptionsV1 configOptions = {.name = "Region Free Plugin"};
if (WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback) != WUPSCONFIG_API_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to init config api");
}
WUPSStorageError storageError;
rootSettingsStorage = WUPSStorageAPI::GetOrCreateSubItem(CAT_GENERAL_ROOT, storageError);
if (!rootSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create %s subitem", CAT_GENERAL_ROOT);
}
titleSettingsStorage = rootSettingsStorage->GetOrCreateSubItem(CAT_TITLE_SETTINGS, storageError);
if (!titleSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("WUPS_CreateSubItem %s failed", CAT_TITLE_SETTINGS);
}
generalSettingsStorage = rootSettingsStorage->GetOrCreateSubItem(CAT_GENERAL_SETTINGS, storageError);
if (!generalSettingsStorage) {
DEBUG_FUNCTION_LINE_ERR("Failed to get or create %s subitem", CAT_GENERAL_SETTINGS);
} else {
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_AUTO_DETECTION, gAutoDetection, DEFAULT_AUTO_DETECTION_VALUE)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_PREFER_SYSTEM_SETTINGS, gPreferSystemSettings, DEFAULT_PREFER_SYSTEM_SETTINGS)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_SKIP_OWN_REGION, gSkipOwnRegion, DEFAULT_SKIP_OWN_REGION)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_EUR, gDefaultLangForEUR, DEFAULT_LANG_FOR_EUR)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_EUR, gDefaultCountryForEUR, DEFAULT_COUNTRY_FOR_EUR)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_USA, gDefaultLangForUSA, DEFAULT_LANG_FOR_USA)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_USA, gDefaultCountryForUSA, DEFAULT_COUNTRY_FOR_USA)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_LANG_JPN, gDefaultLangForJPN, DEFAULT_LANG_FOR_JPN)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
if ((storageError = generalSettingsStorage->GetOrStoreDefault(VAL_DEFAULT_COUNTRY_JPN, gDefaultCountryForJPN, DEFAULT_COUNTRY_FOR_JPN)) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to get or store default: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
}
if ((storageError = WUPSStorageAPI::SaveStorage()) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to save storage: %s", WUPSStorageAPI::GetStatusStr(storageError).data());
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "globals.h"
#include <coreinit/mcp.h>
#include <memory>
#include <wups/config.h>
typedef struct TitleRegionInfo {
uint64_t titleId;
Languages language;
MCPRegion product_area;
} TitleRegionInfo;
std::optional<TitleRegionInfo> getTitleRegionInfo(uint64_t titleId, Languages default_language, MCPRegion default_product_area, bool allow_default_values);
bool saveTitleRegionInfo(const TitleRegionInfo &titleRegionInfo);
void InitConfigAndStorage();