mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
Move getMiiImage logic to iosu + improvments and fixes
This commit is contained in:
parent
3aa4744a04
commit
d840591872
@ -11,6 +11,7 @@
|
|||||||
#include "Cafe/Account/Account.h"
|
#include "Cafe/Account/Account.h"
|
||||||
#include "config/ActiveSettings.h"
|
#include "config/ActiveSettings.h"
|
||||||
#include "util/helpers/helpers.h"
|
#include "util/helpers/helpers.h"
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
|
||||||
#include "Cemu/napi/napi.h"
|
#include "Cemu/napi/napi.h"
|
||||||
#include "Cemu/ncrypto/ncrypto.h"
|
#include "Cemu/ncrypto/ncrypto.h"
|
||||||
@ -554,6 +555,44 @@ namespace iosu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetMiiImage(uint8 slot, uint32 imageType, void* outBuf, uint32 bufferSize, uint32* outImageSize)
|
||||||
|
{
|
||||||
|
uint32 persistentId = 0;
|
||||||
|
if (!GetPersistentId(slot, &persistentId))
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
|
||||||
|
static constexpr uint32 kMaxMiiImageBytes = 2 * 1024 * 1024;
|
||||||
|
|
||||||
|
const auto filePath = ActiveSettings::GetMlcPath(
|
||||||
|
fmt::format("usr/save/system/act/{:08x}/miiimg{:02d}.dat", persistentId, imageType));
|
||||||
|
|
||||||
|
// reject oversized files before allocating. Prevents loading multi-GB crafted files into RAM
|
||||||
|
std::error_code ec;
|
||||||
|
if (fs::file_size(filePath, ec) > kMaxMiiImageBytes || ec)
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
|
||||||
|
auto fileData = FileStream::LoadIntoMemory(filePath);
|
||||||
|
if (!fileData.has_value())
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
|
||||||
|
// type 0 (FaceIcon) is stored raw; all others are zlib-compressed
|
||||||
|
if (imageType != 0)
|
||||||
|
{
|
||||||
|
fileData = zlibDecompress(*fileData);
|
||||||
|
if (!fileData.has_value())
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileData->size() > kMaxMiiImageBytes)
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
|
||||||
|
*outImageSize = (uint32)fileData->size();
|
||||||
|
if (bufferSize < *outImageSize)
|
||||||
|
return ACTResult_OutOfRange;
|
||||||
|
memcpy(outBuf, fileData->data(), fileData->size());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
nnResult AcquireNexToken(uint8 accountSlot, uint64 titleId, uint16 titleVersion, uint32 serverId, uint8* tokenOut, uint32 tokenLen)
|
nnResult AcquireNexToken(uint8 accountSlot, uint64 titleId, uint16 titleVersion, uint32 serverId, uint8* tokenOut, uint32 tokenLen)
|
||||||
{
|
{
|
||||||
if (accountSlot != ACT_SLOT_CURRENT)
|
if (accountSlot != ACT_SLOT_CURRENT)
|
||||||
|
|||||||
@ -50,6 +50,7 @@ namespace iosu
|
|||||||
bool getScreenname(uint8 slot, uint16 screenname[ACT_NICKNAME_LENGTH]);
|
bool getScreenname(uint8 slot, uint16 screenname[ACT_NICKNAME_LENGTH]);
|
||||||
bool getCountryIndex(uint8 slot, uint32* countryIndex);
|
bool getCountryIndex(uint8 slot, uint32* countryIndex);
|
||||||
bool GetPersistentId(uint8 slot, uint32* persistentId);
|
bool GetPersistentId(uint8 slot, uint32* persistentId);
|
||||||
|
uint32 GetMiiImage(uint8 slot, uint32 imageType, void* outBuf, uint32 bufferSize, uint32* outImageSize);
|
||||||
|
|
||||||
std::string getAccountId2(uint8 slot);
|
std::string getAccountId2(uint8 slot);
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,6 @@
|
|||||||
#include "Cafe/OS/libs/nn_common.h"
|
#include "Cafe/OS/libs/nn_common.h"
|
||||||
#include "Cafe/CafeSystem.h"
|
#include "Cafe/CafeSystem.h"
|
||||||
#include "Common/CafeString.h"
|
#include "Common/CafeString.h"
|
||||||
#include "Common/FileStream.h"
|
|
||||||
#include "config/ActiveSettings.h"
|
|
||||||
#include "util/helpers/helpers.h"
|
|
||||||
|
|
||||||
#define actPrepareRequest() \
|
#define actPrepareRequest() \
|
||||||
StackAllocator<iosuActCemuRequest_t> _buf_actRequest; \
|
StackAllocator<iosuActCemuRequest_t> _buf_actRequest; \
|
||||||
@ -24,6 +21,10 @@ StackAllocator<iosuActCemuRequest_t> _buf_actRequest; \
|
|||||||
iosuActCemuRequest_t* actRequest = _buf_actRequest.GetPointer(); \
|
iosuActCemuRequest_t* actRequest = _buf_actRequest.GetPointer(); \
|
||||||
memset(actRequest, 0, sizeof(iosuActCemuRequest_t));
|
memset(actRequest, 0, sizeof(iosuActCemuRequest_t));
|
||||||
|
|
||||||
|
// Returned by ACT functions when a required output pointer is null (matches native RPL behaviour)
|
||||||
|
static const uint32 ACTResult_NullPointer = BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_ACT, 0x12C80);
|
||||||
|
static const uint32 ACTResult_AccountDoesNotExist = BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_ACT, NN_ACT_RESULT_ACCOUNT_DOES_NOT_EXIST);
|
||||||
|
|
||||||
uint32 getNNReturnCode(uint32 iosError, iosuActCemuRequest_t* actRequest)
|
uint32 getNNReturnCode(uint32 iosError, iosuActCemuRequest_t* actRequest)
|
||||||
{
|
{
|
||||||
if ((iosError & 0x80000000) != 0)
|
if ((iosError & 0x80000000) != 0)
|
||||||
@ -135,9 +136,8 @@ namespace act
|
|||||||
|
|
||||||
sint32 GetUtcOffsetEx(sint64be* pOutOffset, uint8 slotNo)
|
sint32 GetUtcOffsetEx(sint64be* pOutOffset, uint8 slotNo)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!pOutOffset)
|
if (!pOutOffset)
|
||||||
return 0xc0712c80;
|
return ACTResult_NullPointer;
|
||||||
|
|
||||||
*pOutOffset = GetUtcOffset();
|
*pOutOffset = GetUtcOffset();
|
||||||
return 0;
|
return 0;
|
||||||
@ -145,7 +145,6 @@ namespace act
|
|||||||
|
|
||||||
uint32 IsPasswordCacheEnabledEx(uint8 slot)
|
uint32 IsPasswordCacheEnabledEx(uint8 slot)
|
||||||
{
|
{
|
||||||
// this is currently a hack.
|
|
||||||
// todo: read the value from the individual account via IOSU
|
// todo: read the value from the individual account via IOSU
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -155,6 +154,18 @@ namespace act
|
|||||||
return IsPasswordCacheEnabledEx(ACT_SLOT_CURRENT);
|
return IsPasswordCacheEnabledEx(ACT_SLOT_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetMiiImageEx(uint32be* outImageSize, MEMPTR<uint8> buffer, uint32 bufferSize, uint32 imageType, uint8 slot)
|
||||||
|
{
|
||||||
|
if (!outImageSize || !buffer.GetPtr())
|
||||||
|
return ACTResult_NullPointer;
|
||||||
|
if (imageType > ACT_MII_IMAGE_TYPE_MAX)
|
||||||
|
return ACTResult_AccountDoesNotExist;
|
||||||
|
uint32 size = 0;
|
||||||
|
uint32 r = iosu::act::GetMiiImage(slot, imageType, buffer.GetPtr(), bufferSize, &size);
|
||||||
|
*outImageSize = size;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
nnResult GetTimeZoneId(CafeString<65>* outTimezoneId)
|
nnResult GetTimeZoneId(CafeString<65>* outTimezoneId)
|
||||||
{
|
{
|
||||||
// return a placeholder timezone id for now
|
// return a placeholder timezone id for now
|
||||||
@ -415,74 +426,6 @@ void nnActExport_GetMiiEx(PPCInterpreter_t* hCPU)
|
|||||||
osLib_returnFromFunction(hCPU, r);
|
osLib_returnFromFunction(hCPU, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: write image bytes to the guest buffer and return the result code.
|
|
||||||
static uint32 ReturnMiiImage(uint32be* outImageSize, MEMPTR<uint8> buffer, uint32 bufferSize,
|
|
||||||
const uint8* data, uint32 dataSize)
|
|
||||||
{
|
|
||||||
if (outImageSize)
|
|
||||||
*outImageSize = dataSize;
|
|
||||||
if (!buffer.GetPtr() || bufferSize < dataSize)
|
|
||||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_LVL6, NN_RESULT_MODULE_NN_ACT, 0x12D80); // OutOfRange
|
|
||||||
memcpy(buffer.GetPtr(), data, dataSize);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nnActExport_GetMiiImageEx(PPCInterpreter_t* hCPU)
|
|
||||||
{
|
|
||||||
// GetMiiImageEx(uint32* outImageSize, void* buffer, uint32 bufferSize, ACTMiiImageType imageType, uint8 slot)
|
|
||||||
ppcDefineParamU32BEPtr(outImageSize, 0);
|
|
||||||
ppcDefineParamMEMPTR(buffer, uint8, 1);
|
|
||||||
ppcDefineParamU32(bufferSize, 2);
|
|
||||||
ppcDefineParamU32(imageType, 3);
|
|
||||||
ppcDefineParamU8(slot, 4);
|
|
||||||
|
|
||||||
cemuLog_logDebug(LogType::Force, "nn_act.GetMiiImageEx(outImageSize=0x{:08x} buffer=0x{:08x} bufferSize={} imageType={} slot={})",
|
|
||||||
hCPU->gpr[3], hCPU->gpr[4], bufferSize, imageType, slot);
|
|
||||||
|
|
||||||
// imageType maps directly to miiimgXX.dat in the account folder:
|
|
||||||
// FaceIcon (0) : 128x128 BGRA, raw TGA
|
|
||||||
// Expressions (1-6): 96x96 BGRA, zlib-compressed
|
|
||||||
// FullBody (7) : 270x360 BGRA, zlib-compressed (standing body render)
|
|
||||||
// FaceIconAlt (8) : 128x128 BGRA, zlib-compressed
|
|
||||||
if (imageType <= ACT_MII_IMAGE_TYPE_MAX)
|
|
||||||
{
|
|
||||||
uint32 persistentId = 0;
|
|
||||||
if (iosu::act::GetPersistentId(slot, &persistentId) && persistentId != 0)
|
|
||||||
{
|
|
||||||
fs::path datPath = ActiveSettings::GetMlcPath(
|
|
||||||
fmt::format("usr/save/system/act/{:08x}/miiimg{:02d}.dat", persistentId, imageType));
|
|
||||||
|
|
||||||
auto fileData = FileStream::LoadIntoMemory(datPath);
|
|
||||||
if (fileData.has_value())
|
|
||||||
{
|
|
||||||
if (imageType == (uint32)ACTMiiImageType::FaceIcon)
|
|
||||||
{
|
|
||||||
// FaceIcon (type 0) is stored as a raw TGA — serve it directly
|
|
||||||
uint32 r = ReturnMiiImage(outImageSize, buffer, bufferSize,
|
|
||||||
fileData->data(), (uint32)fileData->size());
|
|
||||||
osLib_returnFromFunction(hCPU, r);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// All other types are zlib-compressed; decompress before serving
|
|
||||||
auto decompressed = zlibDecompress(*fileData);
|
|
||||||
if (decompressed.has_value())
|
|
||||||
{
|
|
||||||
uint32 r = ReturnMiiImage(outImageSize, buffer, bufferSize,
|
|
||||||
decompressed->data(), (uint32)decompressed->size());
|
|
||||||
osLib_returnFromFunction(hCPU, r);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cemuLog_log(LogType::Force, "nn_act.GetMiiImageEx: failed to decompress miiimg{:02d}.dat", imageType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_ACT, NN_ACT_RESULT_ACCOUNT_DOES_NOT_EXIST));
|
|
||||||
}
|
|
||||||
|
|
||||||
void nnActExport_GetMiiName(PPCInterpreter_t* hCPU)
|
void nnActExport_GetMiiName(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "GetMiiName(0x{:08x})", hCPU->gpr[3]);
|
cemuLog_logDebug(LogType::Force, "GetMiiName(0x{:08x})", hCPU->gpr[3]);
|
||||||
@ -805,7 +748,7 @@ namespace nn::act
|
|||||||
|
|
||||||
osLib_addFunction("nn_act", "GetMii__Q2_2nn3actFP12FFLStoreData", nnActExport_GetMii);
|
osLib_addFunction("nn_act", "GetMii__Q2_2nn3actFP12FFLStoreData", nnActExport_GetMii);
|
||||||
osLib_addFunction("nn_act", "GetMiiEx__Q2_2nn3actFP12FFLStoreDataUc", nnActExport_GetMiiEx);
|
osLib_addFunction("nn_act", "GetMiiEx__Q2_2nn3actFP12FFLStoreDataUc", nnActExport_GetMiiEx);
|
||||||
osLib_addFunction("nn_act", "GetMiiImageEx__Q2_2nn3actFPUiPvUi15ACTMiiImageTypeUc", nnActExport_GetMiiImageEx);
|
cafeExportRegisterFunc(nn::act::GetMiiImageEx, "nn_act", "GetMiiImageEx__Q2_2nn3actFPUiPvUi15ACTMiiImageTypeUc", LogType::Placeholder);
|
||||||
osLib_addFunction("nn_act", "GetMiiName__Q2_2nn3actFPw", nnActExport_GetMiiName);
|
osLib_addFunction("nn_act", "GetMiiName__Q2_2nn3actFPw", nnActExport_GetMiiName);
|
||||||
osLib_addFunction("nn_act", "GetMiiNameEx__Q2_2nn3actFPwUc", nnActExport_GetMiiNameEx);
|
osLib_addFunction("nn_act", "GetMiiNameEx__Q2_2nn3actFPwUc", nnActExport_GetMiiNameEx);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user