mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-25 18:46:55 +01:00
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:
parent
1beec40445
commit
a8d157d310
@ -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
|
||||
|
@ -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)
|
||||
|
@ -26,6 +26,7 @@ namespace CafeSystem
|
||||
CafeConsoleRegion GetPlatformRegion();
|
||||
std::string GetForegroundTitleName();
|
||||
std::string GetForegroundTitleArgStr();
|
||||
uint32 GetForegroundTitleOlvAccesskey();
|
||||
|
||||
void ShutdownTitle();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
285
src/Cafe/OS/libs/nn_olv/nn_olv_Common.cpp
Normal file
285
src/Cafe/OS/libs/nn_olv/nn_olv_Common.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
179
src/Cafe/OS/libs/nn_olv/nn_olv_Common.h
Normal file
179
src/Cafe/OS/libs/nn_olv/nn_olv_Common.h
Normal 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);
|
||||
}
|
||||
}
|
231
src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp
Normal file
231
src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
530
src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h
Normal file
530
src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.h
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
312
src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp
Normal file
312
src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
128
src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h
Normal file
128
src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.h
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
304
src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp
Normal file
304
src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
432
src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
Normal file
432
src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
169
src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
Normal file
169
src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
342
src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
Normal file
342
src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,9 +215,11 @@ bool CurlRequestHelper::submitRequest(bool isPost)
|
||||
// post
|
||||
if (isPost)
|
||||
{
|
||||
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());
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user