mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-22 17:19:18 +01:00
DownloadManager: Always use Nintendo servers + additional streamlining
- Download manager now always uses Nintendo servers. Requires only a valid OTP and SEEPROM dump so you can use it in combination with a Pretendo setup even without a NNID - Account drop down removed from download manager since it's not required - Internally all our API requests now support overriding which service to use - Drop support for act-url and ecs-url command line parameters. Usage of network_services.xml ("custom" option in the UI) is preferred
This commit is contained in:
parent
989e2b8c8c
commit
efbbb817fe
@ -498,7 +498,7 @@ namespace iosu
|
|||||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
|
||||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
|
||||||
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo) // remove Pretendo Function once SSL is in the Service
|
if (IsNetworkServiceSSLDisabled(ActiveSettings::GetNetworkService()))
|
||||||
{
|
{
|
||||||
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
|
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
|
||||||
}
|
}
|
||||||
|
@ -292,16 +292,6 @@ void iosuCrypto_generateDeviceCertificate()
|
|||||||
BN_CTX_free(context);
|
BN_CTX_free(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool iosuCrypto_hasAllDataForLogin()
|
|
||||||
{
|
|
||||||
if (hasOtpMem == false)
|
|
||||||
return false;
|
|
||||||
if (hasSeepromMem == false)
|
|
||||||
return false;
|
|
||||||
// todo - check if certificates are available
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
sint32 iosuCrypto_getDeviceCertificateBase64Encoded(char* output)
|
sint32 iosuCrypto_getDeviceCertificateBase64Encoded(char* output)
|
||||||
{
|
{
|
||||||
iosuCrypto_base64Encode((uint8*)&g_wiiuDeviceCert, sizeof(g_wiiuDeviceCert), output);
|
iosuCrypto_base64Encode((uint8*)&g_wiiuDeviceCert, sizeof(g_wiiuDeviceCert), output);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
void iosuCrypto_init();
|
void iosuCrypto_init();
|
||||||
|
|
||||||
bool iosuCrypto_hasAllDataForLogin();
|
|
||||||
bool iosuCrypto_getDeviceId(uint32* deviceId);
|
bool iosuCrypto_getDeviceId(uint32* deviceId);
|
||||||
void iosuCrypto_getDeviceSerialString(char* serialString);
|
void iosuCrypto_getDeviceSerialString(char* serialString);
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ namespace iosu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = NAPI::IDBE_Request(titleId);
|
auto result = NAPI::IDBE_Request(ActiveSettings::GetNetworkService(), titleId);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
memset(idbeIconOutput, 0, sizeof(NAPI::IDBEIconDataV0));
|
memset(idbeIconOutput, 0, sizeof(NAPI::IDBEIconDataV0));
|
||||||
|
@ -42,7 +42,7 @@ namespace nn
|
|||||||
|
|
||||||
void asyncDownloadIconFile(uint64 titleId, nnIdbeEncryptedIcon_t* iconOut, OSThread_t* thread)
|
void asyncDownloadIconFile(uint64 titleId, nnIdbeEncryptedIcon_t* iconOut, OSThread_t* thread)
|
||||||
{
|
{
|
||||||
std::vector<uint8> idbeData = NAPI::IDBE_RequestRawEncrypted(titleId);
|
std::vector<uint8> idbeData = NAPI::IDBE_RequestRawEncrypted(ActiveSettings::GetNetworkService(), titleId);
|
||||||
if (idbeData.size() != sizeof(nnIdbeEncryptedIcon_t))
|
if (idbeData.size() != sizeof(nnIdbeEncryptedIcon_t))
|
||||||
{
|
{
|
||||||
// icon does not exist or has the wrong size
|
// icon does not exist or has the wrong size
|
||||||
|
@ -43,7 +43,7 @@ namespace nn
|
|||||||
return res;
|
return res;
|
||||||
|
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(reqUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
req.initate(ActiveSettings::GetNetworkService(), reqUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
||||||
InitializeOliveRequest(req);
|
InitializeOliveRequest(req);
|
||||||
|
|
||||||
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
||||||
|
@ -195,7 +195,7 @@ namespace nn
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
||||||
InitializeOliveRequest(req);
|
InitializeOliveRequest(req);
|
||||||
|
|
||||||
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
||||||
|
@ -50,7 +50,7 @@ namespace nn
|
|||||||
|
|
||||||
|
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
||||||
InitializeOliveRequest(req);
|
InitializeOliveRequest(req);
|
||||||
|
|
||||||
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
||||||
|
@ -40,7 +40,7 @@ namespace nn
|
|||||||
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.favorite", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
|
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.favorite", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
|
||||||
|
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
|
||||||
InitializeOliveRequest(req);
|
InitializeOliveRequest(req);
|
||||||
|
|
||||||
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
StackAllocator<coreinit::OSEvent> requestDoneEvent;
|
||||||
|
@ -33,14 +33,7 @@ void DownloadManager::downloadTitleVersionList()
|
|||||||
{
|
{
|
||||||
if (m_hasTitleVersionList)
|
if (m_hasTitleVersionList)
|
||||||
return;
|
return;
|
||||||
NAPI::AuthInfo authInfo;
|
NAPI::AuthInfo authInfo = GetAuthInfo(false);
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
auto versionListVersionResult = NAPI::TAG_GetVersionListVersion(authInfo);
|
auto versionListVersionResult = NAPI::TAG_GetVersionListVersion(authInfo);
|
||||||
if (!versionListVersionResult.isValid)
|
if (!versionListVersionResult.isValid)
|
||||||
return;
|
return;
|
||||||
@ -195,15 +188,7 @@ public:
|
|||||||
|
|
||||||
bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
|
bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
|
||||||
{
|
{
|
||||||
NAPI::AuthInfo authInfo;
|
NAPI::AuthInfo authInfo = GetAuthInfo(false);
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
|
|
||||||
// query IAS/ECS account id and device token (if not cached)
|
// query IAS/ECS account id and device token (if not cached)
|
||||||
auto rChallenge = NAPI::IAS_GetChallenge(authInfo);
|
auto rChallenge = NAPI::IAS_GetChallenge(authInfo);
|
||||||
if (rChallenge.apiError != NAPI_RESULT::SUCCESS)
|
if (rChallenge.apiError != NAPI_RESULT::SUCCESS)
|
||||||
@ -211,7 +196,6 @@ bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
|
|||||||
auto rRegistrationInfo = NAPI::IAS_GetRegistrationInfo_QueryInfo(authInfo, rChallenge.challenge);
|
auto rRegistrationInfo = NAPI::IAS_GetRegistrationInfo_QueryInfo(authInfo, rChallenge.challenge);
|
||||||
if (rRegistrationInfo.apiError != NAPI_RESULT::SUCCESS)
|
if (rRegistrationInfo.apiError != NAPI_RESULT::SUCCESS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_iasToken.serviceAccountId = rRegistrationInfo.accountId;
|
m_iasToken.serviceAccountId = rRegistrationInfo.accountId;
|
||||||
m_iasToken.deviceToken = rRegistrationInfo.deviceToken;
|
m_iasToken.deviceToken = rRegistrationInfo.deviceToken;
|
||||||
// store to cache
|
// store to cache
|
||||||
@ -221,24 +205,13 @@ bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
|
|||||||
std::vector<uint8> serializedData;
|
std::vector<uint8> serializedData;
|
||||||
if (!storedTokenInfo.serialize(serializedData))
|
if (!storedTokenInfo.serialize(serializedData))
|
||||||
return false;
|
return false;
|
||||||
s_nupFileCache->AddFileAsync({ fmt::format("{}/token_info", m_authInfo.nnidAccountName) }, serializedData.data(), serializedData.size());
|
s_nupFileCache->AddFileAsync({ fmt::format("{}/token_info", m_authInfo.cachefileName) }, serializedData.data(), serializedData.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::_connect_queryAccountStatusAndServiceURLs()
|
bool DownloadManager::_connect_queryAccountStatusAndServiceURLs()
|
||||||
{
|
{
|
||||||
NAPI::AuthInfo authInfo;
|
NAPI::AuthInfo authInfo = GetAuthInfo(true);
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
|
|
||||||
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
|
|
||||||
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
|
|
||||||
|
|
||||||
NAPI::NAPI_ECSGetAccountStatus_Result accountStatusResult = NAPI::ECS_GetAccountStatus(authInfo);
|
NAPI::NAPI_ECSGetAccountStatus_Result accountStatusResult = NAPI::ECS_GetAccountStatus(authInfo);
|
||||||
if (accountStatusResult.apiError != NAPI_RESULT::SUCCESS)
|
if (accountStatusResult.apiError != NAPI_RESULT::SUCCESS)
|
||||||
{
|
{
|
||||||
@ -291,7 +264,7 @@ void DownloadManager::loadTicketCache()
|
|||||||
m_ticketCache.clear();
|
m_ticketCache.clear();
|
||||||
cemu_assert_debug(m_ticketCache.empty());
|
cemu_assert_debug(m_ticketCache.empty());
|
||||||
std::vector<uint8> ticketCacheBlob;
|
std::vector<uint8> ticketCacheBlob;
|
||||||
if (!s_nupFileCache->GetFile({ fmt::format("{}/eticket_cache", m_authInfo.nnidAccountName) }, ticketCacheBlob))
|
if (!s_nupFileCache->GetFile({ fmt::format("{}/eticket_cache", m_authInfo.cachefileName) }, ticketCacheBlob))
|
||||||
return;
|
return;
|
||||||
MemStreamReader memReader(ticketCacheBlob.data(), ticketCacheBlob.size());
|
MemStreamReader memReader(ticketCacheBlob.data(), ticketCacheBlob.size());
|
||||||
uint8 version = memReader.readBE<uint8>();
|
uint8 version = memReader.readBE<uint8>();
|
||||||
@ -343,23 +316,12 @@ void DownloadManager::storeTicketCache()
|
|||||||
memWriter.writePODVector(cert);
|
memWriter.writePODVector(cert);
|
||||||
}
|
}
|
||||||
auto serializedBlob = memWriter.getResult();
|
auto serializedBlob = memWriter.getResult();
|
||||||
s_nupFileCache->AddFileAsync({ fmt::format("{}/eticket_cache", m_authInfo.nnidAccountName) }, serializedBlob.data(), serializedBlob.size());
|
s_nupFileCache->AddFileAsync({ fmt::format("{}/eticket_cache", m_authInfo.cachefileName) }, serializedBlob.data(), serializedBlob.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::syncAccountTickets()
|
bool DownloadManager::syncAccountTickets()
|
||||||
{
|
{
|
||||||
NAPI::AuthInfo authInfo;
|
NAPI::AuthInfo authInfo = GetAuthInfo(true);
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
|
|
||||||
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
|
|
||||||
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
|
|
||||||
|
|
||||||
// query TIV list from server
|
// query TIV list from server
|
||||||
NAPI::NAPI_ECSAccountListETicketIds_Result resultTicketIds = NAPI::ECS_AccountListETicketIds(authInfo);
|
NAPI::NAPI_ECSAccountListETicketIds_Result resultTicketIds = NAPI::ECS_AccountListETicketIds(authInfo);
|
||||||
if (!resultTicketIds.isValid())
|
if (!resultTicketIds.isValid())
|
||||||
@ -425,19 +387,7 @@ bool DownloadManager::syncAccountTickets()
|
|||||||
bool DownloadManager::syncSystemTitleTickets()
|
bool DownloadManager::syncSystemTitleTickets()
|
||||||
{
|
{
|
||||||
setStatusMessage(_("Downloading system tickets...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING);
|
setStatusMessage(_("Downloading system tickets...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING);
|
||||||
// todo - add GetAuth() function
|
NAPI::AuthInfo authInfo = GetAuthInfo(true);
|
||||||
NAPI::AuthInfo authInfo;
|
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
|
|
||||||
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
|
|
||||||
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
|
|
||||||
|
|
||||||
auto querySystemTitleTicket = [&](uint64 titleId) -> void
|
auto querySystemTitleTicket = [&](uint64 titleId) -> void
|
||||||
{
|
{
|
||||||
// check if cached already
|
// check if cached already
|
||||||
@ -520,8 +470,7 @@ bool DownloadManager::syncUpdateTickets()
|
|||||||
if (findTicketByTitleIdAndVersion(itr.titleId, itr.availableTitleVersion))
|
if (findTicketByTitleIdAndVersion(itr.titleId, itr.availableTitleVersion))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
NAPI::AuthInfo dummyAuth;
|
auto cetkResult = NAPI::CCS_GetCETK(GetDownloadMgrNetworkService(), itr.titleId, itr.availableTitleVersion);
|
||||||
auto cetkResult = NAPI::CCS_GetCETK(dummyAuth, itr.titleId, itr.availableTitleVersion);
|
|
||||||
if (!cetkResult.isValid)
|
if (!cetkResult.isValid)
|
||||||
continue;
|
continue;
|
||||||
NCrypto::ETicketParser ticketParser;
|
NCrypto::ETicketParser ticketParser;
|
||||||
@ -657,7 +606,7 @@ void DownloadManager::_handle_connect()
|
|||||||
if (s_nupFileCache)
|
if (s_nupFileCache)
|
||||||
{
|
{
|
||||||
std::vector<uint8> serializationBlob;
|
std::vector<uint8> serializationBlob;
|
||||||
if (s_nupFileCache->GetFile({ fmt::format("{}/token_info", m_authInfo.nnidAccountName) }, serializationBlob))
|
if (s_nupFileCache->GetFile({ fmt::format("{}/token_info", m_authInfo.cachefileName) }, serializationBlob))
|
||||||
{
|
{
|
||||||
StoredTokenInfo storedTokenInfo;
|
StoredTokenInfo storedTokenInfo;
|
||||||
if (storedTokenInfo.deserialize(serializationBlob))
|
if (storedTokenInfo.deserialize(serializationBlob))
|
||||||
@ -683,7 +632,7 @@ void DownloadManager::_handle_connect()
|
|||||||
if (!_connect_queryAccountStatusAndServiceURLs())
|
if (!_connect_queryAccountStatusAndServiceURLs())
|
||||||
{
|
{
|
||||||
m_connectState.store(CONNECT_STATE::FAILED);
|
m_connectState.store(CONNECT_STATE::FAILED);
|
||||||
setStatusMessage(_("Failed to query account status. Invalid account information?").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
setStatusMessage(_("Failed to query account status").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// load ticket cache and sync
|
// load ticket cache and sync
|
||||||
@ -692,7 +641,7 @@ void DownloadManager::_handle_connect()
|
|||||||
if (!syncTicketCache())
|
if (!syncTicketCache())
|
||||||
{
|
{
|
||||||
m_connectState.store(CONNECT_STATE::FAILED);
|
m_connectState.store(CONNECT_STATE::FAILED);
|
||||||
setStatusMessage(_("Failed to request tickets (invalid NNID?)").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
setStatusMessage(_("Failed to request tickets").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
searchForIncompleteDownloads();
|
searchForIncompleteDownloads();
|
||||||
@ -713,22 +662,10 @@ void DownloadManager::connect(
|
|||||||
std::string_view serial,
|
std::string_view serial,
|
||||||
std::string_view deviceCertBase64)
|
std::string_view deviceCertBase64)
|
||||||
{
|
{
|
||||||
if (nnidAccountName.empty())
|
|
||||||
{
|
|
||||||
m_connectState.store(CONNECT_STATE::FAILED);
|
|
||||||
setStatusMessage(_("This account is not linked with an NNID").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
runManager();
|
runManager();
|
||||||
m_authInfo.nnidAccountName = nnidAccountName;
|
m_authInfo.nnidAccountName = nnidAccountName;
|
||||||
m_authInfo.passwordHash = passwordHash;
|
m_authInfo.passwordHash = passwordHash;
|
||||||
if (std::all_of(m_authInfo.passwordHash.begin(), m_authInfo.passwordHash.end(), [](uint8 v) { return v == 0; }))
|
m_authInfo.cachefileName = nnidAccountName.empty() ? "DefaultName" : nnidAccountName;
|
||||||
{
|
|
||||||
cemuLog_log(LogType::Force, "DLMgr: Invalid password hash");
|
|
||||||
m_connectState.store(CONNECT_STATE::FAILED);
|
|
||||||
setStatusMessage(_("Failed. Account does not have password set").utf8_string(), DLMGR_STATUS_CODE::FAILED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_authInfo.region = region;
|
m_authInfo.region = region;
|
||||||
m_authInfo.country = country;
|
m_authInfo.country = country;
|
||||||
m_authInfo.deviceCertBase64 = deviceCertBase64;
|
m_authInfo.deviceCertBase64 = deviceCertBase64;
|
||||||
@ -744,6 +681,31 @@ bool DownloadManager::IsConnected() const
|
|||||||
return m_connectState.load() != CONNECT_STATE::UNINITIALIZED;
|
return m_connectState.load() != CONNECT_STATE::UNINITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkService DownloadManager::GetDownloadMgrNetworkService()
|
||||||
|
{
|
||||||
|
return NetworkService::Nintendo;
|
||||||
|
}
|
||||||
|
|
||||||
|
NAPI::AuthInfo DownloadManager::GetAuthInfo(bool withIasToken)
|
||||||
|
{
|
||||||
|
NAPI::AuthInfo authInfo;
|
||||||
|
authInfo.serviceOverwrite = GetDownloadMgrNetworkService();
|
||||||
|
authInfo.accountId = m_authInfo.nnidAccountName;
|
||||||
|
authInfo.passwordHash = m_authInfo.passwordHash;
|
||||||
|
authInfo.deviceId = m_authInfo.deviceId;
|
||||||
|
authInfo.serial = m_authInfo.serial;
|
||||||
|
authInfo.country = m_authInfo.country;
|
||||||
|
authInfo.region = m_authInfo.region;
|
||||||
|
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
||||||
|
if(withIasToken)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(!m_iasToken.serviceAccountId.empty());
|
||||||
|
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
|
||||||
|
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
|
||||||
|
}
|
||||||
|
return authInfo;
|
||||||
|
}
|
||||||
|
|
||||||
/* package / downloading */
|
/* package / downloading */
|
||||||
|
|
||||||
// start/resume/retry download
|
// start/resume/retry download
|
||||||
@ -1022,17 +984,7 @@ void DownloadManager::reportPackageProgress(Package* package, uint32 currentProg
|
|||||||
|
|
||||||
void DownloadManager::asyncPackageDownloadTMD(Package* package)
|
void DownloadManager::asyncPackageDownloadTMD(Package* package)
|
||||||
{
|
{
|
||||||
NAPI::AuthInfo authInfo;
|
NAPI::AuthInfo authInfo = GetAuthInfo(true);
|
||||||
authInfo.accountId = m_authInfo.nnidAccountName;
|
|
||||||
authInfo.passwordHash = m_authInfo.passwordHash;
|
|
||||||
authInfo.deviceId = m_authInfo.deviceId;
|
|
||||||
authInfo.serial = m_authInfo.serial;
|
|
||||||
authInfo.country = m_authInfo.country;
|
|
||||||
authInfo.region = m_authInfo.region;
|
|
||||||
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
|
|
||||||
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
|
|
||||||
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
|
|
||||||
|
|
||||||
TitleIdParser titleIdParser(package->titleId);
|
TitleIdParser titleIdParser(package->titleId);
|
||||||
NAPI::NAPI_CCSGetTMD_Result tmdResult;
|
NAPI::NAPI_CCSGetTMD_Result tmdResult;
|
||||||
if (titleIdParser.GetType() == TitleIdParser::TITLE_TYPE::AOC)
|
if (titleIdParser.GetType() == TitleIdParser::TITLE_TYPE::AOC)
|
||||||
@ -1196,7 +1148,7 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i
|
|||||||
setPackageError(package, _("Cannot create file").utf8_string());
|
setPackageError(package, _("Cannot create file").utf8_string());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!NAPI::CCS_GetContentFile(titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData))
|
if (!NAPI::CCS_GetContentFile(GetDownloadMgrNetworkService(), titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData))
|
||||||
{
|
{
|
||||||
setPackageError(package, _("Download failed").utf8_string());
|
setPackageError(package, _("Download failed").utf8_string());
|
||||||
delete callbackInfoData.fileOutput;
|
delete callbackInfoData.fileOutput;
|
||||||
@ -1490,7 +1442,7 @@ void DownloadManager::prepareIDBE(uint64 titleId)
|
|||||||
if (s_nupFileCache->GetFile({ fmt::format("idbe/{0:016x}", titleId) }, idbeFile) && idbeFile.size() == sizeof(NAPI::IDBEIconDataV0))
|
if (s_nupFileCache->GetFile({ fmt::format("idbe/{0:016x}", titleId) }, idbeFile) && idbeFile.size() == sizeof(NAPI::IDBEIconDataV0))
|
||||||
return addToCache(titleId, (NAPI::IDBEIconDataV0*)(idbeFile.data()));
|
return addToCache(titleId, (NAPI::IDBEIconDataV0*)(idbeFile.data()));
|
||||||
// not cached, query from server
|
// not cached, query from server
|
||||||
std::optional<NAPI::IDBEIconDataV0> iconData = NAPI::IDBE_Request(titleId);
|
std::optional<NAPI::IDBEIconDataV0> iconData = NAPI::IDBE_Request(GetDownloadMgrNetworkService(), titleId);
|
||||||
if (!iconData)
|
if (!iconData)
|
||||||
return;
|
return;
|
||||||
s_nupFileCache->AddFileAsync({ fmt::format("idbe/{0:016x}", titleId) }, (uint8*)&(*iconData), sizeof(NAPI::IDBEIconDataV0));
|
s_nupFileCache->AddFileAsync({ fmt::format("idbe/{0:016x}", titleId) }, (uint8*)&(*iconData), sizeof(NAPI::IDBEIconDataV0));
|
||||||
|
@ -2,17 +2,14 @@
|
|||||||
#include "util/helpers/Semaphore.h"
|
#include "util/helpers/Semaphore.h"
|
||||||
#include "Cemu/ncrypto/ncrypto.h"
|
#include "Cemu/ncrypto/ncrypto.h"
|
||||||
#include "Cafe/TitleList/TitleId.h"
|
#include "Cafe/TitleList/TitleId.h"
|
||||||
|
|
||||||
#include "util/helpers/ConcurrentQueue.h"
|
#include "util/helpers/ConcurrentQueue.h"
|
||||||
|
#include "config/NetworkSettings.h"
|
||||||
|
|
||||||
#include <functional>
|
// forward declarations
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <future>
|
|
||||||
|
|
||||||
namespace NAPI
|
namespace NAPI
|
||||||
{
|
{
|
||||||
struct IDBEIconDataV0;
|
struct IDBEIconDataV0;
|
||||||
|
struct AuthInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NCrypto
|
namespace NCrypto
|
||||||
@ -86,7 +83,6 @@ public:
|
|||||||
|
|
||||||
bool IsConnected() const;
|
bool IsConnected() const;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* connect / login */
|
/* connect / login */
|
||||||
|
|
||||||
@ -101,6 +97,7 @@ private:
|
|||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
std::string cachefileName;
|
||||||
std::string nnidAccountName;
|
std::string nnidAccountName;
|
||||||
std::array<uint8, 32> passwordHash;
|
std::array<uint8, 32> passwordHash;
|
||||||
std::string deviceCertBase64;
|
std::string deviceCertBase64;
|
||||||
@ -123,6 +120,9 @@ private:
|
|||||||
bool _connect_refreshIASAccountIdAndDeviceToken();
|
bool _connect_refreshIASAccountIdAndDeviceToken();
|
||||||
bool _connect_queryAccountStatusAndServiceURLs();
|
bool _connect_queryAccountStatusAndServiceURLs();
|
||||||
|
|
||||||
|
NetworkService GetDownloadMgrNetworkService();
|
||||||
|
NAPI::AuthInfo GetAuthInfo(bool withIasToken);
|
||||||
|
|
||||||
/* idbe cache */
|
/* idbe cache */
|
||||||
public:
|
public:
|
||||||
void prepareIDBE(uint64 titleId);
|
void prepareIDBE(uint64 titleId);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <optional>
|
|
||||||
#include "config/CemuConfig.h" // for ConsoleLanguage
|
#include "config/CemuConfig.h" // for ConsoleLanguage
|
||||||
|
#include "config/NetworkSettings.h" // for NetworkService
|
||||||
|
#include "config/ActiveSettings.h" // for GetNetworkService()
|
||||||
|
|
||||||
enum class NAPI_RESULT
|
enum class NAPI_RESULT
|
||||||
{
|
{
|
||||||
@ -16,8 +17,6 @@ namespace NAPI
|
|||||||
// common auth info structure shared by ACT, ECS and IAS service
|
// common auth info structure shared by ACT, ECS and IAS service
|
||||||
struct AuthInfo
|
struct AuthInfo
|
||||||
{
|
{
|
||||||
// todo - constructor for account name + raw password
|
|
||||||
|
|
||||||
// nnid
|
// nnid
|
||||||
std::string accountId;
|
std::string accountId;
|
||||||
std::array<uint8, 32> passwordHash;
|
std::array<uint8, 32> passwordHash;
|
||||||
@ -41,9 +40,13 @@ namespace NAPI
|
|||||||
std::string deviceToken;
|
std::string deviceToken;
|
||||||
}IASToken;
|
}IASToken;
|
||||||
|
|
||||||
// ACT token (for account.nintendo.net requests)
|
// service selection, if not set fall back to global setting
|
||||||
|
std::optional<NetworkService> serviceOverwrite;
|
||||||
|
|
||||||
|
NetworkService GetService() const
|
||||||
|
{
|
||||||
|
return serviceOverwrite.value_or(ActiveSettings::GetNetworkService());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool NAPI_MakeAuthInfoFromCurrentAccount(AuthInfo& authInfo); // helper function. Returns false if online credentials/dumped files are not available
|
bool NAPI_MakeAuthInfoFromCurrentAccount(AuthInfo& authInfo); // helper function. Returns false if online credentials/dumped files are not available
|
||||||
@ -232,9 +235,9 @@ namespace NAPI
|
|||||||
|
|
||||||
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion);
|
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion);
|
||||||
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId);
|
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId);
|
||||||
NAPI_CCSGetETicket_Result CCS_GetCETK(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion);
|
NAPI_CCSGetETicket_Result CCS_GetCETK(NetworkService service, uint64 titleId, uint16 titleVersion);
|
||||||
bool CCS_GetContentFile(uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
|
bool CCS_GetContentFile(NetworkService service, uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
|
||||||
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(uint64 titleId, uint32 contentId);
|
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(NetworkService service, uint64 titleId, uint32 contentId);
|
||||||
|
|
||||||
/* IDBE */
|
/* IDBE */
|
||||||
|
|
||||||
@ -286,8 +289,8 @@ namespace NAPI
|
|||||||
|
|
||||||
static_assert(sizeof(IDBEHeader) == 2+32);
|
static_assert(sizeof(IDBEHeader) == 2+32);
|
||||||
|
|
||||||
std::optional<IDBEIconDataV0> IDBE_Request(uint64 titleId);
|
std::optional<IDBEIconDataV0> IDBE_Request(NetworkService networkService, uint64 titleId);
|
||||||
std::vector<uint8> IDBE_RequestRawEncrypted(uint64 titleId); // same as IDBE_Request but doesn't strip the header and decrypt the IDBE
|
std::vector<uint8> IDBE_RequestRawEncrypted(NetworkService networkService, uint64 titleId); // same as IDBE_Request but doesn't strip the header and decrypt the IDBE
|
||||||
|
|
||||||
/* Version list */
|
/* Version list */
|
||||||
|
|
||||||
|
@ -14,6 +14,21 @@
|
|||||||
|
|
||||||
namespace NAPI
|
namespace NAPI
|
||||||
{
|
{
|
||||||
|
std::string _getACTUrl(NetworkService service)
|
||||||
|
{
|
||||||
|
switch (service)
|
||||||
|
{
|
||||||
|
case NetworkService::Nintendo:
|
||||||
|
return NintendoURLs::ACTURL;
|
||||||
|
case NetworkService::Pretendo:
|
||||||
|
return PretendoURLs::ACTURL;
|
||||||
|
case NetworkService::Custom:
|
||||||
|
return GetNetworkConfig().urls.ACT.GetValue();
|
||||||
|
default:
|
||||||
|
return NintendoURLs::ACTURL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ACTOauthToken : public _NAPI_CommonResultACT
|
struct ACTOauthToken : public _NAPI_CommonResultACT
|
||||||
{
|
{
|
||||||
std::string token;
|
std::string token;
|
||||||
@ -91,7 +106,7 @@ namespace NAPI
|
|||||||
|
|
||||||
struct OAuthTokenCacheEntry
|
struct OAuthTokenCacheEntry
|
||||||
{
|
{
|
||||||
OAuthTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view token, std::string_view refreshToken, uint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), token(token), refreshToken(refreshToken)
|
OAuthTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view token, std::string_view refreshToken, uint64 expiresIn, NetworkService service) : accountId(accountId), passwordHash(passwordHash), token(token), refreshToken(refreshToken), service(service)
|
||||||
{
|
{
|
||||||
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
|
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
|
||||||
};
|
};
|
||||||
@ -107,10 +122,10 @@ namespace NAPI
|
|||||||
}
|
}
|
||||||
std::string accountId;
|
std::string accountId;
|
||||||
std::array<uint8, 32> passwordHash;
|
std::array<uint8, 32> passwordHash;
|
||||||
|
|
||||||
std::string token;
|
std::string token;
|
||||||
std::string refreshToken;
|
std::string refreshToken;
|
||||||
uint64 expires;
|
uint64 expires;
|
||||||
|
NetworkService service;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<OAuthTokenCacheEntry> g_oauthTokenCache;
|
std::vector<OAuthTokenCacheEntry> g_oauthTokenCache;
|
||||||
@ -122,11 +137,12 @@ namespace NAPI
|
|||||||
ACTOauthToken result{};
|
ACTOauthToken result{};
|
||||||
|
|
||||||
// check cache first
|
// check cache first
|
||||||
|
NetworkService service = authInfo.GetService();
|
||||||
g_oauthTokenCacheMtx.lock();
|
g_oauthTokenCacheMtx.lock();
|
||||||
auto cacheItr = g_oauthTokenCache.begin();
|
auto cacheItr = g_oauthTokenCache.begin();
|
||||||
while (cacheItr != g_oauthTokenCache.end())
|
while (cacheItr != g_oauthTokenCache.end())
|
||||||
{
|
{
|
||||||
if (cacheItr->CheckIfSameAccount(authInfo))
|
if (cacheItr->CheckIfSameAccount(authInfo) && cacheItr->service == service)
|
||||||
{
|
{
|
||||||
if (cacheItr->CheckIfExpired())
|
if (cacheItr->CheckIfExpired())
|
||||||
{
|
{
|
||||||
@ -145,7 +161,7 @@ namespace NAPI
|
|||||||
// token not cached, request from server via oauth2
|
// token not cached, request from server via oauth2
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
|
|
||||||
req.initate(fmt::format("{}/v1/api/oauth20/access_token/generate", LaunchSettings::GetActURLPrefix()), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/oauth20/access_token/generate", _getACTUrl(authInfo.GetService())), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
||||||
_ACTSetCommonHeaderParameters(req, authInfo);
|
_ACTSetCommonHeaderParameters(req, authInfo);
|
||||||
_ACTSetDeviceParameters(req, authInfo);
|
_ACTSetDeviceParameters(req, authInfo);
|
||||||
_ACTSetRegionAndCountryParameters(req, authInfo);
|
_ACTSetRegionAndCountryParameters(req, authInfo);
|
||||||
@ -220,7 +236,7 @@ namespace NAPI
|
|||||||
if (expiration > 0)
|
if (expiration > 0)
|
||||||
{
|
{
|
||||||
g_oauthTokenCacheMtx.lock();
|
g_oauthTokenCacheMtx.lock();
|
||||||
g_oauthTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, result.token, result.refreshToken, expiration);
|
g_oauthTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, result.token, result.refreshToken, expiration, service);
|
||||||
g_oauthTokenCacheMtx.unlock();
|
g_oauthTokenCacheMtx.unlock();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -230,7 +246,7 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
|
|
||||||
req.initate(fmt::format("{}/v1/api/people/@me/profile", LaunchSettings::GetActURLPrefix()), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/people/@me/profile", _getACTUrl(authInfo.GetService())), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
||||||
|
|
||||||
_ACTSetCommonHeaderParameters(req, authInfo);
|
_ACTSetCommonHeaderParameters(req, authInfo);
|
||||||
_ACTSetDeviceParameters(req, authInfo);
|
_ACTSetDeviceParameters(req, authInfo);
|
||||||
@ -238,22 +254,22 @@ namespace NAPI
|
|||||||
// get oauth2 token
|
// get oauth2 token
|
||||||
ACTOauthToken oauthToken = ACT_GetOauthToken_WithCache(authInfo, 0x0005001010001C00, 0x0001C);
|
ACTOauthToken oauthToken = ACT_GetOauthToken_WithCache(authInfo, 0x0005001010001C00, 0x0001C);
|
||||||
|
|
||||||
|
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NexTokenCacheEntry
|
struct NexTokenCacheEntry
|
||||||
{
|
{
|
||||||
NexTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, uint32 gameServerId, ACTNexToken& nexToken) : accountId(accountId), passwordHash(passwordHash), nexToken(nexToken), gameServerId(gameServerId) {};
|
NexTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, NetworkService networkService, uint32 gameServerId, ACTNexToken& nexToken) : accountId(accountId), passwordHash(passwordHash), networkService(networkService), nexToken(nexToken), gameServerId(gameServerId) {};
|
||||||
|
|
||||||
bool IsMatch(const AuthInfo& authInfo, const uint32 gameServerId) const
|
bool IsMatch(const AuthInfo& authInfo, const uint32 gameServerId) const
|
||||||
{
|
{
|
||||||
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && this->gameServerId == gameServerId;
|
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && authInfo.GetService() == networkService && this->gameServerId == gameServerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string accountId;
|
std::string accountId;
|
||||||
std::array<uint8, 32> passwordHash;
|
std::array<uint8, 32> passwordHash;
|
||||||
|
NetworkService networkService;
|
||||||
uint32 gameServerId;
|
uint32 gameServerId;
|
||||||
|
|
||||||
ACTNexToken nexToken;
|
ACTNexToken nexToken;
|
||||||
@ -297,7 +313,7 @@ namespace NAPI
|
|||||||
}
|
}
|
||||||
// do request
|
// do request
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/v1/api/provider/nex_token/@me?game_server_id={:08X}", LaunchSettings::GetActURLPrefix(), serverId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/provider/nex_token/@me?game_server_id={:08X}", _getACTUrl(authInfo.GetService()), serverId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
||||||
_ACTSetCommonHeaderParameters(req, authInfo);
|
_ACTSetCommonHeaderParameters(req, authInfo);
|
||||||
_ACTSetDeviceParameters(req, authInfo);
|
_ACTSetDeviceParameters(req, authInfo);
|
||||||
_ACTSetRegionAndCountryParameters(req, authInfo);
|
_ACTSetRegionAndCountryParameters(req, authInfo);
|
||||||
@ -374,21 +390,21 @@ namespace NAPI
|
|||||||
result.nexToken.port = (uint16)StringHelpers::ToInt(port);
|
result.nexToken.port = (uint16)StringHelpers::ToInt(port);
|
||||||
result.apiError = NAPI_RESULT::SUCCESS;
|
result.apiError = NAPI_RESULT::SUCCESS;
|
||||||
g_nexTokenCacheMtx.lock();
|
g_nexTokenCacheMtx.lock();
|
||||||
g_nexTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, serverId, result.nexToken);
|
g_nexTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, authInfo.GetService(), serverId, result.nexToken);
|
||||||
g_nexTokenCacheMtx.unlock();
|
g_nexTokenCacheMtx.unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IndependentTokenCacheEntry
|
struct IndependentTokenCacheEntry
|
||||||
{
|
{
|
||||||
IndependentTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view clientId, std::string_view independentToken, sint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), clientId(clientId), independentToken(independentToken)
|
IndependentTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, NetworkService networkService, std::string_view clientId, std::string_view independentToken, sint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), networkService(networkService), clientId(clientId), independentToken(independentToken)
|
||||||
{
|
{
|
||||||
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
|
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsMatch(const AuthInfo& authInfo, const std::string_view clientId) const
|
bool IsMatch(const AuthInfo& authInfo, const std::string_view clientId) const
|
||||||
{
|
{
|
||||||
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && this->clientId == clientId;
|
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && authInfo.GetService() == networkService && this->clientId == clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckIfExpired() const
|
bool CheckIfExpired() const
|
||||||
@ -398,6 +414,7 @@ namespace NAPI
|
|||||||
|
|
||||||
std::string accountId;
|
std::string accountId;
|
||||||
std::array<uint8, 32> passwordHash;
|
std::array<uint8, 32> passwordHash;
|
||||||
|
NetworkService networkService;
|
||||||
std::string clientId;
|
std::string clientId;
|
||||||
sint64 expires;
|
sint64 expires;
|
||||||
|
|
||||||
@ -449,7 +466,7 @@ namespace NAPI
|
|||||||
}
|
}
|
||||||
// do request
|
// do request
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/v1/api/provider/service_token/@me?client_id={}", LaunchSettings::GetActURLPrefix(), clientId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/provider/service_token/@me?client_id={}", _getACTUrl(authInfo.GetService()), clientId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
||||||
_ACTSetCommonHeaderParameters(req, authInfo);
|
_ACTSetCommonHeaderParameters(req, authInfo);
|
||||||
_ACTSetDeviceParameters(req, authInfo);
|
_ACTSetDeviceParameters(req, authInfo);
|
||||||
_ACTSetRegionAndCountryParameters(req, authInfo);
|
_ACTSetRegionAndCountryParameters(req, authInfo);
|
||||||
@ -494,7 +511,7 @@ namespace NAPI
|
|||||||
result.apiError = NAPI_RESULT::SUCCESS;
|
result.apiError = NAPI_RESULT::SUCCESS;
|
||||||
|
|
||||||
g_IndependentTokenCacheMtx.lock();
|
g_IndependentTokenCacheMtx.lock();
|
||||||
g_IndependentTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, clientId, result.token, 3600);
|
g_IndependentTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, authInfo.GetService(), clientId, result.token, 3600);
|
||||||
g_IndependentTokenCacheMtx.unlock();
|
g_IndependentTokenCacheMtx.unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -520,7 +537,7 @@ namespace NAPI
|
|||||||
}
|
}
|
||||||
// do request
|
// do request
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/v1/api/admin/mapped_ids?input_type=user_id&output_type=pid&input={}", LaunchSettings::GetActURLPrefix(), nnid), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/admin/mapped_ids?input_type=user_id&output_type=pid&input={}", _getACTUrl(authInfo.GetService()), nnid), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
|
||||||
_ACTSetCommonHeaderParameters(req, authInfo);
|
_ACTSetCommonHeaderParameters(req, authInfo);
|
||||||
_ACTSetDeviceParameters(req, authInfo);
|
_ACTSetDeviceParameters(req, authInfo);
|
||||||
_ACTSetRegionAndCountryParameters(req, authInfo);
|
_ACTSetRegionAndCountryParameters(req, authInfo);
|
||||||
|
@ -16,102 +16,111 @@
|
|||||||
namespace NAPI
|
namespace NAPI
|
||||||
{
|
{
|
||||||
/* Service URL manager */
|
/* Service URL manager */
|
||||||
|
struct CachedServiceUrls
|
||||||
|
{
|
||||||
std::string s_serviceURL_ContentPrefixURL;
|
std::string s_serviceURL_ContentPrefixURL;
|
||||||
std::string s_serviceURL_UncachedContentPrefixURL;
|
std::string s_serviceURL_UncachedContentPrefixURL;
|
||||||
std::string s_serviceURL_EcsURL;
|
std::string s_serviceURL_EcsURL;
|
||||||
std::string s_serviceURL_IasURL;
|
std::string s_serviceURL_IasURL;
|
||||||
std::string s_serviceURL_CasURL;
|
std::string s_serviceURL_CasURL;
|
||||||
std::string s_serviceURL_NusURL;
|
std::string s_serviceURL_NusURL;
|
||||||
|
};
|
||||||
|
|
||||||
std::string _getNUSUrl()
|
std::unordered_map<NetworkService, CachedServiceUrls> s_cachedServiceUrlsMap;
|
||||||
|
|
||||||
|
CachedServiceUrls& GetCachedServiceUrls(NetworkService service)
|
||||||
{
|
{
|
||||||
if (!s_serviceURL_NusURL.empty())
|
return s_cachedServiceUrlsMap[service];
|
||||||
return s_serviceURL_NusURL;
|
}
|
||||||
switch (ActiveSettings::GetNetworkService())
|
|
||||||
|
std::string _getNUSUrl(NetworkService service)
|
||||||
|
{
|
||||||
|
auto& cachedServiceUrls = GetCachedServiceUrls(service);
|
||||||
|
if (!cachedServiceUrls.s_serviceURL_NusURL.empty())
|
||||||
|
return cachedServiceUrls.s_serviceURL_NusURL;
|
||||||
|
switch (service)
|
||||||
{
|
{
|
||||||
case NetworkService::Nintendo:
|
case NetworkService::Nintendo:
|
||||||
return NintendoURLs::NUSURL;
|
return NintendoURLs::NUSURL;
|
||||||
break;
|
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
return PretendoURLs::NUSURL;
|
return PretendoURLs::NUSURL;
|
||||||
break;
|
|
||||||
case NetworkService::Custom:
|
case NetworkService::Custom:
|
||||||
return GetNetworkConfig().urls.NUS;
|
return GetNetworkConfig().urls.NUS;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return NintendoURLs::NUSURL;
|
return NintendoURLs::NUSURL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string _getIASUrl()
|
std::string _getIASUrl(NetworkService service)
|
||||||
{
|
{
|
||||||
if (!s_serviceURL_IasURL.empty())
|
auto& cachedServiceUrls = GetCachedServiceUrls(service);
|
||||||
return s_serviceURL_IasURL;
|
if (!cachedServiceUrls.s_serviceURL_IasURL.empty())
|
||||||
switch (ActiveSettings::GetNetworkService())
|
return cachedServiceUrls.s_serviceURL_IasURL;
|
||||||
|
switch (service)
|
||||||
{
|
{
|
||||||
case NetworkService::Nintendo:
|
case NetworkService::Nintendo:
|
||||||
return NintendoURLs::IASURL;
|
return NintendoURLs::IASURL;
|
||||||
break;
|
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
return PretendoURLs::IASURL;
|
return PretendoURLs::IASURL;
|
||||||
break;
|
|
||||||
case NetworkService::Custom:
|
case NetworkService::Custom:
|
||||||
return GetNetworkConfig().urls.IAS;
|
return GetNetworkConfig().urls.IAS;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return NintendoURLs::IASURL;
|
return NintendoURLs::IASURL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string _getECSUrl()
|
std::string _getECSUrl(NetworkService service)
|
||||||
{
|
{
|
||||||
// this is the first url queried (GetAccountStatus). The others are dynamically set if provided by the server but will fallback to hardcoded defaults otherwise
|
// this is the first url queried (GetAccountStatus). The others are dynamically set if provided by the server but will fallback to hardcoded defaults otherwise
|
||||||
if (!s_serviceURL_EcsURL.empty())
|
auto& cachedServiceUrls = GetCachedServiceUrls(service);
|
||||||
return s_serviceURL_EcsURL;
|
if (!cachedServiceUrls.s_serviceURL_EcsURL.empty())
|
||||||
return LaunchSettings::GetServiceURL_ecs(); // by default this is "https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP"
|
return cachedServiceUrls.s_serviceURL_EcsURL;
|
||||||
|
switch (service)
|
||||||
|
{
|
||||||
|
case NetworkService::Nintendo:
|
||||||
|
return NintendoURLs::ECSURL;
|
||||||
|
case NetworkService::Pretendo:
|
||||||
|
return PretendoURLs::ECSURL;
|
||||||
|
case NetworkService::Custom:
|
||||||
|
return GetNetworkConfig().urls.ECS;
|
||||||
|
default:
|
||||||
|
return NintendoURLs::ECSURL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string _getCCSUncachedUrl() // used for TMD requests
|
std::string _getCCSUncachedUrl(NetworkService service) // used for TMD requests
|
||||||
{
|
{
|
||||||
if (!s_serviceURL_UncachedContentPrefixURL.empty())
|
auto& cachedServiceUrls = GetCachedServiceUrls(service);
|
||||||
return s_serviceURL_UncachedContentPrefixURL;
|
if (!cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL.empty())
|
||||||
switch (ActiveSettings::GetNetworkService())
|
return cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL;
|
||||||
|
switch (service)
|
||||||
{
|
{
|
||||||
case NetworkService::Nintendo:
|
case NetworkService::Nintendo:
|
||||||
return NintendoURLs::CCSUURL;
|
return NintendoURLs::CCSUURL;
|
||||||
break;
|
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
return PretendoURLs::CCSUURL;
|
return PretendoURLs::CCSUURL;
|
||||||
break;
|
|
||||||
case NetworkService::Custom:
|
case NetworkService::Custom:
|
||||||
return GetNetworkConfig().urls.CCSU;
|
return GetNetworkConfig().urls.CCSU;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return NintendoURLs::CCSUURL;
|
return NintendoURLs::CCSUURL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string _getCCSUrl() // used for game data downloads
|
std::string _getCCSUrl(NetworkService service) // used for game data downloads
|
||||||
{
|
{
|
||||||
if (!s_serviceURL_ContentPrefixURL.empty())
|
auto& cachedServiceUrls = GetCachedServiceUrls(service);
|
||||||
return s_serviceURL_ContentPrefixURL;
|
if (!cachedServiceUrls.s_serviceURL_ContentPrefixURL.empty())
|
||||||
switch (ActiveSettings::GetNetworkService())
|
return cachedServiceUrls.s_serviceURL_ContentPrefixURL;
|
||||||
|
switch (service)
|
||||||
{
|
{
|
||||||
case NetworkService::Nintendo:
|
case NetworkService::Nintendo:
|
||||||
return NintendoURLs::CCSURL;
|
return NintendoURLs::CCSURL;
|
||||||
break;
|
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
return PretendoURLs::CCSURL;
|
return PretendoURLs::CCSURL;
|
||||||
break;
|
|
||||||
case NetworkService::Custom:
|
case NetworkService::Custom:
|
||||||
return GetNetworkConfig().urls.CCS;
|
return GetNetworkConfig().urls.CCS;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return NintendoURLs::CCSURL;
|
return NintendoURLs::CCSURL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,8 +131,8 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_NUSGetSystemCommonETicket_Result result{};
|
NAPI_NUSGetSystemCommonETicket_Result result{};
|
||||||
|
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("nus", _getNUSUrl(), "GetSystemCommonETicket", "1.0");
|
soapHelper.SOAP_initate("nus", _getNUSUrl(authInfo.GetService()), "GetSystemCommonETicket", "1.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
||||||
soapHelper.SOAP_addRequestField("RegionId", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("RegionId", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -175,8 +184,8 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_IASGetChallenge_Result result{};
|
NAPI_IASGetChallenge_Result result{};
|
||||||
|
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("ias", _getIASUrl(), "GetChallenge", "2.0");
|
soapHelper.SOAP_initate("ias", _getIASUrl(authInfo.GetService()), "GetChallenge", "2.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // not validated but the generated Challenge is bound to this DeviceId
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // not validated but the generated Challenge is bound to this DeviceId
|
||||||
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -200,8 +209,8 @@ namespace NAPI
|
|||||||
NAPI_IASGetRegistrationInfo_Result IAS_GetRegistrationInfo_QueryInfo(AuthInfo& authInfo, std::string challenge)
|
NAPI_IASGetRegistrationInfo_Result IAS_GetRegistrationInfo_QueryInfo(AuthInfo& authInfo, std::string challenge)
|
||||||
{
|
{
|
||||||
NAPI_IASGetRegistrationInfo_Result result;
|
NAPI_IASGetRegistrationInfo_Result result;
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("ias", _getIASUrl(), "GetRegistrationInfo", "2.0");
|
soapHelper.SOAP_initate("ias", _getIASUrl(authInfo.GetService()), "GetRegistrationInfo", "2.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // this must match the DeviceId used to generate Challenge
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // this must match the DeviceId used to generate Challenge
|
||||||
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -301,8 +310,8 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_ECSGetAccountStatus_Result result{};
|
NAPI_ECSGetAccountStatus_Result result{};
|
||||||
|
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("ecs", _getECSUrl(), "GetAccountStatus", "2.0");
|
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "GetAccountStatus", "2.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
||||||
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -367,19 +376,19 @@ namespace NAPI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assign service URLs
|
// assign service URLs
|
||||||
|
auto& cachedServiceUrls = GetCachedServiceUrls(authInfo.GetService());
|
||||||
if (!result.serviceURLs.ContentPrefixURL.empty())
|
if (!result.serviceURLs.ContentPrefixURL.empty())
|
||||||
s_serviceURL_ContentPrefixURL = result.serviceURLs.ContentPrefixURL;
|
cachedServiceUrls.s_serviceURL_ContentPrefixURL = result.serviceURLs.ContentPrefixURL;
|
||||||
if (!result.serviceURLs.UncachedContentPrefixURL.empty())
|
if (!result.serviceURLs.UncachedContentPrefixURL.empty())
|
||||||
s_serviceURL_UncachedContentPrefixURL = result.serviceURLs.UncachedContentPrefixURL;
|
cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL = result.serviceURLs.UncachedContentPrefixURL;
|
||||||
if (!result.serviceURLs.IasURL.empty())
|
if (!result.serviceURLs.IasURL.empty())
|
||||||
s_serviceURL_IasURL = result.serviceURLs.IasURL;
|
cachedServiceUrls.s_serviceURL_IasURL = result.serviceURLs.IasURL;
|
||||||
if (!result.serviceURLs.CasURL.empty())
|
if (!result.serviceURLs.CasURL.empty())
|
||||||
s_serviceURL_CasURL = result.serviceURLs.CasURL;
|
cachedServiceUrls.s_serviceURL_CasURL = result.serviceURLs.CasURL;
|
||||||
if (!result.serviceURLs.NusURL.empty())
|
if (!result.serviceURLs.NusURL.empty())
|
||||||
s_serviceURL_NusURL = result.serviceURLs.NusURL;
|
cachedServiceUrls.s_serviceURL_NusURL = result.serviceURLs.NusURL;
|
||||||
if (!result.serviceURLs.EcsURL.empty())
|
if (!result.serviceURLs.EcsURL.empty())
|
||||||
s_serviceURL_EcsURL = result.serviceURLs.EcsURL;
|
cachedServiceUrls.s_serviceURL_EcsURL = result.serviceURLs.EcsURL;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,8 +396,8 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_ECSAccountListETicketIds_Result result{};
|
NAPI_ECSAccountListETicketIds_Result result{};
|
||||||
|
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("ecs", _getECSUrl(), "AccountListETicketIds", "2.0");
|
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "AccountListETicketIds", "2.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
||||||
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -446,8 +455,8 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_ECSAccountGetETickets_Result result{};
|
NAPI_ECSAccountGetETickets_Result result{};
|
||||||
|
|
||||||
CurlSOAPHelper soapHelper;
|
CurlSOAPHelper soapHelper(authInfo.GetService());
|
||||||
soapHelper.SOAP_initate("ecs", _getECSUrl(), "AccountGetETickets", "2.0");
|
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "AccountGetETickets", "2.0");
|
||||||
|
|
||||||
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
|
||||||
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
|
||||||
@ -512,7 +521,7 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_CCSGetTMD_Result result{};
|
NAPI_CCSGetTMD_Result result{};
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/{:016x}/tmd.{}?deviceId={}&accountId={}", _getCCSUncachedUrl(), titleId, titleVersion, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
req.initate(authInfo.GetService(), fmt::format("{}/{:016x}/tmd.{}?deviceId={}&accountId={}", _getCCSUncachedUrl(authInfo.GetService()), titleId, titleVersion, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
||||||
req.setTimeout(180);
|
req.setTimeout(180);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
@ -528,7 +537,7 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_CCSGetTMD_Result result{};
|
NAPI_CCSGetTMD_Result result{};
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/{:016x}/tmd?deviceId={}&accountId={}", _getCCSUncachedUrl(), titleId, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
req.initate(authInfo.GetService(), fmt::format("{}/{:016x}/tmd?deviceId={}&accountId={}", _getCCSUncachedUrl(authInfo.GetService()), titleId, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
||||||
req.setTimeout(180);
|
req.setTimeout(180);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
@ -540,11 +549,11 @@ namespace NAPI
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
NAPI_CCSGetETicket_Result CCS_GetCETK(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion)
|
NAPI_CCSGetETicket_Result CCS_GetCETK(NetworkService service, uint64 titleId, uint16 titleVersion)
|
||||||
{
|
{
|
||||||
NAPI_CCSGetETicket_Result result{};
|
NAPI_CCSGetETicket_Result result{};
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/{:016x}/cetk", _getCCSUncachedUrl(), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
req.initate(service, fmt::format("{}/{:016x}/cetk", _getCCSUncachedUrl(service), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
||||||
req.setTimeout(180);
|
req.setTimeout(180);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
@ -556,10 +565,10 @@ namespace NAPI
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCS_GetContentFile(uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData)
|
bool CCS_GetContentFile(NetworkService service, uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData)
|
||||||
{
|
{
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/{:016x}/{:08x}", _getCCSUrl(), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
req.initate(service, fmt::format("{}/{:016x}/{:08x}", _getCCSUrl(service), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
||||||
req.setWriteCallback(cbWriteCallback, userData);
|
req.setWriteCallback(cbWriteCallback, userData);
|
||||||
req.setTimeout(0);
|
req.setTimeout(0);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
@ -570,11 +579,11 @@ namespace NAPI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(uint64 titleId, uint32 contentId)
|
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(NetworkService service, uint64 titleId, uint32 contentId)
|
||||||
{
|
{
|
||||||
NAPI_CCSGetContentH3_Result result{};
|
NAPI_CCSGetContentH3_Result result{};
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("{}/{:016x}/{:08x}.h3", _getCCSUrl(), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
req.initate(service, fmt::format("{}/{:016x}/{:08x}.h3", _getCCSUrl(service), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, fmt::format("Failed to request content hash file {:08x}.h3 for title {:016X}", contentId, titleId));
|
cemuLog_log(LogType::Force, fmt::format("Failed to request content hash file {:08x}.h3 for title {:016X}", contentId, titleId));
|
||||||
|
@ -119,7 +119,7 @@ CurlRequestHelper::~CurlRequestHelper()
|
|||||||
curl_easy_cleanup(m_curl);
|
curl_easy_cleanup(m_curl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext)
|
void CurlRequestHelper::initate(NetworkService service, std::string url, SERVER_SSL_CONTEXT sslContext)
|
||||||
{
|
{
|
||||||
// reset parameters
|
// reset parameters
|
||||||
m_headerExtraFields.clear();
|
m_headerExtraFields.clear();
|
||||||
@ -131,7 +131,9 @@ void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext)
|
|||||||
curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 60);
|
curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 60);
|
||||||
|
|
||||||
// SSL
|
// SSL
|
||||||
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove once Pretendo has SSL
|
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||||
|
if (IsNetworkServiceSSLDisabled(service))
|
||||||
|
{
|
||||||
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
}
|
}
|
||||||
else if (sslContext == SERVER_SSL_CONTEXT::ACT || sslContext == SERVER_SSL_CONTEXT::TAGAYA)
|
else if (sslContext == SERVER_SSL_CONTEXT::ACT || sslContext == SERVER_SSL_CONTEXT::TAGAYA)
|
||||||
@ -256,16 +258,22 @@ bool CurlRequestHelper::submitRequest(bool isPost)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurlSOAPHelper::CurlSOAPHelper()
|
CurlSOAPHelper::CurlSOAPHelper(NetworkService service)
|
||||||
{
|
{
|
||||||
m_curl = curl_easy_init();
|
m_curl = curl_easy_init();
|
||||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, __curlWriteCallback);
|
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, __curlWriteCallback);
|
||||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, this);
|
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, this);
|
||||||
|
|
||||||
// SSL
|
// SSL
|
||||||
if (!GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() != NetworkService::Pretendo && ActiveSettings::GetNetworkService() != NetworkService::Custom) { //Remove once Pretendo has SSL
|
if (!IsNetworkServiceSSLDisabled(service))
|
||||||
|
{
|
||||||
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP);
|
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP);
|
||||||
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
|
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
|
||||||
|
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||||
}
|
}
|
||||||
if (GetConfig().proxy_server.GetValue() != "")
|
if (GetConfig().proxy_server.GetValue() != "")
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ public:
|
|||||||
return m_curl;
|
return m_curl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initate(std::string url, SERVER_SSL_CONTEXT sslContext);
|
void initate(NetworkService service, std::string url, SERVER_SSL_CONTEXT sslContext);
|
||||||
void addHeaderField(const char* fieldName, std::string_view value);
|
void addHeaderField(const char* fieldName, std::string_view value);
|
||||||
void addPostField(const char* fieldName, std::string_view value);
|
void addPostField(const char* fieldName, std::string_view value);
|
||||||
void setWriteCallback(bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
|
void setWriteCallback(bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
|
||||||
@ -74,7 +74,7 @@ private:
|
|||||||
class CurlSOAPHelper // todo - make this use CurlRequestHelper
|
class CurlSOAPHelper // todo - make this use CurlRequestHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CurlSOAPHelper();
|
CurlSOAPHelper(NetworkService service);
|
||||||
~CurlSOAPHelper();
|
~CurlSOAPHelper();
|
||||||
|
|
||||||
CURL* getCURL()
|
CURL* getCURL()
|
||||||
|
@ -54,11 +54,11 @@ namespace NAPI
|
|||||||
AES128_CBC_decrypt((uint8*)iconData, (uint8*)iconData, sizeof(IDBEIconDataV0), aesKey, iv);
|
AES128_CBC_decrypt((uint8*)iconData, (uint8*)iconData, sizeof(IDBEIconDataV0), aesKey, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8> IDBE_RequestRawEncrypted(uint64 titleId)
|
std::vector<uint8> IDBE_RequestRawEncrypted(NetworkService networkService, uint64 titleId)
|
||||||
{
|
{
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
std::string requestUrl;
|
std::string requestUrl;
|
||||||
switch (ActiveSettings::GetNetworkService())
|
switch (networkService)
|
||||||
{
|
{
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
requestUrl = PretendoURLs::IDBEURL;
|
requestUrl = PretendoURLs::IDBEURL;
|
||||||
@ -72,7 +72,7 @@ namespace NAPI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
requestUrl.append(fmt::format(fmt::runtime("/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId));
|
requestUrl.append(fmt::format(fmt::runtime("/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId));
|
||||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
req.initate(networkService, requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
|
||||||
|
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
@ -90,7 +90,7 @@ namespace NAPI
|
|||||||
return receivedData;
|
return receivedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IDBEIconDataV0> IDBE_Request(uint64 titleId)
|
std::optional<IDBEIconDataV0> IDBE_Request(NetworkService networkService, uint64 titleId)
|
||||||
{
|
{
|
||||||
if (titleId == 0x000500301001500A ||
|
if (titleId == 0x000500301001500A ||
|
||||||
titleId == 0x000500301001510A ||
|
titleId == 0x000500301001510A ||
|
||||||
@ -101,7 +101,7 @@ namespace NAPI
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8> idbeData = IDBE_RequestRawEncrypted(titleId);
|
std::vector<uint8> idbeData = IDBE_RequestRawEncrypted(networkService, titleId);
|
||||||
if (idbeData.size() < 0x22)
|
if (idbeData.size() < 0x22)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
if (idbeData[0] != 0)
|
if (idbeData[0] != 0)
|
||||||
|
@ -18,7 +18,7 @@ namespace NAPI
|
|||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
|
|
||||||
std::string requestUrl;
|
std::string requestUrl;
|
||||||
switch (ActiveSettings::GetNetworkService())
|
switch (authInfo.GetService())
|
||||||
{
|
{
|
||||||
case NetworkService::Pretendo:
|
case NetworkService::Pretendo:
|
||||||
requestUrl = PretendoURLs::TAGAYAURL;
|
requestUrl = PretendoURLs::TAGAYAURL;
|
||||||
@ -32,7 +32,7 @@ namespace NAPI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
requestUrl.append(fmt::format(fmt::runtime("/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country));
|
requestUrl.append(fmt::format(fmt::runtime("/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country));
|
||||||
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
req.initate(authInfo.GetService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||||
|
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
@ -63,7 +63,7 @@ namespace NAPI
|
|||||||
{
|
{
|
||||||
NAPI_VersionList_Result result;
|
NAPI_VersionList_Result result;
|
||||||
CurlRequestHelper req;
|
CurlRequestHelper req;
|
||||||
req.initate(fmt::format("https://{}/tagaya/versionlist/{}/{}/list/{}.versionlist", fqdnURL, NCrypto::GetRegionAsString(authInfo.region), authInfo.country, versionListVersion), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
req.initate(authInfo.GetService(), fmt::format("https://{}/tagaya/versionlist/{}/{}/list/{}.versionlist", fqdnURL, NCrypto::GetRegionAsString(authInfo.region), authInfo.country, versionListVersion), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
|
||||||
if (!req.submitRequest(false))
|
if (!req.submitRequest(false))
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, fmt::format("Failed to request update list"));
|
cemuLog_log(LogType::Force, fmt::format("Failed to request update list"));
|
||||||
|
@ -20,7 +20,8 @@ void iosuCrypto_readOtpData(void* output, sint32 wordIndex, sint32 size);
|
|||||||
|
|
||||||
void iosuCrypto_readSeepromData(void* output, sint32 wordIndex, sint32 size);
|
void iosuCrypto_readSeepromData(void* output, sint32 wordIndex, sint32 size);
|
||||||
|
|
||||||
extern bool hasSeepromMem; // remove later
|
extern bool hasSeepromMem; // remove later (migrate otp/seeprom loading & parsing to this class)
|
||||||
|
extern bool hasOtpMem; // remove later
|
||||||
|
|
||||||
namespace NCrypto
|
namespace NCrypto
|
||||||
{
|
{
|
||||||
@ -792,6 +793,16 @@ namespace NCrypto
|
|||||||
return (CafeConsoleRegion)seepromRegionU32[3];
|
return (CafeConsoleRegion)seepromRegionU32[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OTP_IsPresent()
|
||||||
|
{
|
||||||
|
return hasOtpMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasDataForConsoleCert()
|
||||||
|
{
|
||||||
|
return SEEPROM_IsPresent() && OTP_IsPresent();
|
||||||
|
}
|
||||||
|
|
||||||
std::string GetRegionAsString(CafeConsoleRegion regionCode)
|
std::string GetRegionAsString(CafeConsoleRegion regionCode)
|
||||||
{
|
{
|
||||||
if (regionCode == CafeConsoleRegion::EUR)
|
if (regionCode == CafeConsoleRegion::EUR)
|
||||||
@ -957,6 +968,11 @@ namespace NCrypto
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GetCountryCount()
|
||||||
|
{
|
||||||
|
return g_countryTable.size();
|
||||||
|
}
|
||||||
|
|
||||||
void unitTests()
|
void unitTests()
|
||||||
{
|
{
|
||||||
base64Tests();
|
base64Tests();
|
||||||
|
@ -205,7 +205,11 @@ namespace NCrypto
|
|||||||
CafeConsoleRegion SEEPROM_GetRegion();
|
CafeConsoleRegion SEEPROM_GetRegion();
|
||||||
std::string GetRegionAsString(CafeConsoleRegion regionCode);
|
std::string GetRegionAsString(CafeConsoleRegion regionCode);
|
||||||
const char* GetCountryAsString(sint32 index); // returns NN if index is not valid or known
|
const char* GetCountryAsString(sint32 index); // returns NN if index is not valid or known
|
||||||
|
size_t GetCountryCount();
|
||||||
bool SEEPROM_IsPresent();
|
bool SEEPROM_IsPresent();
|
||||||
|
bool OTP_IsPresent();
|
||||||
|
|
||||||
|
bool HasDataForConsoleCert();
|
||||||
|
|
||||||
void unitTests();
|
void unitTests();
|
||||||
}
|
}
|
@ -39,7 +39,6 @@ ActiveSettings::LoadOnce(
|
|||||||
|
|
||||||
g_config.SetFilename(GetConfigPath("settings.xml").generic_wstring());
|
g_config.SetFilename(GetConfigPath("settings.xml").generic_wstring());
|
||||||
g_config.Load();
|
g_config.Load();
|
||||||
LaunchSettings::ChangeNetworkServiceURL(GetConfig().account.active_service);
|
|
||||||
std::string additionalErrorInfo;
|
std::string additionalErrorInfo;
|
||||||
s_has_required_online_files = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo) == IOS_CRYPTO_ONLINE_REQ_OK;
|
s_has_required_online_files = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo) == IOS_CRYPTO_ONLINE_REQ_OK;
|
||||||
return failed_write_access;
|
return failed_write_access;
|
||||||
|
@ -69,11 +69,7 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||||||
("account,a", po::value<std::string>(), "Persistent id of account")
|
("account,a", po::value<std::string>(), "Persistent id of account")
|
||||||
|
|
||||||
("force-interpreter", po::value<bool>()->implicit_value(true), "Force interpreter CPU emulation, disables recompiler")
|
("force-interpreter", po::value<bool>()->implicit_value(true), "Force interpreter CPU emulation, disables recompiler")
|
||||||
("enable-gdbstub", po::value<bool>()->implicit_value(true), "Enable GDB stub to debug executables inside Cemu using an external debugger")
|
("enable-gdbstub", po::value<bool>()->implicit_value(true), "Enable GDB stub to debug executables inside Cemu using an external debugger");
|
||||||
|
|
||||||
("act-url", po::value<std::string>(), "URL prefix for account server")
|
|
||||||
("ecs-url", po::value<std::string>(), "URL for ECS service");
|
|
||||||
|
|
||||||
|
|
||||||
po::options_description hidden{ "Hidden options" };
|
po::options_description hidden{ "Hidden options" };
|
||||||
hidden.add_options()
|
hidden.add_options()
|
||||||
@ -190,16 +186,6 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||||||
if (vm.count("output"))
|
if (vm.count("output"))
|
||||||
log_path = vm["output"].as<std::wstring>();
|
log_path = vm["output"].as<std::wstring>();
|
||||||
|
|
||||||
// urls
|
|
||||||
if (vm.count("act-url"))
|
|
||||||
{
|
|
||||||
serviceURL_ACT = vm["act-url"].as<std::string>();
|
|
||||||
if (serviceURL_ACT.size() > 0 && serviceURL_ACT.back() == '/')
|
|
||||||
serviceURL_ACT.pop_back();
|
|
||||||
}
|
|
||||||
if (vm.count("ecs-url"))
|
|
||||||
serviceURL_ECS = vm["ecs-url"].as<std::string>();
|
|
||||||
|
|
||||||
if(!extract_path.empty())
|
if(!extract_path.empty())
|
||||||
{
|
{
|
||||||
ExtractorTool(extract_path, output_path, log_path);
|
ExtractorTool(extract_path, output_path, log_path);
|
||||||
@ -280,24 +266,3 @@ bool LaunchSettings::ExtractorTool(std::wstring_view wud_path, std::string_view
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LaunchSettings::ChangeNetworkServiceURL(int ID){
|
|
||||||
NetworkService Network = static_cast<NetworkService>(ID);
|
|
||||||
switch (Network)
|
|
||||||
{
|
|
||||||
case NetworkService::Pretendo:
|
|
||||||
serviceURL_ACT = PretendoURLs::ACTURL;
|
|
||||||
serviceURL_ECS = PretendoURLs::ECSURL;
|
|
||||||
break;
|
|
||||||
case NetworkService::Custom:
|
|
||||||
serviceURL_ACT = GetNetworkConfig().urls.ACT.GetValue();
|
|
||||||
serviceURL_ECS = GetNetworkConfig().urls.ECS.GetValue();
|
|
||||||
break;
|
|
||||||
case NetworkService::Nintendo:
|
|
||||||
default:
|
|
||||||
serviceURL_ACT = NintendoURLs::ACTURL;
|
|
||||||
serviceURL_ECS = NintendoURLs::ECSURL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -29,10 +29,6 @@ public:
|
|||||||
|
|
||||||
static std::optional<uint32> GetPersistentId() { return s_persistent_id; }
|
static std::optional<uint32> GetPersistentId() { return s_persistent_id; }
|
||||||
|
|
||||||
static std::string GetActURLPrefix() { return serviceURL_ACT; }
|
|
||||||
static std::string GetServiceURL_ecs() { return serviceURL_ECS; }
|
|
||||||
static void ChangeNetworkServiceURL(int ID);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline static std::optional<fs::path> s_load_game_file{};
|
inline static std::optional<fs::path> s_load_game_file{};
|
||||||
inline static std::optional<uint64> s_load_title_id{};
|
inline static std::optional<uint64> s_load_title_id{};
|
||||||
@ -48,12 +44,6 @@ private:
|
|||||||
|
|
||||||
inline static std::optional<uint32> s_persistent_id{};
|
inline static std::optional<uint32> s_persistent_id{};
|
||||||
|
|
||||||
// service URLS
|
|
||||||
inline static std::string serviceURL_ACT;
|
|
||||||
inline static std::string serviceURL_ECS;
|
|
||||||
// todo - npts and other boss urls
|
|
||||||
|
|
||||||
|
|
||||||
static bool ExtractorTool(std::wstring_view wud_path, std::string_view output_path, std::wstring_view log_path);
|
static bool ExtractorTool(std::wstring_view wud_path, std::string_view output_path, std::wstring_view log_path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ void NetworkConfig::Load(XMLConfigParser& parser)
|
|||||||
urls.BOSS = u.get("boss", NintendoURLs::BOSSURL);
|
urls.BOSS = u.get("boss", NintendoURLs::BOSSURL);
|
||||||
urls.TAGAYA = u.get("tagaya", NintendoURLs::TAGAYAURL);
|
urls.TAGAYA = u.get("tagaya", NintendoURLs::TAGAYAURL);
|
||||||
urls.OLV = u.get("olv", NintendoURLs::OLVURL);
|
urls.OLV = u.get("olv", NintendoURLs::OLVURL);
|
||||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
|
||||||
LaunchSettings::ChangeNetworkServiceURL(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkConfig::XMLExists()
|
bool NetworkConfig::XMLExists()
|
||||||
@ -41,7 +39,6 @@ bool NetworkConfig::XMLExists()
|
|||||||
{
|
{
|
||||||
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
|
||||||
{
|
{
|
||||||
LaunchSettings::ChangeNetworkServiceURL(0);
|
|
||||||
GetConfig().account.active_service = 0;
|
GetConfig().account.active_service = 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -3,13 +3,15 @@
|
|||||||
#include "ConfigValue.h"
|
#include "ConfigValue.h"
|
||||||
#include "XMLConfig.h"
|
#include "XMLConfig.h"
|
||||||
|
|
||||||
|
enum class NetworkService
|
||||||
enum class NetworkService {
|
{
|
||||||
Nintendo,
|
Nintendo,
|
||||||
Pretendo,
|
Pretendo,
|
||||||
Custom,
|
Custom
|
||||||
};
|
};
|
||||||
struct NetworkConfig {
|
|
||||||
|
struct NetworkConfig
|
||||||
|
{
|
||||||
NetworkConfig()
|
NetworkConfig()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -70,3 +72,14 @@ struct PretendoURLs {
|
|||||||
typedef XMLDataConfig<NetworkConfig, &NetworkConfig::Load, &NetworkConfig::Save> XMLNetworkConfig_t;
|
typedef XMLDataConfig<NetworkConfig, &NetworkConfig::Load, &NetworkConfig::Save> XMLNetworkConfig_t;
|
||||||
extern XMLNetworkConfig_t n_config;
|
extern XMLNetworkConfig_t n_config;
|
||||||
inline NetworkConfig& GetNetworkConfig() { return n_config.data();};
|
inline NetworkConfig& GetNetworkConfig() { return n_config.data();};
|
||||||
|
|
||||||
|
inline bool IsNetworkServiceSSLDisabled(NetworkService service)
|
||||||
|
{
|
||||||
|
if(service == NetworkService::Nintendo)
|
||||||
|
return false;
|
||||||
|
else if(service == NetworkService::Pretendo)
|
||||||
|
return true;
|
||||||
|
else if(service == NetworkService::Custom)
|
||||||
|
return GetNetworkConfig().disablesslver.GetValue();
|
||||||
|
return false;
|
||||||
|
}
|
@ -332,7 +332,7 @@ void CemuApp::CreateDefaultFiles(bool first_start)
|
|||||||
if (!fs::exists(countryFile))
|
if (!fs::exists(countryFile))
|
||||||
{
|
{
|
||||||
std::ofstream file(countryFile);
|
std::ofstream file(countryFile);
|
||||||
for (sint32 i = 0; i < 201; i++)
|
for (sint32 i = 0; i < NCrypto::GetCountryCount(); i++)
|
||||||
{
|
{
|
||||||
const char* countryCode = NCrypto::GetCountryAsString(i);
|
const char* countryCode = NCrypto::GetCountryAsString(i);
|
||||||
if (boost::iequals(countryCode, "NN"))
|
if (boost::iequals(countryCode, "NN"))
|
||||||
|
@ -686,8 +686,10 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
|||||||
if (!NetworkConfig::XMLExists())
|
if (!NetworkConfig::XMLExists())
|
||||||
m_active_service->Enable(2, false);
|
m_active_service->Enable(2, false);
|
||||||
|
|
||||||
|
m_active_service->SetItemToolTip(0, _("Connect to the official Nintendo Network Service"));
|
||||||
|
m_active_service->SetItemToolTip(1, _("Connect to the Pretendo Network Service"));
|
||||||
|
m_active_service->SetItemToolTip(2, _("Connect to a custom Network Service (configured via network_services.xml)"));
|
||||||
|
|
||||||
m_active_service->SetToolTip(_("Connect to which Network Service"));
|
|
||||||
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
|
m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this);
|
||||||
content->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
|
content->Add(m_active_service, 0, wxEXPAND | wxALL, 5);
|
||||||
|
|
||||||
@ -762,7 +764,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
|||||||
m_account_grid->Append(new wxStringProperty(_("Email"), kPropertyEmail));
|
m_account_grid->Append(new wxStringProperty(_("Email"), kPropertyEmail));
|
||||||
|
|
||||||
wxPGChoices countries;
|
wxPGChoices countries;
|
||||||
for (int i = 0; i < 195; ++i)
|
for (int i = 0; i < NCrypto::GetCountryCount(); ++i)
|
||||||
{
|
{
|
||||||
const auto country = NCrypto::GetCountryAsString(i);
|
const auto country = NCrypto::GetCountryAsString(i);
|
||||||
if (country && (i == 0 || !boost::equals(country, "NN")))
|
if (country && (i == 0 || !boost::equals(country, "NN")))
|
||||||
@ -1948,8 +1950,6 @@ void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event)
|
|||||||
|
|
||||||
void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event)
|
void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
LaunchSettings::ChangeNetworkServiceURL(m_active_service->GetSelection());
|
|
||||||
|
|
||||||
UpdateAccountInformation();
|
UpdateAccountInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,17 +31,15 @@
|
|||||||
#include <wx/dirdlg.h>
|
#include <wx/dirdlg.h>
|
||||||
#include <wx/notebook.h>
|
#include <wx/notebook.h>
|
||||||
|
|
||||||
|
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
||||||
#include "config/ActiveSettings.h"
|
#include "config/ActiveSettings.h"
|
||||||
#include "gui/dialogs/SaveImport/SaveImportWindow.h"
|
#include "gui/dialogs/SaveImport/SaveImportWindow.h"
|
||||||
#include "Cafe/Account/Account.h"
|
#include "Cafe/Account/Account.h"
|
||||||
#include "Cemu/Tools/DownloadManager/DownloadManager.h"
|
#include "Cemu/Tools/DownloadManager/DownloadManager.h"
|
||||||
#include "gui/CemuApp.h"
|
#include "gui/CemuApp.h"
|
||||||
|
|
||||||
#include "Cafe/TitleList/TitleList.h"
|
#include "Cafe/TitleList/TitleList.h"
|
||||||
|
|
||||||
#include "resource/embedded/resources.h"
|
|
||||||
|
|
||||||
#include "Cafe/TitleList/SaveList.h"
|
#include "Cafe/TitleList/SaveList.h"
|
||||||
|
#include "resource/embedded/resources.h"
|
||||||
|
|
||||||
wxDEFINE_EVENT(wxEVT_TITLE_FOUND, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_TITLE_FOUND, wxCommandEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_TITLE_SEARCH_COMPLETE, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_TITLE_SEARCH_COMPLETE, wxCommandEvent);
|
||||||
@ -155,6 +153,7 @@ wxPanel* TitleManager::CreateDownloadManagerPage()
|
|||||||
{
|
{
|
||||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN
|
||||||
m_account = new wxChoice(panel, wxID_ANY);
|
m_account = new wxChoice(panel, wxID_ANY);
|
||||||
m_account->SetMinSize({ 250,-1 });
|
m_account->SetMinSize({ 250,-1 });
|
||||||
auto accounts = Account::GetAccounts();
|
auto accounts = Account::GetAccounts();
|
||||||
@ -172,6 +171,7 @@ wxPanel* TitleManager::CreateDownloadManagerPage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
row->Add(m_account, 0, wxALL, 5);
|
row->Add(m_account, 0, wxALL, 5);
|
||||||
|
#endif
|
||||||
|
|
||||||
m_connect = new wxButton(panel, wxID_ANY, _("Connect"));
|
m_connect = new wxButton(panel, wxID_ANY, _("Connect"));
|
||||||
m_connect->Bind(wxEVT_BUTTON, &TitleManager::OnConnect, this);
|
m_connect->Bind(wxEVT_BUTTON, &TitleManager::OnConnect, this);
|
||||||
@ -180,7 +180,17 @@ wxPanel* TitleManager::CreateDownloadManagerPage()
|
|||||||
sizer->Add(row, 0, wxEXPAND, 5);
|
sizer->Add(row, 0, wxEXPAND, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN
|
||||||
m_status_text = new wxStaticText(panel, wxID_ANY, _("Select an account and press Connect"));
|
m_status_text = new wxStaticText(panel, wxID_ANY, _("Select an account and press Connect"));
|
||||||
|
#else
|
||||||
|
if(!NCrypto::HasDataForConsoleCert())
|
||||||
|
{
|
||||||
|
m_status_text = new wxStaticText(panel, wxID_ANY, _("Valid online files are required to download eShop titles. For more information, go to the Account tab in the General Settings."));
|
||||||
|
m_connect->Enable(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_status_text = new wxStaticText(panel, wxID_ANY, _("Click on Connect to load the list of downloadable titles"));
|
||||||
|
#endif
|
||||||
this->Bind(wxEVT_SET_TEXT, &TitleManager::OnSetStatusText, this);
|
this->Bind(wxEVT_SET_TEXT, &TitleManager::OnSetStatusText, this);
|
||||||
sizer->Add(m_status_text, 0, wxALL, 5);
|
sizer->Add(m_status_text, 0, wxALL, 5);
|
||||||
|
|
||||||
@ -720,9 +730,10 @@ void TitleManager::OnSaveImport(wxCommandEvent& event)
|
|||||||
void TitleManager::InitiateConnect()
|
void TitleManager::InitiateConnect()
|
||||||
{
|
{
|
||||||
// init connection to download manager if queued
|
// init connection to download manager if queued
|
||||||
|
#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN
|
||||||
uint32 persistentId = (uint32)(uintptr_t)m_account->GetClientData(m_account->GetSelection());
|
uint32 persistentId = (uint32)(uintptr_t)m_account->GetClientData(m_account->GetSelection());
|
||||||
auto& account = Account::GetAccount(persistentId);
|
auto& account = Account::GetAccount(persistentId);
|
||||||
|
#endif
|
||||||
DownloadManager* dlMgr = DownloadManager::GetInstance();
|
DownloadManager* dlMgr = DownloadManager::GetInstance();
|
||||||
dlMgr->reset();
|
dlMgr->reset();
|
||||||
m_download_list->SetCurrentDownloadMgr(dlMgr);
|
m_download_list->SetCurrentDownloadMgr(dlMgr);
|
||||||
@ -742,7 +753,15 @@ void TitleManager::InitiateConnect()
|
|||||||
TitleManager::Callback_ConnectStatusUpdate,
|
TitleManager::Callback_ConnectStatusUpdate,
|
||||||
TitleManager::Callback_AddDownloadableTitle,
|
TitleManager::Callback_AddDownloadableTitle,
|
||||||
TitleManager::Callback_RemoveDownloadableTitle);
|
TitleManager::Callback_RemoveDownloadableTitle);
|
||||||
dlMgr->connect(account.GetAccountId(), account.GetAccountPasswordCache(), NCrypto::SEEPROM_GetRegion(), NCrypto::GetCountryAsString(account.GetCountry()), NCrypto::GetDeviceId(), NCrypto::GetSerial(), deviceCertBase64);
|
std::string accountName;
|
||||||
|
std::array<uint8, 32> accountPassword;
|
||||||
|
std::string accountCountry;
|
||||||
|
#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN
|
||||||
|
accountName = account.GetAccountId();
|
||||||
|
accountPassword = account.GetAccountPasswordCache();
|
||||||
|
accountCountry.assign(NCrypto::GetCountryAsString(account.GetCountry()));
|
||||||
|
#endif
|
||||||
|
dlMgr->connect(accountName, accountPassword, NCrypto::SEEPROM_GetRegion(), accountCountry, NCrypto::GetDeviceId(), NCrypto::GetSerial(), deviceCertBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TitleManager::OnConnect(wxCommandEvent& event)
|
void TitleManager::OnConnect(wxCommandEvent& event)
|
||||||
@ -787,7 +806,9 @@ void TitleManager::OnDisconnect(wxCommandEvent& event)
|
|||||||
|
|
||||||
void TitleManager::SetConnected(bool state)
|
void TitleManager::SetConnected(bool state)
|
||||||
{
|
{
|
||||||
|
#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN
|
||||||
m_account->Enable(!state);
|
m_account->Enable(!state);
|
||||||
|
#endif
|
||||||
m_connect->Enable(!state);
|
m_connect->Enable(!state);
|
||||||
|
|
||||||
m_show_titles->Enable(state);
|
m_show_titles->Enable(state);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "Cemu/Tools/DownloadManager/DownloadManager.h"
|
#include "Cemu/Tools/DownloadManager/DownloadManager.h"
|
||||||
|
|
||||||
|
#define DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN 0
|
||||||
|
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
class wxStaticText;
|
class wxStaticText;
|
||||||
class wxListEvent;
|
class wxListEvent;
|
||||||
|
Loading…
Reference in New Issue
Block a user