Cemu/src/Cafe/IOSU/legacy/iosu_nim.cpp

333 lines
9.3 KiB
C++

#include "iosu_nim.h"
#include "iosu_ioctl.h"
#include "iosu_act.h"
#include "iosu_mcp.h"
#include "util/crypto/aes128.h"
#include "curl/curl.h"
#include "openssl/bn.h"
#include "openssl/x509.h"
#include "openssl/ssl.h"
#include "util/helpers/helpers.h"
#include <thread>
#include "Cemu/napi/napi.h"
#include "Cemu/ncrypto/ncrypto.h"
namespace iosu
{
namespace nim
{
struct NIMTitleLatestVersion
{
uint64 titleId;
uint32 version;
};
#define PACKAGE_TYPE_UPDATE (1)
struct NIMPackage
{
uint64 titleId;
uint16 version;
uint8 type;
};
struct
{
bool isInitialized;
// version list
sint32 latestVersion;
char fqdn[256];
std::vector<NIMTitleLatestVersion> titlesLatestVersion;
// nim packages
// note: Seems like scope.rpx expects the number of packages to never change after the initial GetNum call?
std::vector<NIMPackage*> packages;
bool packageListReady;
bool backgroundThreadStarted;
} g_nim = {};
bool nim_getLatestVersion()
{
g_nim.latestVersion = -1;
NAPI::AuthInfo authInfo;
authInfo.country = NCrypto::GetCountryAsString(Account::GetCurrentAccount().GetCountry());
authInfo.region = NCrypto::SEEPROM_GetRegion();
auto versionListVersionResult = NAPI::TAG_GetVersionListVersion(authInfo);
if (!versionListVersionResult.isValid)
return false;
if (versionListVersionResult.fqdnURL.size() >= 256)
{
cemuLog_log(LogType::Force, "NIM: fqdn URL too long");
return false;
}
g_nim.latestVersion = (sint32)versionListVersionResult.version;
strcpy(g_nim.fqdn, versionListVersionResult.fqdnURL.c_str());
return true;
}
bool nim_getVersionList()
{
g_nim.titlesLatestVersion.clear();
NAPI::AuthInfo authInfo;
authInfo.country = NCrypto::GetCountryAsString(Account::GetCurrentAccount().GetCountry());
authInfo.region = NCrypto::SEEPROM_GetRegion();
auto versionListVersionResult = NAPI::TAG_GetVersionListVersion(authInfo);
if (!versionListVersionResult.isValid)
return false;
auto versionListResult = TAG_GetVersionList(authInfo, versionListVersionResult.fqdnURL, versionListVersionResult.version);
if (!versionListResult.isValid)
return false;
for (auto& itr : versionListResult.titleVersionList)
{
NIMTitleLatestVersion titleLatestVersion;
titleLatestVersion.titleId = itr.first;
titleLatestVersion.version = itr.second;
g_nim.titlesLatestVersion.push_back(titleLatestVersion);
}
return true;
}
NIMTitleLatestVersion* nim_findTitleLatestVersion(uint64 titleId)
{
for (auto& titleLatestVersion : g_nim.titlesLatestVersion)
{
if (titleLatestVersion.titleId == titleId)
return &titleLatestVersion;
}
return nullptr;
}
void nim_buildDownloadList()
{
sint32 titleCount = mcpGetTitleCount();
MCPTitleInfo* titleList = (MCPTitleInfo*)malloc(titleCount * sizeof(MCPTitleInfo));
memset(titleList, 0, titleCount * sizeof(MCPTitleInfo));
uint32be titleCountBE = titleCount;
if (mcpGetTitleList(titleList, titleCount * sizeof(MCPTitleInfo), &titleCountBE) != 0)
{
cemuLog_log(LogType::Force, "IOSU: nim failed to acquire title list");
free(titleList);
return;
}
titleCount = titleCountBE;
// check for game updates
for (sint32 i = 0; i < titleCount; i++)
{
if( titleList[i].titleIdHigh != 0x00050000 )
continue;
// find update title in title version list
uint64 titleId = (0x0005000EULL << 32) | ((uint64)(uint32)titleList[i].titleIdLow);
NIMTitleLatestVersion* latestVersionInfo = nim_findTitleLatestVersion(titleId);
if(latestVersionInfo == nullptr)
continue;
// compare version
if(latestVersionInfo->version <= (uint32)titleList[i].titleVersion )
continue; // already on latest version
// add to packages
NIMPackage* nimPackage = (NIMPackage*)malloc(sizeof(NIMPackage));
memset(nimPackage, 0, sizeof(NIMPackage));
nimPackage->titleId = titleId;
nimPackage->type = PACKAGE_TYPE_UPDATE;
g_nim.packages.push_back(nimPackage);
}
// check for AOC/titles to download
// todo
free(titleList);
}
void nim_getPackagesInfo(uint64* titleIdList, sint32 count, titlePackageInfo_t* packageInfoList)
{
memset(packageInfoList, 0, sizeof(titlePackageInfo_t)*count);
for (sint32 i = 0; i < count; i++)
{
uint64 titleId = _swapEndianU64(titleIdList[i]);
titlePackageInfo_t* packageInfo = packageInfoList + i;
packageInfo->titleId = _swapEndianU64(titleIdList[0]);
packageInfo->ukn0C = 1; // update
// pending
packageInfo->ukn48 = 0; // with 0 there is no progress bar, with 3 there is a progress bar
packageInfo->ukn40 = (5 << 20) | (0 << 27) | (1<<31) | (0x30000<<0);
packageInfo->ukn0F = 1;
}
}
struct idbeIconCacheEntry_t
{
void setIconData(NAPI::IDBEIconDataV0& newIconData)
{
iconData = newIconData;
hasIconData = true;
}
void setNoIcon()
{
hasIconData = false;
iconData = {};
}
uint64 titleId;
uint32 lastRequestTime;
bool hasIconData{ false };
NAPI::IDBEIconDataV0 iconData{};
};
std::vector<idbeIconCacheEntry_t> idbeIconCache;
void idbe_addIconToCache(uint64 titleId, NAPI::IDBEIconDataV0* iconData)
{
idbeIconCacheEntry_t newCacheEntry;
newCacheEntry.titleId = titleId;
newCacheEntry.lastRequestTime = GetTickCount();
if (iconData)
newCacheEntry.setIconData(*iconData);
else
newCacheEntry.setNoIcon();
idbeIconCache.push_back(newCacheEntry);
}
sint32 nim_getIconDatabaseEntry(uint64 titleId, void* idbeIconOutput)
{
// if titleId is an update, replace it with gameId instead
if ((uint32)(titleId >> 32) == 0x0005000EULL)
{
titleId &= ~0xF00000000ULL;
}
for (auto& entry : idbeIconCache)
{
if (entry.titleId == titleId)
{
if( entry.hasIconData )
memcpy(idbeIconOutput, &entry.iconData, sizeof(NAPI::IDBEIconDataV0));
else
memset(idbeIconOutput, 0, sizeof(NAPI::IDBEIconDataV0));
return 0;
}
}
auto result = NAPI::IDBE_Request(titleId);
if (!result)
{
memset(idbeIconOutput, 0, sizeof(NAPI::IDBEIconDataV0));
cemuLog_log(LogType::Force, "NIM: Unable to download IDBE icon");
return 0;
}
// add new cache entry
idbe_addIconToCache(titleId, &*result);
// return result
memcpy(idbeIconOutput, &*result, sizeof(NAPI::IDBEIconDataV0));
return 0;
}
void nim_backgroundThread()
{
while (iosuAct_isAccountDataLoaded() == false)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
if (nim_getLatestVersion())
{
if (nim_getVersionList())
{
nim_buildDownloadList();
}
}
g_nim.packageListReady = true;
}
void iosuNim_waitUntilPackageListReady()
{
if (g_nim.backgroundThreadStarted == false)
{
cemuLog_log(LogType::Force, "IOSU: Starting nim background thread");
std::thread t(nim_backgroundThread);
t.detach();
g_nim.backgroundThreadStarted = true;
}
while (g_nim.packageListReady == false)
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
void iosuNim_thread()
{
SetThreadName("iosuNim_thread");
while (true)
{
uint32 returnValue = 0; // Ioctl return value
ioQueueEntry_t* ioQueueEntry = iosuIoctl_getNextWithWait(IOS_DEVICE_NIM);
if (ioQueueEntry->request == IOSU_NIM_REQUEST_CEMU)
{
iosuNimCemuRequest_t* nimCemuRequest = (iosuNimCemuRequest_t*)ioQueueEntry->bufferVectors[0].buffer.GetPtr();
if (nimCemuRequest->requestCode == IOSU_NIM_GET_ICON_DATABASE_ENTRY)
{
nimCemuRequest->returnCode = nim_getIconDatabaseEntry(nimCemuRequest->titleId, nimCemuRequest->ptr.GetPtr());
}
else if (nimCemuRequest->requestCode == IOSU_NIM_GET_PACKAGE_COUNT)
{
iosuNim_waitUntilPackageListReady();
nimCemuRequest->resultU32.u32 = (uint32)g_nim.packages.size();
nimCemuRequest->returnCode = 0;
}
else if (nimCemuRequest->requestCode == IOSU_NIM_GET_PACKAGES_TITLEID)
{
iosuNim_waitUntilPackageListReady();
uint32 maxCount = nimCemuRequest->maxCount;
uint64* titleIdList = (uint64*)nimCemuRequest->ptr.GetPtr();
uint32 count = 0;
memset(titleIdList, 0, sizeof(uint64) * maxCount);
for (auto& package : g_nim.packages)
{
titleIdList[count] = _swapEndianU64(package->titleId);
count++;
if (count >= maxCount)
break;
}
nimCemuRequest->returnCode = 0;
}
else if (nimCemuRequest->requestCode == IOSU_NIM_GET_PACKAGES_INFO)
{
iosuNim_waitUntilPackageListReady();
uint32 maxCount = nimCemuRequest->maxCount;
uint64* titleIdList = (uint64*)nimCemuRequest->ptr.GetPtr();
titlePackageInfo_t* packageInfo = (titlePackageInfo_t*)nimCemuRequest->ptr2.GetPtr();
nim_getPackagesInfo(titleIdList, maxCount, packageInfo);
nimCemuRequest->returnCode = 0;
}
else
cemu_assert_unimplemented();
}
else
cemu_assert_unimplemented();
iosuIoctl_completeRequest(ioQueueEntry, returnValue);
}
return;
}
void Initialize()
{
if (g_nim.isInitialized)
return;
std::vector<idbeIconCacheEntry_t> idbeIconCache = std::vector<idbeIconCacheEntry_t>();
g_nim.titlesLatestVersion = std::vector<NIMTitleLatestVersion>();
g_nim.packages = std::vector<NIMPackage*>();
g_nim.packageListReady = false;
g_nim.backgroundThreadStarted = false;
std::thread t2(iosuNim_thread);
t2.detach();
g_nim.isInitialized = true;
}
}
}