nn_olv: Added community related API (#873)

- Initialize
- Download communities (self-made / favorites / officials)
- Upload communities (create subcommunity)
- Upload favorite status (Add/Delete favorite to a subcommunity)

Enough for support of Mario Kart 8 tournaments
This commit is contained in:
Rambo6Glaz 2023-06-24 14:51:41 +02:00 committed by GitHub
parent 1beec40445
commit a8d157d310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3125 additions and 56 deletions

View File

@ -406,6 +406,16 @@ add_library(CemuCafe
OS/libs/nn_nim/nn_nim.h
OS/libs/nn_olv/nn_olv.cpp
OS/libs/nn_olv/nn_olv.h
OS/libs/nn_olv/nn_olv_Common.cpp
OS/libs/nn_olv/nn_olv_Common.h
OS/libs/nn_olv/nn_olv_InitializeTypes.cpp
OS/libs/nn_olv/nn_olv_InitializeTypes.h
OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp
OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
OS/libs/nn_pdm/nn_pdm.cpp
OS/libs/nn_pdm/nn_pdm.h
OS/libs/nn_save/nn_save.cpp

View File

@ -723,6 +723,13 @@ namespace CafeSystem
return applicationName;
}
uint32 GetForegroundTitleOlvAccesskey()
{
if (sLaunchModeIsStandalone)
return -1;
return sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetOlvAccesskey();
}
std::string GetForegroundTitleArgStr()
{
if (sLaunchModeIsStandalone)

View File

@ -26,6 +26,7 @@ namespace CafeSystem
CafeConsoleRegion GetPlatformRegion();
std::string GetForegroundTitleName();
std::string GetForegroundTitleArgStr();
uint32 GetForegroundTitleOlvAccesskey();
void ShutdownTitle();

View File

@ -95,6 +95,36 @@ namespace act
return result;
}
uint32 GetTransferableIdEx(uint64* transferableId, uint32 unique, uint8 slot)
{
actPrepareRequest2();
actRequest->requestCode = IOSU_ARC_TRANSFERABLEID;
actRequest->accountSlot = slot;
actRequest->unique = unique;
uint32 result = _doCemuActRequest(actRequest);
*transferableId = _swapEndianU64(actRequest->resultU64.u64);
return result;
}
uint32 AcquireIndependentServiceToken(independentServiceToken_t* token, const char* clientId, uint32 cacheDurationInSeconds)
{
memset(token, 0, sizeof(independentServiceToken_t));
actPrepareRequest();
actRequest->requestCode = IOSU_ARC_ACQUIREINDEPENDENTTOKEN;
actRequest->titleId = CafeSystem::GetForegroundTitleId();
actRequest->titleVersion = CafeSystem::GetForegroundTitleVersion();
actRequest->expiresIn = cacheDurationInSeconds;
strcpy(actRequest->clientId, clientId);
uint32 resultCode = __depr__IOS_Ioctlv(IOS_DEVICE_ACT, IOSU_ACT_REQUEST_CEMU, 1, 1, actBufferVector);
memcpy(token, actRequest->resultBinary.binBuffer, sizeof(independentServiceToken_t));
return getNNReturnCode(resultCode, actRequest);
}
sint32 g_initializeCount = 0; // inc in Initialize and dec in Finalize
uint32 Initialize()
{
@ -170,21 +200,6 @@ uint32 GetPrincipalIdEx(uint32be* principalId, uint8 slot)
return result;
}
uint32 GetTransferableIdEx(uint64* transferableId, uint32 unique, uint8 slot)
{
actPrepareRequest2();
actRequest->requestCode = IOSU_ARC_TRANSFERABLEID;
actRequest->accountSlot = slot;
actRequest->unique = unique;
uint32 result = _doCemuActRequest(actRequest);
*transferableId = _swapEndianU64(actRequest->resultU64.u64);
return result;
}
uint32 GetCountryEx(char* country, uint8 slot)
{
actPrepareRequest2();
@ -278,7 +293,7 @@ void nnActExport_GetTransferableIdEx(PPCInterpreter_t* hCPU)
cemuLog_logDebug(LogType::Force, "nn_act.GetTransferableIdEx(0x{:08x}, 0x{:08x}, {})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5] & 0xFF);
uint32 r = GetTransferableIdEx(transferableId, unique, slot);
uint32 r = nn::act::GetTransferableIdEx(transferableId, unique, slot);
osLib_returnFromFunction(hCPU, 0); // ResultSuccess
}
@ -589,33 +604,11 @@ void nnActExport_AcquireNexServiceToken(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, getNNReturnCode(resultCode, actRequest));
}
struct independentServiceToken_t
{
/* +0x000 */ char token[0x201];
};
static_assert(sizeof(independentServiceToken_t) == 0x201); // todo - verify size
uint32 AcquireIndependentServiceToken(independentServiceToken_t* token, const char* clientId, uint32 cacheDurationInSeconds)
{
memset(token, 0, sizeof(independentServiceToken_t));
actPrepareRequest();
actRequest->requestCode = IOSU_ARC_ACQUIREINDEPENDENTTOKEN;
actRequest->titleId = CafeSystem::GetForegroundTitleId();
actRequest->titleVersion = CafeSystem::GetForegroundTitleVersion();
actRequest->expiresIn = cacheDurationInSeconds;
strcpy(actRequest->clientId, clientId);
uint32 resultCode = __depr__IOS_Ioctlv(IOS_DEVICE_ACT, IOSU_ACT_REQUEST_CEMU, 1, 1, actBufferVector);
memcpy(token, actRequest->resultBinary.binBuffer, sizeof(independentServiceToken_t));
return getNNReturnCode(resultCode, actRequest);
}
void nnActExport_AcquireIndependentServiceToken(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(token, independentServiceToken_t, 0);
ppcDefineParamMEMPTR(serviceToken, const char, 1);
uint32 result = AcquireIndependentServiceToken(token.GetPtr(), serviceToken.GetPtr(), 0);
uint32 result = nn::act::AcquireIndependentServiceToken(token.GetPtr(), serviceToken.GetPtr(), 0);
cemuLog_logDebug(LogType::Force, "nn_act.AcquireIndependentServiceToken(0x{}, {}) -> {:x}", (void*)token.GetPtr(), serviceToken.GetPtr(), result);
cemuLog_logDebug(LogType::Force, "Token: {}", serviceToken.GetPtr());
osLib_returnFromFunction(hCPU, result);
@ -626,7 +619,7 @@ void nnActExport_AcquireIndependentServiceToken2(PPCInterpreter_t* hCPU)
ppcDefineParamStructPtr(token, independentServiceToken_t, 0);
ppcDefineParamMEMPTR(clientId, const char, 1);
ppcDefineParamU32(cacheDurationInSeconds, 2);
uint32 result = AcquireIndependentServiceToken(token, clientId.GetPtr(), cacheDurationInSeconds);
uint32 result = nn::act::AcquireIndependentServiceToken(token, clientId.GetPtr(), cacheDurationInSeconds);
cemuLog_logDebug(LogType::Force, "Called nn_act.AcquireIndependentServiceToken2");
osLib_returnFromFunction(hCPU, result);
}
@ -634,7 +627,7 @@ void nnActExport_AcquireIndependentServiceToken2(PPCInterpreter_t* hCPU)
void nnActExport_AcquireEcServiceToken(PPCInterpreter_t* hCPU)
{
ppcDefineParamMEMPTR(pEcServiceToken, independentServiceToken_t, 0);
uint32 result = AcquireIndependentServiceToken(pEcServiceToken.GetPtr(), "71a6f5d6430ea0183e3917787d717c46", 0);
uint32 result = nn::act::AcquireIndependentServiceToken(pEcServiceToken.GetPtr(), "71a6f5d6430ea0183e3917787d717c46", 0);
cemuLog_logDebug(LogType::Force, "Called nn_act.AcquireEcServiceToken");
osLib_returnFromFunction(hCPU, result);
}

View File

@ -1,5 +1,13 @@
#pragma once
#include "Cafe/IOSU/legacy/iosu_act.h"
struct independentServiceToken_t
{
/* +0x000 */ char token[0x201];
};
static_assert(sizeof(independentServiceToken_t) == 0x201); // todo - verify size
namespace nn
{
namespace act
@ -9,6 +17,9 @@ namespace act
uint32 GetPersistentIdEx(uint8 slot);
uint32 GetUuidEx(uint8* uuid, uint8 slot, sint32 name = -2);
uint32 GetSimpleAddressIdEx(uint32be* simpleAddressId, uint8 slot);
uint32 GetTransferableIdEx(uint64* transferableId, uint32 unique, uint8 slot);
uint32 AcquireIndependentServiceToken(independentServiceToken_t* token, const char* clientId, uint32 cacheDurationInSeconds);
static uint32 getCountryCodeFromSimpleAddress(uint32 simpleAddressId)
{

View File

@ -1,7 +1,10 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/nn_common.h"
#include "nn_olv.h"
#include "nn_olv_InitializeTypes.h"
#include "nn_olv_UploadCommunityTypes.h"
#include "nn_olv_DownloadCommunityTypes.h"
#include "nn_olv_UploadFavoriteTypes.h"
namespace nn
{
namespace olv
@ -124,14 +127,6 @@ namespace nn
osLib_returnFromFunction(hCPU, 0);
}
void export_DownloadCommunityDataList(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(communityListSizeOut, uint32be, 1);
*communityListSizeOut = 0;
osLib_returnFromFunction(hCPU, 0);
}
void exportDownloadPostData_TestFlags(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(downloadedPostData, DownloadedPostData_t, 0);
@ -209,8 +204,65 @@ namespace nn
return BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_OLV, 0); // undefined error
}
// https://github.com/kinnay/NintendoClients/wiki/Wii-U-Error-Codes#act-error-codes
constexpr uint32 GetErrorCodeImpl(uint32 in)
{
uint32_t errorCode = in;
uint32_t errorVersion = (errorCode >> 27) & 3;
uint32_t errorModuleMask = (errorVersion != 3) ? 0x1FF00000 : 0x7F00000;
bool isCodeFailure = errorCode & 0x80000000;
if (((errorCode & errorModuleMask) >> 20) == NN_RESULT_MODULE_NN_ACT)
{
// BANNED_ACCOUNT_IN_INDEPENDENT_SERVICE or BANNED_ACCOUNT_IN_INDEPENDENT_SERVICE_TEMPORARILY
if (errorCode == OLV_ACT_RESULT_STATUS(2805) || errorCode == OLV_ACT_RESULT_STATUS(2825))
{
uint32 tmpCode = OLV_RESULT_STATUS(1008);
return GetErrorCodeImpl(tmpCode);
}
// BANNED_DEVICE_IN_INDEPENDENT_SERVICE or BANNED_DEVICE_IN_INDEPENDENT_SERVICE_TEMPORARILY
else if (errorCode == OLV_ACT_RESULT_STATUS(2815) || errorCode == OLV_ACT_RESULT_STATUS(2835))
{
uint32 tmpCode = OLV_RESULT_STATUS(1009);
return GetErrorCodeImpl(tmpCode);
}
else
{
// Check ACT error code
return 1159999;
}
}
else
{
if (((errorCode & errorModuleMask) >> 20) == NN_RESULT_MODULE_NN_OLV && isCodeFailure)
{
uint32_t errorValueMask = (errorVersion != 3) ? 0xFFFFF : 0x3FF;
return ((errorCode & errorValueMask) >> 7) + 1150000;
}
else
{
return 1159999;
}
}
}
uint32 GetErrorCode(uint32be* pResult)
{
return GetErrorCodeImpl(pResult->value());
}
static_assert(GetErrorCodeImpl(0xa119c600) == 1155004);
void load()
{
loadOliveInitializeTypes();
loadOliveUploadCommunityTypes();
loadOliveDownloadCommunityTypes();
loadOliveUploadFavoriteTypes();
cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::None);
osLib_addFunction("nn_olv", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", export_DownloadPostDataList);
osLib_addFunction("nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", exportDownloadPostData_TestFlags);
osLib_addFunction("nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetPostId);
@ -218,8 +270,6 @@ namespace nn
osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag);
osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText);
osLib_addFunction("nn_olv", "DownloadCommunityDataList__Q2_2nn3olvFPQ3_2nn3olv23DownloadedCommunityDataPUiUiPCQ3_2nn3olv30DownloadCommunityDataListParam", export_DownloadCommunityDataList);
osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken);
cafeExportRegisterFunc(UploadPostDataByPostApp, "nn_olv", "UploadPostDataByPostApp__Q2_2nn3olvFPCQ3_2nn3olv28UploadPostDataByPostAppParam", LogType::Placeholder);

View File

@ -1,9 +1,23 @@
#pragma once
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/nn_common.h"
#include "Cafe/OS/libs/nn_act/nn_act.h"
#include "Cafe/CafeSystem.h"
#include "Cemu/napi/napi.h"
#include "nn_olv_Common.h"
namespace nn
{
namespace olv
{
extern ParamPackStorage g_ParamPack;
extern DiscoveryResultStorage g_DiscoveryResults;
sint32 GetOlvAccessKey(uint32_t* pOutKey);
void load();
}
}

View File

@ -0,0 +1,285 @@
#include "nn_olv_Common.h"
#include <zlib.h>
namespace nn
{
namespace olv
{
sint32 olv_copy_wstr(char16_t* dest, const char16_t* src, uint32_t maxSize, uint32_t destSize)
{
size_t len = maxSize + 1;
if (olv_wstrnlen(src, len) > maxSize)
return OLV_RESULT_NOT_ENOUGH_SIZE;
memset(dest, 0, 2 * destSize);
olv_wstrncpy(dest, src, len);
return OLV_RESULT_SUCCESS;
}
size_t olv_wstrnlen(const char16_t* str, size_t max_len)
{
size_t len = 0;
while (len < max_len && str[len] != u'\0')
len++;
return len;
}
char16_t* olv_wstrncpy(char16_t* dest, const char16_t* src, size_t n)
{
char16_t* ret = dest;
while (n > 0 && *src != u'\0')
{
*dest++ = *src++;
n--;
}
while (n > 0)
{
*dest++ = u'\0';
n--;
}
return ret;
}
bool CheckTGA(const uint8* pTgaFile, uint32 pTgaFileLen, TGACheckType checkType)
{
const TGAHeader* header = (const TGAHeader*)pTgaFile;
try
{
if (checkType == TGACheckType::CHECK_PAINTING)
{
if (
header->idLength ||
header->colorMapType ||
header->imageType != 2 || // Uncompressed true color
header->first_entry_idx ||
header->colormap_length ||
header->bpp ||
header->x_origin ||
header->y_origin ||
header->width != 320 ||
header->height != 120 ||
header->pixel_depth_bpp != 32 ||
header->image_desc_bits != 8
)
{
throw std::runtime_error("TGACheckType::CHECK_PAINTING - Invalid TGA file!");
}
}
else if (checkType == TGACheckType::CHECK_COMMUNITY_ICON)
{
if (header->width != 128 || header->height != 128 || header->pixel_depth_bpp != 32)
throw std::runtime_error("TGACheckType::CHECK_COMMUNITY_ICON - Invalid TGA file -> width, height or bpp is wrong");
}
else if (checkType == TGACheckType::CHECK_100x100_200x200)
{
if (header->pixel_depth_bpp != 32)
throw std::runtime_error("TGACheckType::CHECK_100x100_200x200 - Invalid TGA file -> bpp is wrong");
if (header->width == 100)
{
if (header->height != 100)
throw std::runtime_error("TGACheckType::CHECK_100x100_200x200 - Invalid TGA file -> Not 100x100");
}
else if (header->width != 200 || header->height != 200)
throw std::runtime_error("TGACheckType::CHECK_100x100_200x200 - Invalid TGA file -> Not 100x100 or 200x200");
}
}
catch (const std::runtime_error& error)
{
// TGA Check Error! illegal format
cemuLog_log(LogType::Force, error.what());
return false;
}
return true;
}
sint32 DecodeTGA(uint8* pInBuffer, uint32 inSize, uint8* pOutBuffer, uint32 outSize, TGACheckType checkType)
{
uint32 decompressedSize = outSize;
if (DecompressTGA(pOutBuffer, &decompressedSize, pInBuffer, inSize))
{
if (CheckTGA(pOutBuffer, decompressedSize, checkType))
return decompressedSize;
return -2;
}
else
{
cemuLog_log(LogType::Force, "OLIVE uncompress error.\n");
return -1;
}
}
sint32 EncodeTGA(uint8* pInBuffer, uint32 inSize, uint8* pOutBuffer, uint32 outSize, TGACheckType checkType)
{
if (inSize == outSize)
{
if (!CheckTGA(pInBuffer, inSize, checkType))
return -1;
uint32 compressedSize = outSize;
if (CompressTGA(pOutBuffer, &compressedSize, pInBuffer, inSize))
return compressedSize;
else
{
cemuLog_log(LogType::Force, "OLIVE compress error.\n");
return -1;
}
}
else
{
cemuLog_log(LogType::Force, "compress buffer size check error. uSrcBufSize({}) != uDstBufSize({})\n", inSize, outSize);
return -1;
}
}
bool DecompressTGA(uint8* pOutBuffer, uint32* pOutSize, uint8* pInBuffer, uint32 inSize)
{
if (pOutBuffer == nullptr || pOutSize == nullptr || pInBuffer == nullptr || inSize == 0)
return false;
uLongf bufferSize = *pOutSize;
int result = uncompress(pOutBuffer, &bufferSize, pInBuffer, inSize);
if (result == Z_OK)
{
*pOutSize = static_cast<unsigned int>(bufferSize);
return true;
}
else
{
const char* error_msg = (result == Z_MEM_ERROR) ? "Insufficient memory" : "Unknown decompression error";
cemuLog_log(LogType::Force, "OLIVE ZLIB - ERROR: {}\n", error_msg);
return false;
}
}
bool CompressTGA(uint8* pOutBuffer, uint32* pOutSize, uint8* pInBuffer, uint32 inSize)
{
if (pOutBuffer == nullptr || pOutSize == nullptr || pInBuffer == nullptr || inSize == 0)
return false;
uLongf bufferSize = *pOutSize;
int result = compress(pOutBuffer, &bufferSize, pInBuffer, inSize);
if (result == Z_OK)
{
*pOutSize = static_cast<unsigned int>(bufferSize);
return true;
}
else
{
const char* error_msg = (result == Z_MEM_ERROR) ? "Insufficient memory" : "Unknown compression error";
cemuLog_log(LogType::Force, "OLIVE ZLIB - ERROR: {}\n", error_msg);
return false;
}
}
constexpr uint32 CreateCommunityCodeById(uint32 communityId)
{
uint32 res = communityId ^ (communityId << 18) ^ (communityId << 24) ^ (communityId << 30);
return res ^ (16 * (res & 0xF0F0F0F)) ^ ((res ^ (16 * (res & 0xF0F0F0F))) >> 17) ^ ((res ^ (16 * (res & 0xF0F0F0F))) >> 23) ^ ((res ^ (16 * (res & 0xF0F0F0F))) >> 29) ^ 0x20121002;
}
constexpr uint32 CreateCommunityIdByCode(uint32 code)
{
uint32 res = code ^ 0x20121002 ^ ((code ^ 0x20121002u) >> 17) ^ ((code ^ 0x20121002u) >> 23) ^ ((code ^ 0x20121002u) >> 29);
return res ^ (16 * (res & 0xF0F0F0F)) ^ ((res ^ (16 * (res & 0xF0F0F0F))) << 18) ^ ((res ^ (16 * (res & 0xF0F0F0F))) << 24) ^ ((res ^ (16 * (res & 0xF0F0F0F))) << 30);
}
constexpr uint32 GetCommunityCodeTopByte(uint32 communityId)
{
uint8 code_byte3 = (uint8_t)(communityId >> 0x18);
uint8 code_byte2 = (uint8_t)(communityId >> 0x10);
uint8 code_byte1 = (uint8_t)(communityId >> 8);
uint8 code_byte0 = (uint8_t)(communityId >> 0);
return code_byte3 ^ code_byte2 ^ code_byte1 ^ code_byte0 ^ 0xff;
}
constexpr uint64 GetRealCommunityCode(uint32_t communityId)
{
uint64 topByte = GetCommunityCodeTopByte(communityId);
if ((0xe7 < topByte) && ((0xe8 < topByte || (0xd4a50fff < communityId))))
return ((topByte << 32) | communityId) & 0x7fffffffff;
return ((topByte << 32) | communityId);
}
void WriteCommunityCode(char* pOutCode, uint32 communityId)
{
uint32 code = CreateCommunityCodeById(communityId);
uint64 communityCode = GetRealCommunityCode(code);
sprintf(pOutCode, "%012llu", communityCode);
}
bool EnsureCommunityCode(char* pCode)
{
uint64 code;
if (sscanf(pCode, "%012llu", &code) > 0)
{
uint32 lowerCode = code;
uint64 newCode = GetRealCommunityCode(code);
return code == newCode;
}
return false;
}
bool FormatCommunityCode(char* pOutCode, uint32* outLen, uint32 communityId)
{
bool result = false;
if (communityId != -1)
{
if (communityId)
{
WriteCommunityCode(pOutCode, communityId);
*outLen = strnlen(pOutCode, 12);
if (EnsureCommunityCode(pOutCode))
result = 1;
}
}
return result;
}
static_assert(GetRealCommunityCode(CreateCommunityCodeById(140500)) == 717651734336, "Wrong community code generation code, result must match.");
uint32 ExtractCommunityIdFromCode(char* pCode)
{
uint32 id = 0;
uint64 code;
if (sscanf(pCode, "%012llu", &code) > 0)
{
uint32 lower_code = code;
id = CreateCommunityIdByCode(lower_code);
}
return id;
}
bool GetCommunityIdFromCode(uint32* pOutId, const char* pCode)
{
if (!EnsureCommunityCode((char*)pCode))
return false;
*pOutId = ExtractCommunityIdFromCode((char*)pCode);
return true;
}
sint32 olv_curlformcode_to_error(CURLFORMcode code)
{
switch (code)
{
case CURL_FORMADD_OK:
return OLV_RESULT_SUCCESS;
case CURL_FORMADD_MEMORY:
return OLV_RESULT_FATAL(25);
case CURL_FORMADD_OPTION_TWICE:
default:
return OLV_RESULT_LVL6(50);
}
}
}
}

View File

@ -0,0 +1,179 @@
#pragma once
#include "Cafe/OS/libs/nn_common.h"
#include "Cafe/OS/common/OSCommon.h"
#include "Cemu/napi/napi_helper.h"
#include "util/helpers/StringHelpers.h"
#include "pugixml.hpp"
// https://github.com/kinnay/NintendoClients/wiki/Wii-U-Error-Codes#act-error-codes
#define OLV_ACT_RESULT_STATUS(code) (BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_OLV, ((code) << 7)))
#define OLV_RESULT_STATUS(code) (BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_OLV, ((code) << 7)))
#define OLV_RESULT_LVL6(code) (BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_OLV, ((code) << 7)))
#define OLV_RESULT_FATAL(code) (BUILD_NN_RESULT(NN_RESULT_LEVEL_FATAL, NN_RESULT_MODULE_NN_OLV, ((code) << 7)))
#define OLV_RESULT_SUCCESS (BUILD_NN_RESULT(0, NN_RESULT_MODULE_NN_OLV, 1 << 7))
#define OLV_RESULT_INVALID_PARAMETER (OLV_RESULT_LVL6(201))
#define OLV_RESULT_INVALID_DATA (OLV_RESULT_LVL6(202))
#define OLV_RESULT_NOT_ENOUGH_SIZE (OLV_RESULT_LVL6(203))
#define OLV_RESULT_INVALID_PTR (OLV_RESULT_LVL6(204))
#define OLV_RESULT_NOT_INITIALIZED (OLV_RESULT_LVL6(205))
#define OLV_RESULT_ALREADY_INITIALIZED (OLV_RESULT_LVL6(206))
#define OLV_RESULT_OFFLINE_MODE_REQUEST (OLV_RESULT_LVL6(207))
#define OLV_RESULT_MISSING_DATA (OLV_RESULT_LVL6(208))
#define OLV_RESULT_INVALID_SIZE (OLV_RESULT_LVL6(209))
#define OLV_RESULT_BAD_VERSION (OLV_RESULT_STATUS(2001))
#define OLV_RESULT_FAILED_REQUEST (OLV_RESULT_STATUS(2003))
#define OLV_RESULT_INVALID_XML (OLV_RESULT_STATUS(2004))
#define OLV_RESULT_INVALID_TEXT_FIELD (OLV_RESULT_STATUS(2006))
#define OLV_RESULT_INVALID_INTEGER_FIELD (OLV_RESULT_STATUS(2007))
#define OLV_CLIENT_ID "87cd32617f1985439ea608c2746e4610"
#define OLV_VERSION_MAJOR 5
#define OLV_VERSION_MINOR 0
#define OLV_VERSION_PATCH 3
namespace nn
{
namespace olv
{
struct ParamPackStorage
{
uint64_t titleId;
uint32_t accessKey;
uint32_t platformId;
uint8_t regionId, languageId, countryId, areaId;
uint8_t networkRestriction, friendRestriction;
uint32_t ratingRestriction;
uint8_t ratingOrganization;
uint64_t transferableId;
char tzName[72];
uint64_t utcOffset;
char encodedParamPack[512];
};
struct DiscoveryResultStorage
{
sint32 has_error;
char serviceToken[512];
char userAgent[64];
char apiEndpoint[256];
char portalEndpoint[256];
};
extern ParamPackStorage g_ParamPack;
extern DiscoveryResultStorage g_DiscoveryResults;
extern uint32_t g_ReportTypes;
extern bool g_IsInitialized;
extern bool g_IsOnlineMode;
static void InitializeOliveRequest(CurlRequestHelper& req)
{
req.addHeaderField("X-Nintendo-ServiceToken", g_DiscoveryResults.serviceToken);
req.addHeaderField("X-Nintendo-ParamPack", g_ParamPack.encodedParamPack);
curl_easy_setopt(req.getCURL(), CURLOPT_USERAGENT, g_DiscoveryResults.userAgent);
}
static void appendQueryToURL(char* url, const char* query)
{
size_t urlLength = strlen(url);
size_t queryLength = strlen(query);
char* delimiter = strchr(url, '?');
if (delimiter)
snprintf(url + urlLength, queryLength + 2, "&%s", query);
else
snprintf(url + urlLength, queryLength + 2, "?%s", query);
}
static sint32 CheckOliveResponse(pugi::xml_document& doc)
{
/*
<result>
<has_error>1</has_error>
<version>1</version>
<code>400</code>
<error_code>4</error_code>
<message>SERVICE_CLOSED</message>
</result>
*/
pugi::xml_node resultNode = doc.child("result");
if (!resultNode)
{
cemuLog_log(LogType::Force, "Discovery response doesn't contain <result>...</result>");
return OLV_RESULT_INVALID_XML;
}
std::string_view has_error = resultNode.child_value("has_error");
std::string_view version = resultNode.child_value("version");
std::string_view code = resultNode.child_value("code");
std::string_view error_code = resultNode.child_value("error_code");
if (has_error.compare("1") == 0)
{
int codeVal = StringHelpers::ToInt(error_code, -1);
if (codeVal < 0)
{
codeVal = StringHelpers::ToInt(code, -1);
return OLV_RESULT_STATUS(codeVal + 4000);
}
return OLV_RESULT_STATUS(codeVal + 5000);
}
if (version.compare("1") != 0)
return OLV_RESULT_BAD_VERSION; // Version mismatch
return OLV_RESULT_SUCCESS;
}
sint32 olv_copy_wstr(char16_t* dest, const char16_t* src, uint32_t maxSize, uint32_t destSize);
size_t olv_wstrnlen(const char16_t* str, size_t max_len);
char16_t* olv_wstrncpy(char16_t* dest, const char16_t* src, size_t n);
#pragma pack(push, 1)
struct TGAHeader
{
uint8 idLength;
uint8 colorMapType;
uint8 imageType;
uint16 first_entry_idx;
uint16 colormap_length;
uint8 bpp;
uint16 x_origin;
uint16 y_origin;
uint16 width;
uint16 height;
uint8 pixel_depth_bpp;
uint8 image_desc_bits;
};
#pragma pack(pop)
static_assert(sizeof(nn::olv::TGAHeader) == 0x12, "sizeof(nn::olv::TGAHeader != 0x12");
enum TGACheckType : uint32
{
CHECK_PAINTING = 0,
CHECK_COMMUNITY_ICON = 1,
CHECK_100x100_200x200 = 2
};
bool CheckTGA(const uint8* pTgaFile, uint32 pTgaFileLen, TGACheckType checkType);
sint32 DecodeTGA(uint8* pInBuffer, uint32 inSize, uint8* pOutBuffer, uint32 outSize, TGACheckType checkType);
sint32 EncodeTGA(uint8* pInBuffer, uint32 inSize, uint8* pOutBuffer, uint32 outSize, TGACheckType checkTyp);
bool CompressTGA(uint8* pOutBuffer, uint32* pOutSize, uint8* pInBuffer, uint32 inSize);
bool DecompressTGA(uint8* pOutBuffer, uint32* pOutSize, uint8* pInBuffer, uint32 inSize);
bool GetCommunityIdFromCode(uint32* pOutId, const char* pCode);
bool FormatCommunityCode(char* pOutCode, uint32* outLen, uint32 communityId);
sint32 olv_curlformcode_to_error(CURLFORMcode code);
}
}

View File

@ -0,0 +1,231 @@
#include "nn_olv_DownloadCommunityTypes.h"
namespace nn
{
namespace olv
{
sint32 DownloadCommunityDataList_AsyncRequestImpl(
CurlRequestHelper& req, const char* reqUrl,
DownloadedCommunityData* pOutList, uint32* pOutNum, uint32 numMaxList, const DownloadCommunityDataListParam* pParam);
sint32 DownloadCommunityDataList_AsyncRequest(
CurlRequestHelper& req, const char* reqUrl, coreinit::OSEvent* requestDoneEvent,
DownloadedCommunityData* pOutList, uint32* pOutNum, uint32 numMaxList, const DownloadCommunityDataListParam* pParam
)
{
sint32 res = DownloadCommunityDataList_AsyncRequestImpl(req, reqUrl, pOutList, pOutNum, numMaxList, pParam);
coreinit::OSSignalEvent(requestDoneEvent);
return res;
}
sint32 DownloadCommunityDataList(DownloadedCommunityData* pOutList, uint32* pOutNum, uint32 numMaxList, const DownloadCommunityDataListParam* pParam)
{
if (!g_IsInitialized)
return OLV_RESULT_NOT_INITIALIZED;
if (!g_IsOnlineMode)
return OLV_RESULT_OFFLINE_MODE_REQUEST;
if (!pOutList || !pOutNum || !pParam)
return OLV_RESULT_INVALID_PTR;
if (!numMaxList)
return OLV_RESULT_NOT_ENOUGH_SIZE;
for (int i = 0; i < numMaxList; i++)
DownloadedCommunityData::Clean(&pOutList[i]);
char reqUrl[2048];
sint32 res = pParam->GetRawDataUrl(reqUrl, sizeof(reqUrl));
if (res < 0)
return res;
CurlRequestHelper req;
req.initate(reqUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL);
std::future<sint32> requestRes = std::async(std::launch::async, DownloadCommunityDataList_AsyncRequest,
std::ref(req), reqUrl, requestDoneEvent.GetPointer(), pOutList, pOutNum, numMaxList, pParam);
coreinit::OSWaitEvent(requestDoneEvent);
return requestRes.get();
}
sint32 DownloadCommunityDataList_AsyncRequestImpl(
CurlRequestHelper& req, const char* reqUrl,
DownloadedCommunityData* pOutList, uint32* pOutNum, uint32 numMaxList, const DownloadCommunityDataListParam* pParam
)
{
bool reqResult = req.submitRequest();
long httpCode = 0;
curl_easy_getinfo(req.getCURL(), CURLINFO_RESPONSE_CODE, &httpCode);
if (!reqResult)
{
cemuLog_log(LogType::Force, "Failed request: {} ({})", reqUrl, httpCode);
if (!(httpCode >= 400))
return OLV_RESULT_FAILED_REQUEST;
}
pugi::xml_document doc;
if (!doc.load_buffer(req.getReceivedData().data(), req.getReceivedData().size()))
{
cemuLog_log(LogType::Force, fmt::format("Invalid XML in community download response"));
return OLV_RESULT_INVALID_XML;
}
sint32 responseError = CheckOliveResponse(doc);
if (responseError < 0)
return responseError;
if (httpCode != 200)
return OLV_RESULT_STATUS(httpCode + 4000);
std::string request_name = doc.select_single_node("//request_name").node().child_value();
if (request_name.size() == 0)
{
cemuLog_log(LogType::Force, "Community download response doesn't contain <request_name>");
return OLV_RESULT_INVALID_XML;
}
if ((request_name.compare("communities") != 0) && (request_name.compare("specified_communities") != 0))
{
cemuLog_log(LogType::Force, "Community download response <request_name> isn't \"communities\" or \"specified_communities\"");
return OLV_RESULT_INVALID_XML;
}
pugi::xml_node communities = doc.select_single_node("//communities").node();
if (!communities)
{
cemuLog_log(LogType::Force, "Community download response doesn't contain <communities>");
return OLV_RESULT_INVALID_XML;
}
int idx = 0;
for (pugi::xml_node communityNode : communities.children("community"))
{
if (idx >= numMaxList)
break;
DownloadedCommunityData* pOutData = &pOutList[idx];
std::string_view app_data = communityNode.child_value("app_data");
std::string_view community_id = communityNode.child_value("community_id");
std::string_view name = communityNode.child_value("name");
std::string_view description = communityNode.child_value("description");
std::string_view pid = communityNode.child_value("pid");
std::string_view icon = communityNode.child_value("icon");
std::string_view mii = communityNode.child_value("mii");
std::string_view screen_name = communityNode.child_value("screen_name");
if (app_data.size() != 0)
{
auto app_data_bin = NCrypto::base64Decode(app_data);
if (app_data_bin.size() != 0)
{
memcpy(pOutData->appData, app_data_bin.data(), std::min(size_t(0x400), app_data_bin.size()));
pOutData->flags |= DownloadedCommunityData::FLAG_HAS_APP_DATA;
pOutData->appDataLen = app_data_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 community_id_val = StringHelpers::ToInt64(community_id, -1);
if (community_id_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->communityId = community_id_val;
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
pOutData->titleText[i] = name_utf16.at(i).bevalue();
pOutData->flags |= DownloadedCommunityData::FLAG_HAS_TITLE_TEXT;
pOutData->titleTextMaxLen = name_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)
pOutData->description[i] = description_utf16.at(i).bevalue();
pOutData->flags |= DownloadedCommunityData::FLAG_HAS_DESC_TEXT;
pOutData->descriptionMaxLen = description_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 pid_val = StringHelpers::ToInt64(pid, -1);
if (pid_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->pid = pid_val;
if (icon.size() != 0)
{
auto icon_bin = NCrypto::base64Decode(icon);
if (icon_bin.size() != 0)
{
memcpy(pOutData->iconData, icon_bin.data(), std::min(size_t(0x1002c), icon_bin.size()));
pOutData->flags |= DownloadedCommunityData::FLAG_HAS_ICON_DATA;
pOutData->iconDataSize = icon_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
if (mii.size() != 0)
{
auto mii_bin = NCrypto::base64Decode(mii);
if (mii_bin.size() != 0)
{
memcpy(pOutData->miiFFLStoreData, mii_bin.data(), std::min(size_t(96), mii_bin.size()));
pOutData->flags |= DownloadedCommunityData::FLAG_HAS_MII_DATA;
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
if (screen_name.size() != 0)
{
auto screen_name_utf16 = StringHelpers::FromUtf8(screen_name).substr(0, 32);
if (screen_name_utf16.size() != 0)
{
for (int i = 0; i < screen_name_utf16.size(); i++)
pOutData->miiDisplayName[i] = screen_name_utf16.at(i).bevalue();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
idx++;
}
*pOutNum = _swapEndianU32(idx);
sint32 res = OLV_RESULT_SUCCESS;
if (idx > 0)
res = 0; // nn_olv doesn't do it like that, but it's the same effect. I have no clue why it returns 0 when you have 1+ communities downloaded
return res;
}
}
}

View File

@ -0,0 +1,530 @@
#pragma once
#include "Cemu/ncrypto/ncrypto.h"
#include "config/ActiveSettings.h"
#include "Cafe/OS/libs/nn_olv/nn_olv_Common.h"
namespace nn
{
namespace olv
{
class DownloadedCommunityData
{
public:
static const inline uint32 FLAG_HAS_TITLE_TEXT = (1 << 0);
static const inline uint32 FLAG_HAS_DESC_TEXT = (1 << 1);
static const inline uint32 FLAG_HAS_APP_DATA = (1 << 2);
static const inline uint32 FLAG_HAS_ICON_DATA = (1 << 3);
static const inline uint32 FLAG_HAS_MII_DATA = (1 << 4);
DownloadedCommunityData()
{
this->titleTextMaxLen = 0;
this->appDataLen = 0;
this->descriptionMaxLen = 0;
this->pid = 0;
this->communityId = 0;
this->flags = 0;
this->iconDataSize = 0;
this->miiDisplayName[0] = 0;
}
static DownloadedCommunityData* __ctor(DownloadedCommunityData* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) DownloadedCommunityData();
}
static DownloadedCommunityData* Clean(DownloadedCommunityData* data)
{
data->flags = 0;
data->communityId = 0;
data->pid = 0;
data->iconData[0] = 0;
data->titleTextMaxLen = 0;
data->appData[0] = 0;
data->appDataLen = 0;
data->description[0] = 0;
data->descriptionMaxLen = 0;
data->iconDataSize = 0;
data->titleText[0] = 0;
data->miiDisplayName[0] = 0;
return data;
}
bool TestFlags(uint32 flags) const
{
return (this->flags & flags) != 0;
}
static bool __TestFlags(DownloadedCommunityData* _this, uint32 flags)
{
return _this->TestFlags(flags);
}
uint32 GetCommunityId() const
{
return this->communityId;
}
static uint32 __GetCommunityId(DownloadedCommunityData* _this)
{
return _this->GetCommunityId();
}
sint32 GetCommunityCode(char* pBuffer, uint32 bufferSize) const
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize <= 12)
return OLV_RESULT_NOT_ENOUGH_SIZE;
uint32 len = 0;
if (FormatCommunityCode(pBuffer, &len, this->communityId))
return OLV_RESULT_SUCCESS;
return OLV_RESULT_INVALID_PARAMETER;
}
static sint32 __GetCommunityCode(DownloadedCommunityData* _this, char* pBuffer, uint32 bufferSize)
{
return _this->GetCommunityCode(pBuffer, bufferSize);
}
uint32 GetOwnerPid() const
{
return this->pid;
}
static uint32 __GetOwnerPid(DownloadedCommunityData* _this)
{
return _this->GetOwnerPid();
}
sint32 GetTitleText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_TITLE_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
uint32 readSize = this->titleTextMaxLen;
if (numChars < readSize)
readSize = numChars;
olv_wstrncpy(pBuffer, this->titleText, readSize);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetTitleText(DownloadedCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetTitleText(pBuffer, numChars);
}
sint32 GetDescriptionText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_DESC_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
olv_wstrncpy(pBuffer, this->description, numChars);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetDescriptionText(DownloadedCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetDescriptionText(pBuffer, numChars);
}
sint32 GetAppData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
uint32 appDataSize = bufferSize;
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize)
{
if (!this->TestFlags(FLAG_HAS_APP_DATA))
return OLV_RESULT_MISSING_DATA;
if (this->appDataLen < appDataSize)
appDataSize = this->appDataLen;
memcpy(pBuffer, this->appData, appDataSize);
if (pOutSize)
*pOutSize = appDataSize;
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetAppData(DownloadedCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetAppData(pBuffer, pOutSize, bufferSize);
}
uint32 GetAppDataSize() const
{
if (this->TestFlags(FLAG_HAS_APP_DATA))
return this->appDataLen;
return 0;
}
static uint32 __GetAppDataSize(DownloadedCommunityData* _this)
{
return _this->GetAppDataSize();
}
sint32 GetIconData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize < sizeof(this->iconData))
return OLV_RESULT_NOT_ENOUGH_SIZE;
if (!this->TestFlags(FLAG_HAS_ICON_DATA))
return OLV_RESULT_MISSING_DATA;
sint32 decodeRes = DecodeTGA(this->iconData, this->iconDataSize, pBuffer, bufferSize, TGACheckType::CHECK_COMMUNITY_ICON);
if (decodeRes >= 0)
{
if (pOutSize)
*pOutSize = (uint32)decodeRes;
return OLV_RESULT_SUCCESS;
}
if (pOutSize)
*pOutSize = 0;
if (decodeRes == -1)
cemuLog_log(LogType::Force, "OLIVE - icon uncompress failed.\n");
else if (decodeRes == -2)
cemuLog_log(LogType::Force, "OLIVE - icon decode error. NOT TGA.\n");
return OLV_RESULT_INVALID_TEXT_FIELD;
}
static sint32 __GetIconData(DownloadedCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetIconData(pBuffer, pOutSize, bufferSize);
}
sint32 GetOwnerMiiData(/* FFLStoreData* */void* pBuffer) const
{
if (!this->TestFlags(FLAG_HAS_MII_DATA))
return OLV_RESULT_MISSING_DATA;
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
memcpy(pBuffer, this->miiFFLStoreData, sizeof(this->miiFFLStoreData));
return OLV_RESULT_SUCCESS;
}
static sint32 __GetOwnerMiiData(DownloadedCommunityData* _this, /* FFLStoreData* */void* pBuffer)
{
return _this->GetOwnerMiiData(pBuffer);
}
const char16_t* GetOwnerMiiNickname() const
{
if (this->miiDisplayName[0])
return this->miiDisplayName;
return nullptr;
}
static const char16_t* __GetOwnerMiiNickname(DownloadedCommunityData* _this)
{
return _this->GetOwnerMiiNickname();
}
public:
uint32be flags;
uint32be communityId;
uint32be pid;
char16_t titleText[128];
uint32be titleTextMaxLen;
char16_t description[256];
uint32be descriptionMaxLen;
uint8 appData[1024];
uint32be appDataLen;
uint8 iconData[65580];
uint32be iconDataSize;
uint8 miiFFLStoreData[96];
char16_t miiDisplayName[32];
uint8 unk[6168];
};
static_assert(sizeof(nn::olv::DownloadedCommunityData) == 0x12000, "sizeof(nn::olv::DownloadedCommunityData) != 0x12000");
class DownloadCommunityDataListParam
{
public:
static const inline uint32 FLAG_FILTER_FAVORITES = (1 << 0);
static const inline uint32 FLAG_FILTER_OFFICIALS = (1 << 1);
static const inline uint32 FLAG_FILTER_OWNED = (1 << 2);
static const inline uint32 FLAG_QUERY_MII_DATA = (1 << 3);
static const inline uint32 FLAG_QUERY_ICON_DATA = (1 << 4);
DownloadCommunityDataListParam()
{
this->flags = 0;
this->communityDownloadLimit = 0;
this->communityId = 0;
for (int i = 0; i < 20; i++)
this->additionalCommunityIdList[i] = -2;
}
static DownloadCommunityDataListParam* __ctor(DownloadCommunityDataListParam* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) DownloadCommunityDataListParam();
}
sint32 SetFlags(uint32 flags)
{
this->flags = flags;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetFlags(DownloadCommunityDataListParam* _this, uint32 flags)
{
return _this->SetFlags(flags);
}
sint32 SetCommunityId(uint32 communityId)
{
if (communityId == -1)
return OLV_RESULT_INVALID_PARAMETER;
this->communityId = communityId;
if (communityId)
{
if (!this->communityDownloadLimit)
this->communityDownloadLimit = 1;
}
return OLV_RESULT_SUCCESS;
}
static sint32 __SetCommunityId(DownloadCommunityDataListParam* _this, uint32 communityId)
{
return _this->SetCommunityId(communityId);
}
sint32 SetCommunityId(uint32 communityId, uint8 idx)
{
if (communityId == -1)
return OLV_RESULT_INVALID_PARAMETER;
if (idx >= 20)
return OLV_RESULT_INVALID_PARAMETER;
this->additionalCommunityIdList[idx] = communityId;
int validIdsCount = 0;
for (int i = 0; i < 20; i++ )
{
if (this->additionalCommunityIdList[i] != -2)
++validIdsCount;
}
if (validIdsCount > this->communityDownloadLimit)
this->communityDownloadLimit = validIdsCount;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetCommunityId(DownloadCommunityDataListParam* _this, uint32 communityId, uint8 idx)
{
return _this->SetCommunityId(communityId, idx);
}
sint32 SetCommunityDataMaxNum(uint32 num)
{
if (!num)
return OLV_RESULT_INVALID_PARAMETER;
int validIdsCount = 0;
for (int i = 0; i < 20; ++i)
{
if (this->additionalCommunityIdList[i] != -2)
++validIdsCount;
}
if (validIdsCount > num)
return OLV_RESULT_INVALID_PARAMETER;
this->communityDownloadLimit = num;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetCommunityDataMaxNum(DownloadCommunityDataListParam* _this, uint32 num)
{
return _this->SetCommunityDataMaxNum(num);
}
sint32 GetRawDataUrl(char* pBuffer, uint32 bufferSize) const
{
if (!g_IsOnlineMode)
return OLV_RESULT_OFFLINE_MODE_REQUEST;
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (!bufferSize)
return OLV_RESULT_NOT_ENOUGH_SIZE;
char tmpFormatBuffer[64];
char urlBuffer[1024];
memset(urlBuffer, 0, sizeof(urlBuffer));
uint32 communityId;
int validIdsCount = 0;
for (int i = 0; i < 20; ++i)
{
if (this->additionalCommunityIdList[i] != -2)
{
communityId = this->additionalCommunityIdList[i];
++validIdsCount;
}
}
if (validIdsCount)
{
if (this->communityId && this->communityId != -2)
return OLV_RESULT_INVALID_PARAMETER;
uint32 unkFlag = this->flags & 0xFFFFFFE7;
if (unkFlag)
return OLV_RESULT_INVALID_PARAMETER;
// It's how it's done in the real nn_olv, what even the fuck is this, never seen used yet.
snprintf(urlBuffer, sizeof(urlBuffer), "%s/v1/communities/%u.search", g_DiscoveryResults.apiEndpoint, communityId);
for (int i = 0; i < 20; ++i)
{
if (this->additionalCommunityIdList[i] != -2)
{
snprintf(tmpFormatBuffer, 64, "community_id=%u", this->additionalCommunityIdList[i].value());
appendQueryToURL(urlBuffer, tmpFormatBuffer);
++unkFlag;
}
}
}
else
snprintf(urlBuffer, sizeof(urlBuffer), "%s/v1/communities", g_DiscoveryResults.apiEndpoint);
if (this->communityId)
{
snprintf(tmpFormatBuffer, 64, "community_id=%u", this->communityId.value());
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
else
{
uint32 filterBy_favorite = (this->flags & FLAG_FILTER_FAVORITES) != 0;
uint32 filterBy_official = (this->flags & FLAG_FILTER_OFFICIALS) != 0;
uint32 filterBy_selfmade = (this->flags & FLAG_FILTER_OWNED) != 0;
if ((filterBy_favorite + filterBy_official + filterBy_selfmade) != 1)
return OLV_RESULT_INVALID_PARAMETER;
snprintf(tmpFormatBuffer, 64, "limit=%u", this->communityDownloadLimit.value());
appendQueryToURL(urlBuffer, tmpFormatBuffer);
if (filterBy_favorite)
{
strncpy(tmpFormatBuffer, "type=favorite", 64);
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
else if (filterBy_official)
{
strncpy(tmpFormatBuffer, "type=official", 64);
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
else
{
strncpy(tmpFormatBuffer, "type=my", 64);
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
}
if (this->flags & FLAG_QUERY_MII_DATA)
{
strncpy(tmpFormatBuffer, "with_mii=1", 64);
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
if (this->flags & FLAG_QUERY_ICON_DATA)
{
strncpy(tmpFormatBuffer, "with_icon=1", 64);
appendQueryToURL(urlBuffer, tmpFormatBuffer);
}
int res = snprintf(pBuffer, bufferSize, "%s", urlBuffer);
if (res < 0)
return OLV_RESULT_NOT_ENOUGH_SIZE;
return OLV_RESULT_SUCCESS;
}
static sint32 __GetRawDataUrl(DownloadCommunityDataListParam* _this, char* pBuffer, uint32 bufferSize)
{
return _this->GetRawDataUrl(pBuffer, bufferSize);
}
public:
uint32be flags;
uint32be communityId;
uint32be communityDownloadLimit;
uint32be additionalCommunityIdList[20]; // Additional community ID filter list
uint8 unk[4004]; // Looks unused lol, probably reserved data
};
static_assert(sizeof(nn::olv::DownloadCommunityDataListParam) == 0x1000, "sizeof(nn::olv::DownloadCommunityDataListParam) != 0x1000");
sint32 DownloadCommunityDataList(DownloadedCommunityData* pOutList, uint32* pOutNum, uint32 numMaxList, const DownloadCommunityDataListParam* pParam);
static void loadOliveDownloadCommunityTypes()
{
cafeExportRegisterFunc(DownloadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv23DownloadedCommunityDataFv", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv23DownloadedCommunityDataCFUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv23DownloadedCommunityDataCFPcUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv23DownloadedCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv23DownloadedCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiData, "nn_olv", "GetOwnerMiiData__Q3_2nn3olv23DownloadedCommunityDataCFP12FFLStoreData", LogType::None);
cafeExportRegisterFunc(DownloadedCommunityData::__GetOwnerMiiNickname, "nn_olv", "GetOwnerMiiNickname__Q3_2nn3olv23DownloadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadCommunityDataListParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv30DownloadCommunityDataListParamFv", LogType::None);
cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None);
cafeExportRegisterFunc(DownloadCommunityDataListParam::__SetCommunityDataMaxNum, "nn_olv", "SetCommunityDataMaxNum__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None);
cafeExportRegisterFunc(DownloadCommunityDataListParam::__GetRawDataUrl, "nn_olv", "GetRawDataUrl__Q3_2nn3olv30DownloadCommunityDataListParamCFPcUi", LogType::None);
cafeExportRegisterFunc((sint32 (*)(DownloadCommunityDataListParam*, uint32))DownloadCommunityDataListParam::__SetCommunityId,
"nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUi", LogType::None);
cafeExportRegisterFunc((sint32(*)(DownloadCommunityDataListParam*, uint32, uint8))DownloadCommunityDataListParam::__SetCommunityId,
"nn_olv", "SetCommunityId__Q3_2nn3olv30DownloadCommunityDataListParamFUiUc", LogType::None);
cafeExportRegisterFunc(DownloadCommunityDataList, "nn_olv", "DownloadCommunityDataList__Q2_2nn3olvFPQ3_2nn3olv23DownloadedCommunityDataPUiUiPCQ3_2nn3olv30DownloadCommunityDataListParam", LogType::None);
}
}
}

View File

@ -0,0 +1,312 @@
#pragma once
#include "nn_olv_InitializeTypes.h"
#include "CafeSystem.h"
#include "Cafe/OS/libs/nn_act/nn_act.h"
#include <time.h>
namespace nn
{
namespace olv
{
uint32_t g_ReportTypes = 0;
bool g_IsOnlineMode = false;
bool g_IsInitialized = false;
ParamPackStorage g_ParamPack;
DiscoveryResultStorage g_DiscoveryResults;
uint64 get_utc_offset()
{
time_t gmt, rawtime = time(NULL);
struct tm* ptm;
#if !defined(WIN32)
struct tm gbuf;
ptm = gmtime_r(&rawtime, &gbuf);
#else
ptm = gmtime(&rawtime);
#endif
ptm->tm_isdst = -1;
gmt = mktime(ptm);
return (uint64)difftime(rawtime, gmt);
}
sint32 GetOlvAccessKey(uint32_t* pOutKey)
{
*pOutKey = 0;
uint32_t accessKey = CafeSystem::GetForegroundTitleOlvAccesskey();
if (accessKey == -1)
return OLV_RESULT_STATUS(1102);
*pOutKey = accessKey;
return OLV_RESULT_SUCCESS;
}
sint32 CreateParamPack(uint64_t titleId, uint32_t accessKey)
{
g_ParamPack.languageId = uint8(GetConfig().console_language.GetValue());
uint32be simpleAddress = 0;
nn::act::GetSimpleAddressIdEx(&simpleAddress, nn::act::ACT_SLOT_CURRENT);
uint32 countryCode = nn::act::getCountryCodeFromSimpleAddress(simpleAddress);
g_ParamPack.countryId = countryCode;
g_ParamPack.titleId = titleId;
g_ParamPack.platformId = 1;
g_ParamPack.areaId = (simpleAddress >> 8) & 0xff;
MCPHANDLE handle = MCP_Open();
SysProdSettings sysProdSettings;
MCP_GetSysProdSettings(handle, &sysProdSettings);
MCP_Close(handle);
g_ParamPack.regionId = sysProdSettings.platformRegion;
g_ParamPack.accessKey = accessKey;
g_ParamPack.networkRestriction = 0;
g_ParamPack.friendRestriction = 0;
g_ParamPack.ratingRestriction = 18;
g_ParamPack.ratingOrganization = 4; // PEGI ?
uint64 transferrableId;
nn::act::GetTransferableIdEx(&transferrableId, (titleId >> 8) & 0xFFFFF, nn::act::ACT_SLOT_CURRENT);
g_ParamPack.transferableId = transferrableId;
strcpy(g_ParamPack.tzName, "CEMU/Olive"); // Should be nn::act::GetTimeZoneId
g_ParamPack.utcOffset = get_utc_offset();
char paramPackStr[1024];
snprintf(
paramPackStr,
sizeof(paramPackStr),
"\\%s\\%llu\\%s\\%u\\%s\\%u\\%s\\%d\\%s\\%d\\%s\\%d\\%s\\%d\\%s\\%d\\%s\\%d\\%s\\%u\\%s\\%d\\%s\\%llu\\"
"%s\\%s\\%s\\%lld\\",
"title_id",
g_ParamPack.titleId,
"access_key",
g_ParamPack.accessKey,
"platform_id",
g_ParamPack.platformId,
"region_id",
g_ParamPack.regionId,
"language_id",
g_ParamPack.languageId,
"country_id",
g_ParamPack.countryId,
"area_id",
g_ParamPack.areaId,
"network_restriction",
g_ParamPack.networkRestriction,
"friend_restriction",
g_ParamPack.friendRestriction,
"rating_restriction",
g_ParamPack.ratingRestriction,
"rating_organization",
g_ParamPack.ratingOrganization,
"transferable_id",
g_ParamPack.transferableId,
"tz_name",
g_ParamPack.tzName,
"utc_offset",
g_ParamPack.utcOffset);
std::string encodedParamPack = NCrypto::base64Encode(paramPackStr, strnlen(paramPackStr, 1024));
memset(&g_ParamPack.encodedParamPack, 0, sizeof(g_ParamPack.encodedParamPack));
memcpy(&g_ParamPack.encodedParamPack, encodedParamPack.data(), encodedParamPack.size());
return OLV_RESULT_SUCCESS;
}
sint32 MakeDiscoveryRequest_AsyncRequestImpl(CurlRequestHelper& req, const char* reqUrl)
{
bool reqResult = req.submitRequest();
long httpCode = 0;
curl_easy_getinfo(req.getCURL(), CURLINFO_RESPONSE_CODE, &httpCode);
if (!reqResult)
{
cemuLog_log(LogType::Force, "Failed request: {} ({})", reqUrl, httpCode);
if (!(httpCode >= 400))
return OLV_RESULT_FAILED_REQUEST;
}
pugi::xml_document doc;
if (!doc.load_buffer(req.getReceivedData().data(), req.getReceivedData().size()))
{
cemuLog_log(LogType::Force, fmt::format("Invalid XML in discovery service response"));
return OLV_RESULT_INVALID_XML;
}
sint32 responseError = CheckOliveResponse(doc);
if (responseError < 0)
return responseError;
if (httpCode != 200)
return OLV_RESULT_STATUS(httpCode + 4000);
/*
<result>
<has_error>0</has_error>
<version>1</version>
<endpoint>
<host>api.olv.pretendo.cc</host>
<api_host>api.olv.pretendo.cc</api_host>
<portal_host>portal.olv.pretendo.cc</portal_host>
<n3ds_host>ctr.olv.pretendo.cc</n3ds_host>
</endpoint>
</result>
*/
pugi::xml_node resultNode = doc.child("result");
if (!resultNode)
{
cemuLog_log(LogType::Force, "Discovery response doesn't contain <result>");
return OLV_RESULT_INVALID_XML;
}
pugi::xml_node endpointNode = resultNode.child("endpoint");
if (!endpointNode)
{
cemuLog_log(LogType::Force, "Discovery response doesn't contain <result><endpoint>");
return OLV_RESULT_INVALID_XML;
}
// Yes it only uses <host> and <portal_host>
std::string_view host = endpointNode.child_value("host");
std::string_view portal_host = endpointNode.child_value("portal_host");
snprintf(g_DiscoveryResults.apiEndpoint, sizeof(g_DiscoveryResults.apiEndpoint), "https://%s", host.data());
snprintf(g_DiscoveryResults.portalEndpoint, sizeof(g_DiscoveryResults.portalEndpoint), "https://%s", portal_host.data());
return OLV_RESULT_SUCCESS;
}
sint32 MakeDiscoveryRequest_AsyncRequest(CurlRequestHelper& req, const char* reqUrl, coreinit::OSEvent* requestDoneEvent)
{
sint32 res = MakeDiscoveryRequest_AsyncRequestImpl(req, reqUrl);
coreinit::OSSignalEvent(requestDoneEvent);
return res;
}
sint32 MakeDiscoveryRequest()
{
// =============================================================================
// Discovery request | https://discovery.olv.nintendo.net/v1/endpoint
// =============================================================================
CurlRequestHelper req;
std::string requestUrl;
switch (ActiveSettings::GetNetworkService())
{
case NetworkService::Pretendo:
requestUrl = PretendoURLs::OLVURL;
break;
case NetworkService::Custom:
requestUrl = GetNetworkConfig().urls.OLV.GetValue();
break;
case NetworkService::Nintendo:
default:
requestUrl = NintendoURLs::OLVURL;
break;
}
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL);
std::future<sint32> requestRes = std::async(std::launch::async, MakeDiscoveryRequest_AsyncRequest, std::ref(req), requestUrl.c_str(), requestDoneEvent.GetPointer());
coreinit::OSWaitEvent(requestDoneEvent);
return requestRes.get();
}
sint32 Initialize(nn::olv::InitializeParam* pParam)
{
if (g_IsInitialized)
return OLV_RESULT_ALREADY_INITIALIZED;
if (!pParam->m_Work)
{
g_IsInitialized = false;
return OLV_RESULT_INVALID_PTR;
}
if (pParam->m_WorkSize < 0x10000)
{
g_IsInitialized = false;
return OLV_RESULT_INVALID_SIZE;
}
uint32_t accessKey;
int32_t olvAccessKeyStatus = GetOlvAccessKey(&accessKey);
if (olvAccessKeyStatus < 0)
{
g_IsInitialized = false;
return olvAccessKeyStatus;
}
uint64_t tid = CafeSystem::GetForegroundTitleId();
int32_t createParamPackResult = CreateParamPack(tid, accessKey);
if (createParamPackResult < 0)
{
g_IsInitialized = false;
return createParamPackResult;
}
g_IsInitialized = true;
if ((pParam->m_Flags & InitializeParam::FLAG_OFFLINE_MODE) == 0)
{
g_IsOnlineMode = true;
independentServiceToken_t token;
sint32 res = (sint32)nn::act::AcquireIndependentServiceToken(&token, OLV_CLIENT_ID, 0);
if (res < 0)
{
g_IsInitialized = false;
return res;
}
// Assuming we're always a production WiiU (non-dev)
uint32 uniqueId = (CafeSystem::GetForegroundTitleId() >> 8) & 0xFFFFF;
char versionBuffer[32];
snprintf(versionBuffer, sizeof(versionBuffer), "%d.%d.%d", OLV_VERSION_MAJOR, OLV_VERSION_MINOR, OLV_VERSION_PATCH);
snprintf(g_DiscoveryResults.userAgent, sizeof(g_DiscoveryResults.userAgent), "%s/%s-%s/%d", "WiiU", "POLV", versionBuffer, uniqueId);
memcpy(g_DiscoveryResults.serviceToken, token.token, sizeof(g_DiscoveryResults.serviceToken));
sint32 discoveryRes = MakeDiscoveryRequest();
if (discoveryRes < 0)
g_IsInitialized = false;
return discoveryRes;
}
return OLV_RESULT_SUCCESS;
}
namespace Report
{
uint32 GetReportTypes()
{
return g_ReportTypes;
}
void SetReportTypes(uint32 reportTypes)
{
g_ReportTypes = reportTypes | 0x1000;
}
}
bool IsInitialized()
{
return g_IsInitialized;
}
}
}

View File

@ -0,0 +1,128 @@
#pragma once
#include "Cemu/ncrypto/ncrypto.h"
#include "config/ActiveSettings.h"
#include "Cafe/OS/libs/nn_olv/nn_olv_Common.h"
#include "Cafe/OS/libs/coreinit/coreinit_MCP.h"
namespace nn
{
namespace olv
{
class InitializeParam
{
public:
static const inline uint32 FLAG_OFFLINE_MODE = (1 << 0);
InitializeParam()
{
this->m_Flags = 0;
this->m_ReportTypes = 7039;
this->m_SysArgsSize = 0;
this->m_Work = MEMPTR<uint8_t>(nullptr);
this->m_SysArgs = MEMPTR<const void>(nullptr);
this->m_WorkSize = 0;
}
static InitializeParam* __ctor(InitializeParam* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) InitializeParam();
}
sint32 SetFlags(uint32 flags)
{
this->m_Flags = flags;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetFlags(InitializeParam* _this, uint32 flags)
{
return _this->SetFlags(flags);
}
sint32 SetWork(MEMPTR<uint8_t> pWorkData, uint32 workDataSize)
{
if (!pWorkData)
return OLV_RESULT_INVALID_PTR;
if (workDataSize < 0x10000)
return OLV_RESULT_NOT_ENOUGH_SIZE;
this->m_Work = pWorkData;
this->m_WorkSize = workDataSize;
return OLV_RESULT_SUCCESS;
}
static uint32 __SetWork(InitializeParam* _this, MEMPTR<uint8> pWorkData, uint32 workDataSize)
{
return _this->SetWork(pWorkData, workDataSize);
}
sint32 SetReportTypes(uint32 reportTypes)
{
this->m_ReportTypes = reportTypes;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetReportTypes(InitializeParam* _this, uint32 reportTypes)
{
return _this->SetReportTypes(reportTypes);
}
sint32 SetSysArgs(MEMPTR<const void> pSysArgs, uint32 sysArgsSize)
{
if (!pSysArgs)
return OLV_RESULT_INVALID_PTR;
if (!sysArgsSize)
return OLV_RESULT_INVALID_PARAMETER;
this->m_SysArgs = pSysArgs;
this->m_SysArgsSize = sysArgsSize;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetSysArgs(InitializeParam* _this, MEMPTR<const void> pSysArgs, uint32 sysArgsSize)
{
return _this->SetSysArgs(pSysArgs, sysArgsSize);
}
uint32be m_Flags;
uint32be m_ReportTypes;
MEMPTR<uint8_t> m_Work;
uint32be m_WorkSize;
MEMPTR<const void> m_SysArgs;
uint32be m_SysArgsSize;
char unk[0x28];
};
static_assert(sizeof(nn::olv::InitializeParam) == 0x40, "sizeof(nn::olv::InitializeParam) != 0x40");
namespace Report
{
uint32 GetReportTypes();
void SetReportTypes(uint32 reportTypes);
}
bool IsInitialized();
sint32 Initialize(nn::olv::InitializeParam* pParam);
static void loadOliveInitializeTypes()
{
cafeExportRegisterFunc(Initialize, "nn_olv", "Initialize__Q2_2nn3olvFPCQ3_2nn3olv15InitializeParam", LogType::None);
cafeExportRegisterFunc(IsInitialized, "nn_olv", "IsInitialized__Q2_2nn3olvFv", LogType::None);
cafeExportRegisterFunc(Report::GetReportTypes, "nn_olv", "GetReportTypes__Q3_2nn3olv6ReportFv", LogType::None);
cafeExportRegisterFunc(Report::SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv6ReportFUi", LogType::None);
cafeExportRegisterFunc(InitializeParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv15InitializeParamFv", LogType::None);
cafeExportRegisterFunc(InitializeParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv15InitializeParamFUi", LogType::None);
cafeExportRegisterFunc(InitializeParam::__SetWork, "nn_olv", "SetWork__Q3_2nn3olv15InitializeParamFPUcUi", LogType::None);
cafeExportRegisterFunc(InitializeParam::__SetReportTypes, "nn_olv", "SetReportTypes__Q3_2nn3olv15InitializeParamFUi", LogType::None);
cafeExportRegisterFunc(InitializeParam::__SetSysArgs, "nn_olv", "SetSysArgs__Q3_2nn3olv15InitializeParamFPCvUi", LogType::None);
}
}
}

View File

@ -0,0 +1,304 @@
#include "nn_olv_UploadCommunityTypes.h"
#include <algorithm>
namespace nn
{
namespace olv
{
sint32 UploadCommunityData_AsyncRequestImpl(CurlRequestHelper& req, const char* reqUrl,
UploadedCommunityData* pOutData, UploadCommunityDataParam const* pParam);
sint32 UploadCommunityData_AsyncRequest(CurlRequestHelper& req, const char* reqUrl, coreinit::OSEvent* requestDoneEvent,
UploadedCommunityData* pOutData, UploadCommunityDataParam const* pParam
)
{
sint32 res = UploadCommunityData_AsyncRequestImpl(req, reqUrl, pOutData, pParam);
coreinit::OSSignalEvent(requestDoneEvent);
return res;
}
sint32 UploadCommunityData(UploadedCommunityData* pOutData, UploadCommunityDataParam const* pParam)
{
if (!nn::olv::g_IsInitialized)
return OLV_RESULT_NOT_INITIALIZED;
if (!nn::olv::g_IsOnlineMode)
return OLV_RESULT_OFFLINE_MODE_REQUEST;
if (!pParam)
return OLV_RESULT_INVALID_PTR;
if (pOutData)
UploadedCommunityData::Clean(pOutData);
char requestUrl[512];
if (pParam->flags & UploadCommunityDataParam::FLAG_DELETION)
{
if (!pParam->communityId)
return OLV_RESULT_INVALID_PARAMETER;
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.delete", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
}
else
{
if (pParam->communityId)
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
else
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities", g_DiscoveryResults.apiEndpoint);
}
CurlRequestHelper req;
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL);
std::future<sint32> requestRes = std::async(std::launch::async, UploadCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam);
coreinit::OSWaitEvent(requestDoneEvent);
return requestRes.get();
}
sint32 UploadCommunityData(UploadCommunityDataParam const* pParam)
{
return UploadCommunityData(nullptr, pParam);
}
sint32 UploadCommunityData_AsyncRequestImpl(CurlRequestHelper& req, const char* reqUrl,
UploadedCommunityData* pOutData, UploadCommunityDataParam const* pParam)
{
sint32 res = OLV_RESULT_SUCCESS;
std::string base64icon;
std::string form_name;
std::string form_desc;
std::string form_searchKey[5];
std::string encodedAppData;
uint8* encodedIcon = nullptr;
struct curl_httppost* post = nullptr;
struct curl_httppost* last = nullptr;
try
{
if (!pParam->iconData.IsNull())
{
encodedIcon = new uint8[pParam->iconDataLen];
if (encodedIcon)
{
sint32 iconEncodeRes = EncodeTGA(pParam->iconData.GetPtr(), pParam->iconDataLen, encodedIcon, pParam->iconDataLen, TGACheckType::CHECK_COMMUNITY_ICON);
if (iconEncodeRes <= 0)
{
delete[] encodedIcon;
return OLV_RESULT_NOT_ENOUGH_SIZE; // ?
}
base64icon = NCrypto::base64Encode(encodedIcon, iconEncodeRes);
res = olv_curlformcode_to_error(
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "icon",
CURLFORM_PTRCONTENTS, base64icon.data(),
CURLFORM_CONTENTSLENGTH, base64icon.size(),
CURLFORM_END)
);
if (res < 0)
throw std::runtime_error("curl_formadd() error! - icon");
}
}
if (pParam->titleText[0])
{
form_name = StringHelpers::ToUtf8((const uint16be*)pParam->titleText, 127);
res = olv_curlformcode_to_error(
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "name",
CURLFORM_PTRCONTENTS, form_name.data(),
CURLFORM_CONTENTSLENGTH, form_name.size(),
CURLFORM_END)
);
if (res < 0)
throw std::runtime_error("curl_formadd() error! - name");
}
if (pParam->description[0])
{
form_desc = StringHelpers::ToUtf8((const uint16be*)pParam->description, 255);
res = olv_curlformcode_to_error(
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "description",
CURLFORM_PTRCONTENTS, form_desc.data(),
CURLFORM_CONTENTSLENGTH, form_desc.size(),
CURLFORM_END)
);
if (res < 0)
throw std::runtime_error("curl_formadd() error! - description");
}
for (int i = 0; i < 5; i++)
{
if (pParam->searchKeys[i][0])
{
form_searchKey[i] = StringHelpers::ToUtf8((const uint16be*)pParam->searchKeys[i], 151);
res = olv_curlformcode_to_error(
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "search_key",
CURLFORM_PTRCONTENTS, form_searchKey[i].data(),
CURLFORM_CONTENTSLENGTH, form_searchKey[i].size(),
CURLFORM_END)
);
if (res < 0)
throw std::runtime_error("curl_formadd() error! - search_key");
}
}
if (!pParam->appData.IsNull())
{
encodedAppData = NCrypto::base64Encode(pParam->appData.GetPtr(), pParam->appDataLen);
if (encodedAppData.size() < pParam->appDataLen)
res = OLV_RESULT_FATAL(101);
else
{
res = olv_curlformcode_to_error(
curl_formadd(&post, &last,
CURLFORM_COPYNAME, "app_data",
CURLFORM_PTRCONTENTS, encodedAppData.data(),
CURLFORM_CONTENTSLENGTH, encodedAppData.size(),
CURLFORM_END)
);
if (res < 0)
throw std::runtime_error("curl_formadd() error! - app_data");
}
}
}
catch (const std::runtime_error& error)
{
cemuLog_log(LogType::Force, "Error in multipart curl -> {}", error.what());
curl_formfree(post);
if (encodedIcon)
delete[] encodedIcon;
return res;
}
curl_easy_setopt(req.getCURL(), CURLOPT_HTTPPOST, post);
req.setUseMultipartFormData(true);
bool reqResult = req.submitRequest(true);
long httpCode = 0;
curl_easy_getinfo(req.getCURL(), CURLINFO_RESPONSE_CODE, &httpCode);
if (encodedIcon)
delete[] encodedIcon;
if (!reqResult)
{
cemuLog_log(LogType::Force, "Failed request: {} ({})", reqUrl, httpCode);
if (!(httpCode >= 400))
return OLV_RESULT_FAILED_REQUEST;
}
pugi::xml_document doc;
if (!doc.load_buffer(req.getReceivedData().data(), req.getReceivedData().size()))
{
cemuLog_log(LogType::Force, fmt::format("Invalid XML in community upload response"));
return OLV_RESULT_INVALID_XML;
}
sint32 responseError = CheckOliveResponse(doc);
if (responseError < 0)
return responseError;
if (httpCode != 200)
return OLV_RESULT_STATUS(httpCode + 4000);
if (pOutData)
{
std::string_view app_data = doc.select_single_node("//app_data").node().child_value();
std::string_view community_id = doc.select_single_node("//community_id").node().child_value();
std::string_view name = doc.select_single_node("//name").node().child_value();
std::string_view description = doc.select_single_node("//description").node().child_value();
std::string_view pid = doc.select_single_node("//pid").node().child_value();
std::string_view icon = doc.select_single_node("//icon").node().child_value();
if (app_data.size() != 0)
{
auto app_data_bin = NCrypto::base64Decode(app_data);
if (app_data_bin.size() != 0) {
memcpy(pOutData->appData, app_data_bin.data(), std::min(size_t(0x400), app_data_bin.size()));
pOutData->flags |= UploadedCommunityData::FLAG_HAS_APP_DATA;
pOutData->appDataLen = app_data_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 community_id_val = StringHelpers::ToInt64(community_id, -1);
if (community_id_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->communityId = community_id_val;
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
pOutData->titleText[i] = name_utf16.at(i);
pOutData->flags |= UploadedCommunityData::FLAG_HAS_TITLE_TEXT;
pOutData->titleTextMaxLen = name_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)
pOutData->description[i] = description_utf16.at(i);
pOutData->flags |= UploadedCommunityData::FLAG_HAS_DESC_TEXT;
pOutData->descriptionMaxLen = description_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 pid_val = StringHelpers::ToInt64(pid, -1);
if (pid_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->pid = pid_val;
if (icon.size() != 0)
{
auto icon_bin = NCrypto::base64Decode(icon);
if (icon_bin.size() != 0)
{
memcpy(pOutData->iconData, icon_bin.data(), std::min(size_t(0x1002c), icon_bin.size()));
pOutData->flags |= UploadedCommunityData::FLAG_HAS_ICON_DATA;
pOutData->iconDataSize = icon_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
}
return OLV_RESULT_SUCCESS;
}
}
}

View File

@ -0,0 +1,432 @@
#pragma once
#include "Cemu/ncrypto/ncrypto.h"
#include "config/ActiveSettings.h"
#include "Cafe/OS/libs/nn_olv/nn_olv_Common.h"
namespace nn
{
namespace olv
{
class UploadedCommunityData
{
public:
static const inline uint32 FLAG_HAS_TITLE_TEXT = (1 << 0);
static const inline uint32 FLAG_HAS_DESC_TEXT = (1 << 1);
static const inline uint32 FLAG_HAS_APP_DATA = (1 << 2);
static const inline uint32 FLAG_HAS_ICON_DATA = (1 << 3);
UploadedCommunityData()
{
this->titleTextMaxLen = 0;
this->appDataLen = 0;
this->descriptionMaxLen = 0;
this->pid = 0;
this->communityId = 0;
this->flags = 0;
this->iconDataSize = 0;
}
static UploadedCommunityData* __ctor(UploadedCommunityData* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) UploadedCommunityData();
}
static UploadedCommunityData* Clean(UploadedCommunityData* data)
{
data->appDataLen = 0;
data->pid = 0;
data->titleText[0] = 0;
data->description[0] = 0;
data->appData[0] = 0;
data->titleTextMaxLen = 0;
data->iconData[0] = 0;
data->descriptionMaxLen = 0;
data->communityId = 0;
data->flags = 0;
data->iconDataSize = 0;
return data;
}
bool TestFlags(uint32 flags) const
{
return (this->flags & flags) != 0;
}
static bool __TestFlags(UploadedCommunityData* _this, uint32 flags)
{
return _this->TestFlags(flags);
}
uint32 GetCommunityId() const
{
return this->communityId;
}
static uint32 __GetCommunityId(UploadedCommunityData* _this)
{
return _this->GetCommunityId();
}
sint32 GetCommunityCode(char* pBuffer, uint32 bufferSize) const
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize <= 12)
return OLV_RESULT_NOT_ENOUGH_SIZE;
uint32 len = 0;
if (FormatCommunityCode(pBuffer, &len, this->communityId))
return OLV_RESULT_SUCCESS;
return OLV_RESULT_INVALID_PARAMETER;
}
static sint32 __GetCommunityCode(UploadedCommunityData* _this, char* pBuffer, uint32 bufferSize)
{
return _this->GetCommunityCode(pBuffer, bufferSize);
}
uint32 GetOwnerPid() const
{
return this->pid;
}
static uint32 __GetOwnerPid(UploadedCommunityData* _this)
{
return _this->GetOwnerPid();
}
sint32 GetTitleText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_TITLE_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
uint32 readSize = this->titleTextMaxLen;
if (numChars < readSize)
readSize = numChars;
olv_wstrncpy(pBuffer, this->titleText, readSize);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetTitleText(UploadedCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetTitleText(pBuffer, numChars);
}
sint32 GetDescriptionText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_DESC_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
olv_wstrncpy(pBuffer, this->description, numChars);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetDescriptionText(UploadedCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetDescriptionText(pBuffer, numChars);
}
sint32 GetAppData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
uint32 appDataSize = bufferSize;
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize)
{
if (!this->TestFlags(FLAG_HAS_APP_DATA))
return OLV_RESULT_MISSING_DATA;
if (this->appDataLen < appDataSize)
appDataSize = this->appDataLen;
memcpy(pBuffer, this->appData, appDataSize);
if (pOutSize)
*pOutSize = appDataSize;
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetAppData(UploadedCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetAppData(pBuffer, pOutSize, bufferSize);
}
uint32 GetAppDataSize() const
{
if (this->TestFlags(FLAG_HAS_APP_DATA))
return this->appDataLen;
return 0;
}
static uint32 __GetAppDataSize(UploadedCommunityData* _this)
{
return _this->GetAppDataSize();
}
sint32 GetIconData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize < sizeof(this->iconData))
return OLV_RESULT_NOT_ENOUGH_SIZE;
if (!this->TestFlags(FLAG_HAS_ICON_DATA))
return OLV_RESULT_MISSING_DATA;
sint32 decodeRes = DecodeTGA(this->iconData, this->iconDataSize, pBuffer, bufferSize, TGACheckType::CHECK_COMMUNITY_ICON);
if (decodeRes >= 0)
{
if (pOutSize)
*pOutSize = (uint32)decodeRes;
return OLV_RESULT_SUCCESS;
}
if (pOutSize)
*pOutSize = 0;
if (decodeRes == -1)
cemuLog_log(LogType::Force, "OLIVE - icon uncompress failed.\n");
else if (decodeRes == -2)
cemuLog_log(LogType::Force, "OLIVE - icon decode error. NOT TGA.\n");
return OLV_RESULT_INVALID_TEXT_FIELD;
}
static sint32 __GetIconData(UploadedCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetIconData(pBuffer, pOutSize, bufferSize);
}
public:
uint32be flags;
uint32be communityId;
uint32be pid;
char16_t titleText[128];
uint32be titleTextMaxLen;
char16_t description[256];
uint32be descriptionMaxLen;
uint8 appData[1024];
uint32be appDataLen;
uint8 iconData[65580];
uint32be iconDataSize;
uint8 unk[6328];
};
static_assert(sizeof(nn::olv::UploadedCommunityData) == 0x12000, "sizeof(nn::olv::UploadedCommunityData) != 0x12000");
class UploadCommunityDataParam
{
public:
static const inline uint32 FLAG_DELETION = (1 << 0);
UploadCommunityDataParam()
{
this->appDataLen = 0;
this->communityId = 0;
this->titleId = 0;
this->iconData = MEMPTR<uint8>(nullptr);
this->appData = MEMPTR<uint8>(nullptr);
this->iconDataLen = 0;
this->flags = 0;
memset(this->titleText, 0, sizeof(this->titleText));
memset(this->description, 0, sizeof(this->description));
int v2 = 0;
do
memset(this->searchKeys[v2++], 0, sizeof(this->searchKeys[v2++]));
while (v2 < 5);
}
static UploadCommunityDataParam* __ctor(UploadCommunityDataParam* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) UploadCommunityDataParam();
}
sint32 SetFlags(uint32 flags)
{
this->flags = flags;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetFlags(UploadCommunityDataParam* _this, uint32 flags)
{
return _this->SetFlags(flags);
}
sint32 SetCommunityId(uint32 communityId)
{
if (communityId == -1)
return OLV_RESULT_INVALID_PARAMETER;
this->communityId = communityId;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetCommunityId(UploadCommunityDataParam* _this, uint32 communityId)
{
return _this->SetCommunityId(communityId);
}
sint32 SetAppData(MEMPTR<uint8> pBuffer, uint32 bufferSize)
{
if (!pBuffer.IsNull())
{
if (bufferSize - 1 >= 0x400)
return OLV_RESULT_NOT_ENOUGH_SIZE;
this->appData = pBuffer;
this->appDataLen = bufferSize;
}
else
{
this->appData = MEMPTR<uint8>(nullptr);
this->appDataLen = 0;
}
return OLV_RESULT_SUCCESS;
}
static sint32 __SetAppData(UploadCommunityDataParam* _this, MEMPTR<uint8> pBuffer, uint32 bufferSize)
{
return _this->SetAppData(pBuffer, bufferSize);
}
sint32 SetTitleText(char16_t const* pText)
{
if (pText)
return olv_copy_wstr(this->titleText, pText, 127, 128);
memset(this->titleText, 0, sizeof(this->titleText));
return OLV_RESULT_SUCCESS;
}
static sint32 __SetTitleText(UploadCommunityDataParam* _this, char16_t const* pText)
{
return _this->SetTitleText(pText);
}
sint32 SetDescriptionText(char16_t const* pText)
{
if (pText)
return olv_copy_wstr(this->description, pText, 255, 256);
memset(this->description, 0, sizeof(this->description));
return OLV_RESULT_SUCCESS;
}
static sint32 __SetDescriptionText(UploadCommunityDataParam* _this, char16_t const* pText)
{
return _this->SetDescriptionText(pText);
}
sint32 SetIconData(MEMPTR<uint8> pBuffer, uint32 bufferSize)
{
if (!pBuffer.IsNull())
{
if (bufferSize)
{
if (bufferSize - 0x10012 < 0x1B)
{
if (CheckTGA(pBuffer.GetPtr(), bufferSize, TGACheckType::CHECK_COMMUNITY_ICON))
{
this->iconData = pBuffer;
this->iconDataLen = bufferSize;
return OLV_RESULT_SUCCESS;
}
else
{
cemuLog_log(LogType::Force, "OLIVE - SetIconData: TGA Check Failed.\n");
return OLV_RESULT_INVALID_DATA;
}
}
else
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
else
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
else
{
this->iconData = MEMPTR<uint8>(nullptr);
this->iconDataLen = 0;
return OLV_RESULT_SUCCESS;
}
}
static sint32 __SetIconData(UploadCommunityDataParam* _this, MEMPTR<uint8> pBuffer, uint32 bufferSize)
{
return _this->SetIconData(pBuffer, bufferSize);
}
public:
uint32be flags;
sint32be ___padding_0c;
uint64be titleId;
uint32be communityId;
char16_t titleText[128];
char16_t description[256];
char16_t searchKeys[5][152];
MEMPTR<uint8_t> appData;
uint32be appDataLen;
MEMPTR<uint8_t> iconData;
uint32be iconDataLen;
char unk3[1772];
};
static_assert(sizeof(nn::olv::UploadCommunityDataParam) == 0x1000, "sizeof(nn::olv::UploadCommunityDataParam) != 0x1000");
sint32 UploadCommunityData(UploadCommunityDataParam const* pParam);
sint32 UploadCommunityData(UploadedCommunityData* pOutData, UploadCommunityDataParam const* pParam);
static void loadOliveUploadCommunityTypes()
{
cafeExportRegisterFunc(UploadedCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv21UploadedCommunityDataFv", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv21UploadedCommunityDataCFUi", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv21UploadedCommunityDataCFPcUi", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv21UploadedCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv21UploadedCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv21UploadedCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv24UploadCommunityDataParamFv", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv24UploadCommunityDataParamFUi", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetAppData, "nn_olv", "SetAppData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetTitleText, "nn_olv", "SetTitleText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetDescriptionText, "nn_olv", "SetDescriptionText__Q3_2nn3olv24UploadCommunityDataParamFPCw", LogType::None);
cafeExportRegisterFunc(UploadCommunityDataParam::__SetIconData, "nn_olv", "SetIconData__Q3_2nn3olv24UploadCommunityDataParamFPCUcUi", LogType::None);
cafeExportRegisterFunc((sint32(*)(UploadCommunityDataParam const*))UploadCommunityData,
"nn_olv", "UploadCommunityData__Q2_2nn3olvFPCQ3_2nn3olv24UploadCommunityDataParam", LogType::None);
cafeExportRegisterFunc((sint32(*)(UploadedCommunityData *, UploadCommunityDataParam const*))UploadCommunityData,
"nn_olv", "UploadCommunityData__Q2_2nn3olvFPQ3_2nn3olv21UploadedCommunityDataPCQ3_2nn3olv24UploadCommunityDataParam", LogType::None);
}
}
}

View File

@ -0,0 +1,169 @@
#include "nn_olv_UploadFavoriteTypes.h"
#include <algorithm>
namespace nn
{
namespace olv
{
sint32 UploadFavoriteToCommunityData_AsyncRequestImpl(CurlRequestHelper& req, const char* reqUrl,
UploadedFavoriteToCommunityData* pOutData, const UploadFavoriteToCommunityDataParam* pParam
);
sint32 UploadFavoriteToCommunityData_AsyncRequest(CurlRequestHelper& req, const char* reqUrl, coreinit::OSEvent* requestDoneEvent,
UploadedFavoriteToCommunityData* pOutData, const UploadFavoriteToCommunityDataParam* pParam
)
{
sint32 res = UploadFavoriteToCommunityData_AsyncRequestImpl(req, reqUrl, pOutData, pParam);
coreinit::OSSignalEvent(requestDoneEvent);
return res;
}
sint32 UploadFavoriteToCommunityData(UploadedFavoriteToCommunityData* pOutData, const UploadFavoriteToCommunityDataParam* pParam)
{
if (!nn::olv::g_IsInitialized)
return OLV_RESULT_NOT_INITIALIZED;
if (!nn::olv::g_IsOnlineMode)
return OLV_RESULT_OFFLINE_MODE_REQUEST;
if (!pParam)
return OLV_RESULT_INVALID_PTR;
if (pOutData)
UploadedFavoriteToCommunityData::Clean(pOutData);
char requestUrl[512];
if (pParam->flags & UploadFavoriteToCommunityDataParam::FLAG_DELETION)
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.unfavorite", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
else
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.favorite", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
CurlRequestHelper req;
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
coreinit::OSInitEvent(requestDoneEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_MANUAL);
std::future<sint32> requestRes = std::async(std::launch::async, UploadFavoriteToCommunityData_AsyncRequest, std::ref(req), requestUrl, requestDoneEvent.GetPointer(), pOutData, pParam);
coreinit::OSWaitEvent(requestDoneEvent);
return requestRes.get();
}
sint32 UploadFavoriteToCommunityData(const UploadFavoriteToCommunityDataParam* pParam)
{
return UploadFavoriteToCommunityData(nullptr, pParam);
}
sint32 UploadFavoriteToCommunityData_AsyncRequestImpl(CurlRequestHelper& req, const char* reqUrl,
UploadedFavoriteToCommunityData* pOutData, const UploadFavoriteToCommunityDataParam* pParam
)
{
bool reqResult = req.submitRequest(true);
long httpCode = 0;
curl_easy_getinfo(req.getCURL(), CURLINFO_RESPONSE_CODE, &httpCode);
if (!reqResult)
{
cemuLog_log(LogType::Force, "Failed request: {} ({})", reqUrl, httpCode);
if (!(httpCode >= 400))
return OLV_RESULT_FAILED_REQUEST;
}
pugi::xml_document doc;
if (!doc.load_buffer(req.getReceivedData().data(), req.getReceivedData().size()))
{
cemuLog_log(LogType::Force, fmt::format("Invalid XML in community favorite upload response"));
return OLV_RESULT_INVALID_XML;
}
sint32 responseError = CheckOliveResponse(doc);
if (responseError < 0)
return responseError;
if (httpCode != 200)
return OLV_RESULT_STATUS(httpCode + 4000);
if (pOutData)
{
std::string_view app_data = doc.select_single_node("//app_data").node().child_value();
std::string_view community_id = doc.select_single_node("//community_id").node().child_value();
std::string_view name = doc.select_single_node("//name").node().child_value();
std::string_view description = doc.select_single_node("//description").node().child_value();
std::string_view pid = doc.select_single_node("//pid").node().child_value();
std::string_view icon = doc.select_single_node("//icon").node().child_value();
if (app_data.size() != 0)
{
auto app_data_bin = NCrypto::base64Decode(app_data);
if (app_data_bin.size() != 0)
{
memcpy(pOutData->appData, app_data_bin.data(), std::min(size_t(0x400), app_data_bin.size()));
pOutData->flags |= UploadedFavoriteToCommunityData::FLAG_HAS_APP_DATA;
pOutData->appDataLen = app_data_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 community_id_val = StringHelpers::ToInt64(community_id, -1);
if (community_id_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->communityId = community_id_val;
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
pOutData->titleText[i] = name_utf16.at(i);
pOutData->flags |= UploadedFavoriteToCommunityData::FLAG_HAS_TITLE_TEXT;
pOutData->titleTextMaxLen = name_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)
pOutData->description[i] = description_utf16.at(i);
pOutData->flags |= UploadedFavoriteToCommunityData::FLAG_HAS_DESC_TEXT;
pOutData->descriptionMaxLen = description_utf16.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
sint64 pid_val = StringHelpers::ToInt64(pid, -1);
if (pid_val == -1)
return OLV_RESULT_INVALID_INTEGER_FIELD;
pOutData->pid = pid_val;
if (icon.size() != 0)
{
auto icon_bin = NCrypto::base64Decode(icon);
if (icon_bin.size() != 0)
{
memcpy(pOutData->iconData, icon_bin.data(), std::min(size_t(0x1002c), icon_bin.size()));
pOutData->flags |= UploadedFavoriteToCommunityData::FLAG_HAS_ICON_DATA;
pOutData->iconDataSize = icon_bin.size();
}
else
return OLV_RESULT_INVALID_TEXT_FIELD;
}
}
return OLV_RESULT_SUCCESS;
}
}
}

View File

@ -0,0 +1,342 @@
#pragma once
#include "Cemu/ncrypto/ncrypto.h"
#include "config/ActiveSettings.h"
#include "Cafe/OS/libs/nn_olv/nn_olv_Common.h"
namespace nn
{
namespace olv
{
class UploadedFavoriteToCommunityData
{
public:
static const inline uint32 FLAG_HAS_TITLE_TEXT = (1 << 0);
static const inline uint32 FLAG_HAS_DESC_TEXT = (1 << 1);
static const inline uint32 FLAG_HAS_APP_DATA = (1 << 2);
static const inline uint32 FLAG_HAS_ICON_DATA = (1 << 3);
UploadedFavoriteToCommunityData()
{
this->titleTextMaxLen = 0;
this->appDataLen = 0;
this->descriptionMaxLen = 0;
this->pid = 0;
this->communityId = 0;
this->flags = 0;
this->iconDataSize = 0;
}
static UploadedFavoriteToCommunityData* __ctor(UploadedFavoriteToCommunityData* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) UploadedFavoriteToCommunityData();
}
static UploadedFavoriteToCommunityData* Clean(UploadedFavoriteToCommunityData* data)
{
data->appDataLen = 0;
data->pid = 0;
data->titleText[0] = 0;
data->description[0] = 0;
data->appData[0] = 0;
data->titleTextMaxLen = 0;
data->iconData[0] = 0;
data->descriptionMaxLen = 0;
data->communityId = 0;
data->flags = 0;
data->iconDataSize = 0;
return data;
}
bool TestFlags(uint32 flags) const
{
return (this->flags & flags) != 0;
}
static bool __TestFlags(UploadedFavoriteToCommunityData* _this, uint32 flags)
{
return _this->TestFlags(flags);
}
uint32 GetCommunityId() const
{
return this->communityId;
}
static uint32 __GetCommunityId(UploadedFavoriteToCommunityData* _this)
{
return _this->GetCommunityId();
}
sint32 GetCommunityCode(char* pBuffer, uint32 bufferSize) const
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize <= 12)
return OLV_RESULT_NOT_ENOUGH_SIZE;
uint32 len = 0;
if (FormatCommunityCode(pBuffer, &len, this->communityId))
return OLV_RESULT_SUCCESS;
return OLV_RESULT_INVALID_PARAMETER;
}
static sint32 __GetCommunityCode(UploadedFavoriteToCommunityData* _this, char* pBuffer, uint32 bufferSize)
{
return _this->GetCommunityCode(pBuffer, bufferSize);
}
uint32 GetOwnerPid() const
{
return this->pid;
}
static uint32 __GetOwnerPid(UploadedFavoriteToCommunityData* _this)
{
return _this->GetOwnerPid();
}
sint32 GetTitleText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_TITLE_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
uint32 readSize = this->titleTextMaxLen;
if (numChars < readSize)
readSize = numChars;
olv_wstrncpy(pBuffer, this->titleText, readSize);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetTitleText(UploadedFavoriteToCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetTitleText(pBuffer, numChars);
}
sint32 GetDescriptionText(char16_t* pBuffer, uint32 numChars)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (numChars)
{
if (!this->TestFlags(FLAG_HAS_DESC_TEXT))
return OLV_RESULT_MISSING_DATA;
memset(pBuffer, 0, 2 * numChars);
olv_wstrncpy(pBuffer, this->description, numChars);
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetDescriptionText(UploadedFavoriteToCommunityData* _this, char16_t* pBuffer, uint32 numChars)
{
return _this->GetDescriptionText(pBuffer, numChars);
}
sint32 GetAppData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
uint32 appDataSize = bufferSize;
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize)
{
if (!this->TestFlags(FLAG_HAS_APP_DATA))
return OLV_RESULT_MISSING_DATA;
if (this->appDataLen < appDataSize)
appDataSize = this->appDataLen;
memcpy(pBuffer, this->appData, appDataSize);
if (pOutSize)
*pOutSize = appDataSize;
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_NOT_ENOUGH_SIZE;
}
static sint32 __GetAppData(UploadedFavoriteToCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetAppData(pBuffer, pOutSize, bufferSize);
}
uint32 GetAppDataSize() const
{
if (this->TestFlags(FLAG_HAS_APP_DATA))
return this->appDataLen;
return 0;
}
static uint32 __GetAppDataSize(UploadedFavoriteToCommunityData* _this)
{
return _this->GetAppDataSize();
}
sint32 GetIconData(uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
if (!pBuffer)
return OLV_RESULT_INVALID_PTR;
if (bufferSize < sizeof(this->iconData))
return OLV_RESULT_NOT_ENOUGH_SIZE;
if (!this->TestFlags(FLAG_HAS_ICON_DATA))
return OLV_RESULT_MISSING_DATA;
sint32 decodeRes = DecodeTGA(this->iconData, this->iconDataSize, pBuffer, bufferSize, TGACheckType::CHECK_COMMUNITY_ICON);
if (decodeRes >= 0)
{
if (pOutSize)
*pOutSize = (uint32)decodeRes;
return OLV_RESULT_SUCCESS;
}
if (pOutSize)
*pOutSize = 0;
if (decodeRes == -1)
cemuLog_log(LogType::Force, "OLIVE - icon uncompress failed.\n");
else if (decodeRes == -2)
cemuLog_log(LogType::Force, "OLIVE - icon decode error. NOT TGA.\n");
return OLV_RESULT_INVALID_TEXT_FIELD;
}
static sint32 __GetIconData(UploadedFavoriteToCommunityData* _this, uint8* pBuffer, uint32be* pOutSize, uint32 bufferSize)
{
return _this->GetIconData(pBuffer, pOutSize, bufferSize);
}
public:
uint32be flags;
uint32be communityId;
uint32be pid;
char16_t titleText[128];
uint32be titleTextMaxLen;
char16_t description[256];
uint32be descriptionMaxLen;
uint8 appData[1024];
uint32be appDataLen;
uint8 iconData[65580];
uint32be iconDataSize;
uint8 unk[6328];
};
static_assert(sizeof(nn::olv::UploadedFavoriteToCommunityData) == 0x12000, "sizeof(nn::olv::UploadedFavoriteToCommunityData) != 0x12000");
class UploadFavoriteToCommunityDataParam
{
public:
static const inline uint32 FLAG_DELETION = (1 << 0);
UploadFavoriteToCommunityDataParam()
{
this->communityId = 0;
this->flags = 0;
}
static UploadFavoriteToCommunityDataParam* __ctor(UploadFavoriteToCommunityDataParam* _this)
{
if (!_this)
{
assert_dbg(); // DO NOT CONTINUE, SHOULD NEVER HAPPEN
return nullptr;
}
else
return new (_this) UploadFavoriteToCommunityDataParam();
}
sint32 SetFlags(uint32 flags)
{
this->flags = flags;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetFlags(UploadFavoriteToCommunityDataParam* _this, uint32 flags)
{
return _this->SetFlags(flags);
}
sint32 SetCommunityCode(const char* pBuffer)
{
if (strnlen(pBuffer, 13) != 12)
return OLV_RESULT_INVALID_TEXT_FIELD;
uint32_t id;
if (GetCommunityIdFromCode(&id, pBuffer))
{
this->communityId = id;
return OLV_RESULT_SUCCESS;
}
return OLV_RESULT_STATUS(1901);
}
static sint32 __SetCommunityCode(UploadFavoriteToCommunityDataParam* _this, char* pBuffer)
{
return _this->SetCommunityCode(pBuffer);
}
sint32 SetCommunityId(uint32 communityId)
{
if (communityId == -1)
return OLV_RESULT_INVALID_PARAMETER;
this->communityId = communityId;
return OLV_RESULT_SUCCESS;
}
static sint32 __SetCommunityId(UploadFavoriteToCommunityDataParam* _this, uint32 communityId)
{
return _this->SetCommunityId(communityId);
}
public:
uint32be flags;
uint32be communityId;
uint8 unk[54]; // Unused
};
static_assert(sizeof(nn::olv::UploadFavoriteToCommunityDataParam) == 64, "sizeof(nn::olv::UploadFavoriteToCommunityDataParam) != 64");
sint32 UploadFavoriteToCommunityData(const UploadFavoriteToCommunityDataParam* pParam);
sint32 UploadFavoriteToCommunityData(UploadedFavoriteToCommunityData* pOutData, const UploadFavoriteToCommunityDataParam* pParam);
static void loadOliveUploadFavoriteTypes()
{
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__ctor, "nn_olv", "__ct__Q3_2nn3olv31UploadedFavoriteToCommunityDataFv", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFUi", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetCommunityCode, "nn_olv", "GetCommunityCode__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPcUi", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetOwnerPid, "nn_olv", "GetOwnerPid__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetTitleText, "nn_olv", "GetTitleText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetDescriptionText, "nn_olv", "GetDescriptionText__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPwUi", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFv", LogType::None);
cafeExportRegisterFunc(UploadedFavoriteToCommunityData::__GetIconData, "nn_olv", "GetIconData__Q3_2nn3olv31UploadedFavoriteToCommunityDataCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__ctor, "nn_olv", "__ct__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFv", LogType::None);
cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetFlags, "nn_olv", "SetFlags__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::None);
cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityCode, "nn_olv", "SetCommunityCode__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFPCc", LogType::None);
cafeExportRegisterFunc(UploadFavoriteToCommunityDataParam::__SetCommunityId, "nn_olv", "SetCommunityId__Q3_2nn3olv34UploadFavoriteToCommunityDataParamFUi", LogType::None);
cafeExportRegisterFunc((sint32(*)(const UploadFavoriteToCommunityDataParam*))UploadFavoriteToCommunityData,
"nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::None);
cafeExportRegisterFunc((sint32(*)(UploadedFavoriteToCommunityData*, const UploadFavoriteToCommunityDataParam*))UploadFavoriteToCommunityData,
"nn_olv", "UploadFavoriteToCommunityData__Q2_2nn3olvFPQ3_2nn3olv31UploadedFavoriteToCommunityDataPCQ3_2nn3olv34UploadFavoriteToCommunityDataParam", LogType::None);
}
}
}

View File

@ -16,6 +16,8 @@ struct ParsedMetaXml
std::array<std::string, 12> m_short_name;
std::array<std::string, 12> m_publisher;
uint32 m_olv_accesskey;
std::string GetShortName(CafeConsoleLanguage languageId) const
{
return m_short_name[(size_t)languageId].empty() ? m_short_name[(size_t)CafeConsoleLanguage::EN] : m_short_name[(size_t)languageId];
@ -51,6 +53,11 @@ struct ParsedMetaXml
return m_company_code;
}
uint32 GetOlvAccesskey() const
{
return m_olv_accesskey;
}
static ParsedMetaXml* Parse(uint8* xmlData, size_t xmlSize)
{
if (xmlSize == 0)
@ -98,6 +105,8 @@ struct ParsedMetaXml
if (index != -1)
parsedMetaXml->m_publisher[index] = child.text().as_string();
}
else if (boost::starts_with(name, L"olv_accesskey"))
parsedMetaXml->m_olv_accesskey = child.text().as_uint(-1);
}
if (parsedMetaXml->m_title_id == 0)
{

View File

@ -615,6 +615,16 @@ CafeConsoleRegion TitleInfo::GetMetaRegion() const
return CafeConsoleRegion::JPN;
}
uint32 TitleInfo::GetOlvAccesskey() const
{
cemu_assert_debug(m_isValid);
if (m_parsedMetaXml)
return m_parsedMetaXml->GetOlvAccesskey();
cemu_assert_suspicious();
return -1;
}
std::string TitleInfo::GetArgStr() const
{
cemu_assert_debug(m_parsedCosXml);

View File

@ -122,6 +122,7 @@ public:
uint32 GetAppType() const; // from app.xml
std::string GetTitleName() const; // from meta.xml
CafeConsoleRegion GetMetaRegion() const; // from meta.xml
uint32 GetOlvAccesskey() const;
// cos.xml
std::string GetArgStr() const;

View File

@ -67,6 +67,38 @@ CURLcode _sslctx_function_SOAP(CURL* curl, void* sslctx, void* param)
return CURLE_OK;
}
CURLcode _sslctx_function_OLIVE(CURL* curl, void* sslctx, void* param)
{
if (iosuCrypto_addCACertificate(sslctx, 105) == false)
{
cemuLog_log(LogType::Force, "Invalid CA certificate (105)");
cemuLog_log(LogType::Force, "Certificate error");
}
if (iosuCrypto_addClientCertificate(sslctx, 7) == false)
{
cemuLog_log(LogType::Force, "Olive client certificate error");
}
// NSSLAddServerPKIGroups(sslCtx, 3, &x, &y);
{
std::vector<sint16> certGroups = {
100, 101, 102, 103, 104, 105,
1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009,
1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019,
1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029,
1030, 1031, 1032, 1033
};
for (auto& certId : certGroups)
iosuCrypto_addCACertificate(sslctx, certId);
}
SSL_CTX_set_mode((SSL_CTX*)sslctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_verify_depth((SSL_CTX*)sslctx, 2);
SSL_CTX_set_verify((SSL_CTX*)sslctx, SSL_VERIFY_PEER, nullptr);
return CURLE_OK;
}
CurlRequestHelper::CurlRequestHelper()
{
m_curl = curl_easy_init();
@ -122,6 +154,11 @@ void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext)
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
}
else if (sslContext == SERVER_SSL_CONTEXT::OLIVE)
{
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_OLIVE);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
}
else
{
cemu_assert(false);
@ -178,10 +215,12 @@ bool CurlRequestHelper::submitRequest(bool isPost)
// post
if (isPost)
{
if (!m_isUsingMultipartFormData) {
curl_easy_setopt(m_curl, CURLOPT_POST, 1);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_postData.data());
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, m_postData.size());
}
}
else
curl_easy_setopt(m_curl, CURLOPT_POST, 0);

View File

@ -27,6 +27,7 @@ public:
CCS, // ccs.
IDBE, // idbe-wup.
TAGAYA, // tagaya.wup.shop.nintendo.net
OLIVE, // olv.
};
CurlRequestHelper();
@ -50,6 +51,11 @@ public:
return m_receiveBuffer;
}
void setUseMultipartFormData(bool isUsingMultipartFormData)
{
m_isUsingMultipartFormData = isUsingMultipartFormData;
}
private:
static size_t __curlWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
@ -61,6 +67,8 @@ private:
// write callback redirect
bool (*m_cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast);
void* m_writeCallbackUserData{};
bool m_isUsingMultipartFormData = false;
};
class CurlSOAPHelper // todo - make this use CurlRequestHelper

View File

@ -29,6 +29,7 @@ void NetworkConfig::Load(XMLConfigParser& parser)
urls.IDBE = u.get("idbe", NintendoURLs::IDBEURL);
urls.BOSS = u.get("boss", NintendoURLs::BOSSURL);
urls.TAGAYA = u.get("tagaya", NintendoURLs::TAGAYAURL);
urls.OLV = u.get("olv", NintendoURLs::OLVURL);
if (static_cast<NetworkService>(GetConfig().account.active_service.GetValue()) == NetworkService::Custom)
LaunchSettings::ChangeNetworkServiceURL(2);
}

View File

@ -30,6 +30,7 @@ struct NetworkConfig {
ConfigValue<std::string> IDBE;
ConfigValue<std::string> BOSS;
ConfigValue<std::string> TAGAYA;
ConfigValue<std::string> OLV;
}urls{};
public:
@ -50,6 +51,7 @@ struct NintendoURLs {
inline static std::string IDBEURL = "https://idbe-wup.cdn.nintendo.net/icondata";
inline static std::string BOSSURL = "https://npts.app.nintendo.net/p01/tasksheet";
inline static std::string TAGAYAURL = "https://tagaya.wup.shop.nintendo.net/tagaya/versionlist";
inline static std::string OLVURL = "https://discovery.olv.nintendo.net/v1/endpoint";
};
struct PretendoURLs {
@ -62,6 +64,7 @@ struct PretendoURLs {
inline static std::string IDBEURL = "https://idbe-wup.cdn.pretendo.cc/icondata";
inline static std::string BOSSURL = "https://npts.app.pretendo.cc/p01/tasksheet";
inline static std::string TAGAYAURL = "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist";
inline static std::string OLVURL = "https://discovery.olv.pretendo.cc/v1/endpoint";
};
typedef XMLDataConfig<NetworkConfig, &NetworkConfig::Load, &NetworkConfig::Save> XMLNetworkConfig_t;