diff --git a/src/Cafe/Account/Account.cpp b/src/Cafe/Account/Account.cpp index d022b604..729786b1 100644 --- a/src/Cafe/Account/Account.cpp +++ b/src/Cafe/Account/Account.cpp @@ -524,6 +524,10 @@ void Account::ParseFile(class FileStream* file) m_country = ConvertString(value, 16); else if (key == "SimpleAddressId") m_simple_address_id = ConvertString(value, 16); + else if (key == "TimeZoneId") + m_timezone_id = value; + else if (key == "UtcOffset") + m_utc_offset = ConvertString(value, 16); else if (key == "PrincipalId") m_principal_id = ConvertString(value, 16); else if (key == "IsPasswordCacheEnabled") diff --git a/src/Cafe/Account/Account.h b/src/Cafe/Account/Account.h index da196e42..ed32de5a 100644 --- a/src/Cafe/Account/Account.h +++ b/src/Cafe/Account/Account.h @@ -77,6 +77,8 @@ public: [[nodiscard]] std::string_view GetEmail() const { return m_email; } [[nodiscard]] uint32 GetCountry() const { return m_country; } [[nodiscard]] uint32 GetSimpleAddressId() const { return m_simple_address_id; } + [[nodiscard]] std::string_view GetTimeZoneId() const { return m_timezone_id; } + [[nodiscard]] sint64 GetUtcOffset() const { return m_utc_offset; } [[nodiscard]] uint32 GetPrincipalId() const { return m_principal_id; } [[nodiscard]] bool IsPasswordCacheEnabled() const { return m_password_cache_enabled != 0; } [[nodiscard]] const std::array& GetAccountPasswordCache() const { return m_account_password_cache; } @@ -90,6 +92,8 @@ public: void SetGender(uint8 gender) { m_gender = gender; } void SetEmail(std::string_view email) { m_email = email; } void SetCountry(uint32 country) { m_country = country; } + void SetTimeZoneId(std::string_view timezone_id) { m_timezone_id = timezone_id; } + void SetUtcOffset(sint64 utc_offset) { m_utc_offset = utc_offset; } // this will always return at least one account (default one) static const std::vector& RefreshAccounts(); @@ -123,6 +127,8 @@ private: std::string m_email; uint32 m_country = 0; uint32 m_simple_address_id = 0; + std::string m_timezone_id; + sint64 m_utc_offset; uint32 m_principal_id = 0; uint8 m_password_cache_enabled = 0; std::array m_account_password_cache{}; diff --git a/src/Cafe/GamePatch.cpp b/src/Cafe/GamePatch.cpp index 77eaff32..3f90e94a 100644 --- a/src/Cafe/GamePatch.cpp +++ b/src/Cafe/GamePatch.cpp @@ -2,6 +2,7 @@ #include "Cafe/OS/RPL/rpl.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" #include "CafeSystem.h" +#include "config/ActiveSettings.h" // Selectively add some patches based on network settings. void hleExport_breathOfTheWild_busyLoop(PPCInterpreter_t* hCPU) { @@ -268,6 +269,16 @@ static_assert(sizeof(bayo2_audioQueueFixSignature) == sizeof(bayo2_audioQueueFix uint8 cars3_avro_schema_incref[] = { 0x2C,0x03,0x00,0x00,0x94,0x21,0xFF,0xE8,0x41,0x82,0x00,0x40,0x39,0x03,0x00,0x08,0x39,0x41,0x00,0x08,0x91,0x01,0x00,0x08,0x7D,0x80,0x50,0x28,0x2C,0x0C,0xFF,0xFF,0x41,0x82,0x00,0x28,0x39,0x21,0x00,0x0C,0x38,0x0C,0x00,0x01,0x38,0xE0,0x00,0x01,0x91,0x01,0x00,0x0C,0x7C,0x00,0x49,0x2D }; +// For USA titles: 000500301001610a / 000500301001410a +uint8 miiverse_eshop_url_match_whitelist_func[] = { + // For both titles, the code fully matches with the relative + // branch targets, even if the absolute targets are different. + 0x89,0x45,0x00,0x00, // lbz r10, 0x0(r5) + 0x2c,0x0a,0x00,0x2e, // cmpwi r10, 0x2e + 0x40,0x82,0x00,0x08, // bne LAB_020ff3a8 + 0x4b,0xff,0xff,0x5c // b FUN_020ff300 +}; +uint8 wave_libopenssl_ssl_verify_cert_chain[] = { 0x94,0x21,0xff,0x58,0x93,0xc1,0x00,0xa0,0x93,0xe1,0x00,0xa4,0x7c,0x9f,0x23,0x79,0x7c,0x7e,0x1b,0x78,0x90,0x01,0x00,0xac,0x41,0x82,0x00,0x14,0x7f,0xe3,0xfb,0x78 }; // rpl function for the above applets sint32 hleIndex_h000000001 = -1; sint32 hleIndex_h000000002 = -1; @@ -461,6 +472,36 @@ void GamePatch_scan() memory_writeU32(hleAddr + 0x64, 0x60000000); } + // Patch function in Miiverse/eShop wave.rpx that matches + // a domain against another to validate its whitelist. + // This allows those browsers to load any domain. + const NetworkService service = ActiveSettings::GetNetworkService(); + if (service != NetworkService::Nintendo // Only patch for custom services. + && CafeSystem::GetForegroundTitleArgStr().ends_with("wave.rpx") + && (hleAddr = hle_locate(miiverse_eshop_url_match_whitelist_func, + nullptr, sizeof(miiverse_eshop_url_match_whitelist_func)))) + { + cemuLog_log(LogType::Force, "Patching Miiverse/eShop whitelist check at: 0x{:08x}", hleAddr); + // Always return 1. (Note that the matched pattern is not at the beginning but still works) + memory_writeU32(hleAddr, 0x38600001); + memory_writeU32(hleAddr + 0x4, 0x4e800020); + // Note that the same applies to Account Settings, but + // its version of this function differs a lot. + // Search: 88 0c ff ff 2c 00 00 2e (lbz r0,-0x1(r12); cmpwi r0,0x2e) + } + + // Additionally patch out SSL checks for libopenssl.rpl, used by the browser. + if (IsNetworkServiceSSLDisabled(service) + && RPLLoader_GetHandleByModuleName("libopenssl.rpl") != RPL_INVALID_HANDLE + && (hleAddr = hle_locate(wave_libopenssl_ssl_verify_cert_chain, + nullptr, sizeof(wave_libopenssl_ssl_verify_cert_chain)))) + { + cemuLog_log(LogType::Force, "Patching OpenSSL ssl_verify_cert_chain at: 0x{:08x}", hleAddr); + // Reference: https://github.com/PretendoNetwork/Meowth/blob/meowth/src/patcher/patches/webkit_applets.cpp + memory_writeU32(hleAddr + 0x28, 0x60000000); + memory_writeU32(hleAddr + 0x40, 0x38600001); + } + uint32 hleInstallEnd = GetTickCount(); cemuLog_log(LogType::Force, "HLE scan time: {}ms", hleInstallEnd-hleInstallStart); } diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index de6ef9e0..11e613ac 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -49,6 +49,8 @@ struct actAccountData_t // country & language uint32 countryIndex; char country[8]; + char timeZoneId[16]; + sint64 utcOffset; // Mii FFLData_t miiData; uint16le miiNickname[ACT_NICKNAME_LENGTH]; @@ -84,6 +86,8 @@ void FillAccountData(const Account& account, const bool online_enabled, int inde // country & language data.countryIndex = account.GetCountry(); strcpy(data.country, NCrypto::GetCountryAsString(data.countryIndex)); + std::copy(account.GetTimeZoneId().cbegin(), account.GetTimeZoneId().cend(), data.timeZoneId); + data.utcOffset = account.GetUtcOffset() / 1'000'000; // Mii std::copy(account.GetMiiData().begin(), account.GetMiiData().end(), (uint8*)&data.miiData); std::copy(account.GetMiiName().begin(), account.GetMiiName().end(), data.miiNickname); @@ -808,6 +812,13 @@ int iosuAct_thread() strcpy(actCemuRequest->resultString.strBuffer, _actAccountData[accountIndex].country); actCemuRequest->setACTReturnCode(0); } + else if (actCemuRequest->requestCode == IOSU_ARC_TIMEZONEID) + { + accountIndex = iosuAct_getAccountIndexBySlot(actCemuRequest->accountSlot); + _cancelIfAccountDoesNotExist(); + strcpy(actCemuRequest->resultString.strBuffer, _actAccountData[accountIndex].timeZoneId); + actCemuRequest->setACTReturnCode(0); + } else if (actCemuRequest->requestCode == IOSU_ARC_ISNETWORKACCOUNT) { accountIndex = iosuAct_getAccountIndexBySlot(actCemuRequest->accountSlot); diff --git a/src/Cafe/IOSU/legacy/iosu_act.h b/src/Cafe/IOSU/legacy/iosu_act.h index 8ed408a4..6d5c5c26 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.h +++ b/src/Cafe/IOSU/legacy/iosu_act.h @@ -117,6 +117,7 @@ struct iosuActCemuRequest_t #define IOSU_ARC_MIIDATA 0x0A #define IOSU_ARC_ACQUIREINDEPENDENTTOKEN 0x0B #define IOSU_ARC_ACQUIREPIDBYNNID 0x0C +#define IOSU_ARC_TIMEZONEID 0x0D uint32 iosuAct_getAccountIdOfCurrentAccount(); diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp index 1916a18d..0679491a 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv.cpp @@ -15,22 +15,6 @@ namespace nn { namespace olv { - struct PortalAppParam_t - { - /* +0x1A663B */ char serviceToken[32]; // size is unknown - }; - - void exportPortalAppParam_GetServiceToken(PPCInterpreter_t* hCPU) - { - // r3 = PortalAppParam - ppcDefineParamTypePtr(portalAppParam, PortalAppParam_t, 0); - - strcpy(portalAppParam->serviceToken, "servicetoken"); - // this token is probably just the act IndependentServiceToken for the Miiverse title? - - osLib_returnFromFunction(hCPU, memory_getVirtualOffsetFromPointer(&portalAppParam->serviceToken)); - } - static SysAllocator s_OlvReleaseBgThread; SysAllocator s_OlvReleaseBgThreadStack; SysAllocator s_OlvReleaseBgThreadName; @@ -126,8 +110,6 @@ namespace nn cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::NN_OLV); - osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken); - cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadPostDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv28UploadPostDataByPostAppParam", LogType::NN_OLV); cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadCommentDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv31UploadCommentDataByPostAppParam", LogType::NN_OLV); cafeExportRegisterFunc(StubPostApp, "nn_olv", "UploadDirectMessageDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv37UploadDirectMessageDataByPostAppParam", LogType::NN_OLV); diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp index 8f7a7e03..cca14eb0 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp @@ -277,6 +277,21 @@ namespace nn return OLV_RESULT_SUCCESS; } + sint32 InitializePortalApp(nn::olv::PortalAppParam* pPortalAppParam, nn::olv::InitializeParam* pInitializeParam) + { + sint32 result = Initialize(pInitializeParam); + if (result != OLV_RESULT_SUCCESS) + return result; + + memcpy(pPortalAppParam->m_ParamPack, g_ParamPack.encodedParamPack, sizeof(g_ParamPack.encodedParamPack)); + memcpy(pPortalAppParam->m_ServiceToken, g_DiscoveryResults.serviceToken, sizeof(g_DiscoveryResults.serviceToken)); + + snprintf(reinterpret_cast(pPortalAppParam->m_StartUrl), sizeof(pPortalAppParam->m_StartUrl), + "%s/titles/show?src=menu", g_DiscoveryResults.portalEndpoint); + + return OLV_RESULT_SUCCESS; + } + namespace Report { uint32 GetReportTypes() @@ -295,4 +310,4 @@ namespace nn return g_IsInitialized; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h index 51dce8fe..e635fa57 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h @@ -101,6 +101,58 @@ namespace nn }; static_assert(sizeof(nn::olv::InitializeParam) == 0x40, "sizeof(nn::olv::InitializeParam) != 0x40"); + class PortalAppParam + { + public: + PortalAppParam() + { + m_ParamPack[0] = 0; + m_ServiceToken[0] = 0; + m_StartUrl[0] = 0; + } + static PortalAppParam* __ctor(PortalAppParam* _this) + { + if (!_this) + { + assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN + return nullptr; + } + else + return new (_this) PortalAppParam(); + } + + uint8be* GetParamPack() + { + return m_ParamPack; + } + static uint8be* __GetParamPack(PortalAppParam* _this) + { + return _this->GetParamPack(); + } + + uint8be* GetServiceToken() + { + return m_ServiceToken; + } + static uint8be* __GetServiceToken(PortalAppParam* _this) + { + return _this->GetServiceToken(); + } + + uint8be* GetStartUrl() + { + return m_StartUrl; + } + static uint8be* __GetStartUrl(PortalAppParam* _this) + { + return _this->GetStartUrl(); + } + + public: + /* +0x1A5C3C */ uint8be m_ParamPack[0x200]; + /* +0x1A663B */ uint8be m_ServiceToken[0x201]; // IndependentServiceToken for Miiverse title + /* +0x1A5E3C */ uint8be m_StartUrl[0x7ff]; // https://portal-us.olv.nintendo.net/titles/show?src=menu + }; namespace Report { @@ -110,6 +162,7 @@ namespace nn bool IsInitialized(); sint32 Initialize(nn::olv::InitializeParam* pParam); + sint32 InitializePortalApp(nn::olv::PortalAppParam* pPortalAppParam, nn::olv::InitializeParam* pInitializeParam); static void loadOliveInitializeTypes() { @@ -123,6 +176,12 @@ namespace nn cafeExportRegisterFunc(InitializeParam::__SetWork, "nn_olv", "SetWork__Q3_2nn3olv15InitializeParamFPUcUi", LogType::NN_OLV); cafeExportRegisterFunc(InitializeParam::__SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv15InitializeParamFUi", LogType::NN_OLV); cafeExportRegisterFunc(InitializeParam::__SetSysArgs, "nn_olv", "SetSysArgs__Q3_2nn3olv15InitializeParamFPCvUi", LogType::NN_OLV); + + cafeExportRegisterFunc(InitializePortalApp, "nn_olv", "InitializePortalApp__Q3_2nn3olv6hiddenFPQ4_2nn3olv6hidden14PortalAppParamPCQ3_2nn3olv15InitializeParam", LogType::NN_OLV); + cafeExportRegisterFunc(PortalAppParam::__ctor, "nn_olv", "__ct__Q4_2nn3olv6hidden14PortalAppParamFv", LogType::NN_OLV); + cafeExportRegisterFunc(PortalAppParam::__GetParamPack, "nn_olv", "GetParamPack__Q4_2nn3olv6hidden14PortalAppParamCFv", LogType::NN_OLV); + cafeExportRegisterFunc(PortalAppParam::__GetServiceToken, "nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", LogType::NN_OLV); + cafeExportRegisterFunc(PortalAppParam::__GetStartUrl, "nn_olv", "GetStartUrl__Q4_2nn3olv6hidden14PortalAppParamCFv", LogType::NN_OLV); } } } \ No newline at end of file