diff --git a/Dockerfile b/Dockerfile index 4c278f7..5475d03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Makefile b/Makefile index 4aa59d7..7a4a5fc 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/function_patches.cpp b/src/function_patches.cpp new file mode 100644 index 0000000..b3c3d26 --- /dev/null +++ b/src/function_patches.cpp @@ -0,0 +1,68 @@ +#include "utils/SettingsUtils.h" +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include +#include + +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); \ No newline at end of file diff --git a/src/function_patches.h b/src/function_patches.h new file mode 100644 index 0000000..1ce3972 --- /dev/null +++ b/src/function_patches.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +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"))); \ No newline at end of file diff --git a/src/globals.cpp b/src/globals.cpp index ca1da7e..8e7fc65 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -1,21 +1,22 @@ #include "globals.h" +std::optional gRealRegionOpt = {}; +std::optional gRealCountryOpt = {}; +std::optional 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; \ No newline at end of file diff --git a/src/globals.h b/src/globals.h index 213ea5f..a881f80 100644 --- a/src/globals.h +++ b/src/globals.h @@ -2,13 +2,12 @@ #include "version.h" #include +#include -#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 gRealRegionOpt; +extern std::optional gRealCountryOpt; +extern std::optional 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; diff --git a/src/main.cpp b/src/main.cpp index d0f6135..79c4ec2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,19 +1,12 @@ #include "globals.h" #include "utils/ConfigUtils.h" +#include "utils/SettingsUtils.h" +#include "utils/WUPSConfigUtils.h" #include "utils/logger.h" -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include 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(*(uint32_t *) sysConfig.data); - gDefaultLanguage = static_cast(*(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 rootSettingsStorage = {}; -std::optional 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); diff --git a/src/utils/ConfigUtils.cpp b/src/utils/ConfigUtils.cpp index b9f5841..d04c935 100644 --- a/src/utils/ConfigUtils.cpp +++ b/src/utils/ConfigUtils.cpp @@ -142,7 +142,6 @@ void ConfigUtils::displayMenu() { {MCP_REGION_EUROPE, 2}, }; - std::map 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 lang_map{ + std::map 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 lang_map_to_index{ + std::map 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 lang_index_to_map{ + std::map 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); } diff --git a/src/utils/SettingsUtils.cpp b/src/utils/SettingsUtils.cpp new file mode 100644 index 0000000..e64a028 --- /dev/null +++ b/src/utils/SettingsUtils.cpp @@ -0,0 +1,67 @@ +#include "SettingsUtils.h" +#include "function_patches.h" +#include "globals.h" +#include "logger.h" +#include +#include +#include +#include +#include +#include + +template +static std::optional 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 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(*(uint32_t *) sysConfig.data); + } + UCClose(ucHandle); + } + return result; +} + +std::optional getRealCountry() { + return readU32FromConfig("cafe.cntry_reg"); +} + +std::optional getRealLanguage() { + return readU32FromConfig("cafe.language"); +} + +std::optional getRealRegion() { + std::optional 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 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 result = {}; + auto res = ACPGetTitleMetaXml(titleId, acpMetaXml); + if (res >= 0) { + result = (uint32_t) acpMetaXml->region; + } + free(acpMetaXml); + return result; +} \ No newline at end of file diff --git a/src/utils/SettingsUtils.h b/src/utils/SettingsUtils.h new file mode 100644 index 0000000..b7e08e6 --- /dev/null +++ b/src/utils/SettingsUtils.h @@ -0,0 +1,13 @@ +#pragma once + +#include "globals.h" +#include +#include + +std::optional getRealCountry(); + +std::optional getRealLanguage(); + +std::optional getRealRegion(); + +std::optional getRegionForTitle(uint64_t titleId); \ No newline at end of file diff --git a/src/utils/WUPSConfigUtils.cpp b/src/utils/WUPSConfigUtils.cpp new file mode 100644 index 0000000..d87337a --- /dev/null +++ b/src/utils/WUPSConfigUtils.cpp @@ -0,0 +1,322 @@ +#include "WUPSConfigUtils.h" +#include "globals.h" +#include "logger.h" +#include +#include +#include + +#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 rootSettingsStorage = {}; +static std::optional titleSettingsStorage = {}; +static std::optional generalSettingsStorage = {}; + +std::optional 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 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()); + } +} diff --git a/src/utils/WUPSConfigUtils.h b/src/utils/WUPSConfigUtils.h new file mode 100644 index 0000000..28f7f90 --- /dev/null +++ b/src/utils/WUPSConfigUtils.h @@ -0,0 +1,18 @@ +#pragma once + +#include "globals.h" +#include +#include +#include + +typedef struct TitleRegionInfo { + uint64_t titleId; + Languages language; + MCPRegion product_area; +} TitleRegionInfo; + +std::optional getTitleRegionInfo(uint64_t titleId, Languages default_language, MCPRegion default_product_area, bool allow_default_values); + +bool saveTitleRegionInfo(const TitleRegionInfo &titleRegionInfo); + +void InitConfigAndStorage(); \ No newline at end of file