mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
Merge d840591872 into aa7c82dda3
This commit is contained in:
commit
1438d83ed2
@ -323,7 +323,7 @@ const std::vector<Account>& Account::RefreshAccounts()
|
||||
result.emplace_back(account);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we always force at least one account
|
||||
if (result.empty())
|
||||
{
|
||||
@ -331,6 +331,11 @@ const std::vector<Account>& Account::RefreshAccounts()
|
||||
result.begin()->Save();
|
||||
}
|
||||
|
||||
std::sort(result.begin(), result.end(), [](const Account& a, const Account& b)
|
||||
{
|
||||
return a.GetPersistentId() < b.GetPersistentId();
|
||||
});
|
||||
|
||||
s_account_list = result;
|
||||
UpdatePersisidDat();
|
||||
return s_account_list;
|
||||
@ -351,6 +356,7 @@ void Account::UpdatePersisidDat()
|
||||
cemuLog_log(LogType::Force, "Unable to save persisid.dat");
|
||||
}
|
||||
|
||||
|
||||
bool Account::HasFreeAccountSlots()
|
||||
{
|
||||
return s_account_list.size() < 12;
|
||||
@ -400,10 +406,10 @@ uint32 Account::GetNextPersistentId()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// next id
|
||||
++result;
|
||||
|
||||
|
||||
const auto it = std::max_element(s_account_list.cbegin(), s_account_list.cend(), [](const Account& acc1, const Account& acc2) {return acc1.GetPersistentId() < acc2.GetPersistentId(); });
|
||||
if (it != s_account_list.cend())
|
||||
return std::max(result, it->GetPersistentId() + 1);
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Cafe/Account/Account.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#include "Cemu/napi/napi.h"
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
@ -65,6 +66,7 @@ struct actAccountData_t
|
||||
|
||||
actAccountData_t _actAccountData[IOSU_ACT_ACCOUNT_MAX_COUNT] = {};
|
||||
bool _actAccountDataInitialized = false;
|
||||
int _actAccountCount = 0;
|
||||
|
||||
void FillAccountData(const Account& account, const bool online_enabled, int index)
|
||||
{
|
||||
@ -108,26 +110,22 @@ void iosuAct_loadAccounts()
|
||||
return;
|
||||
|
||||
const bool online_enabled = ActiveSettings::IsOnlineEnabled();
|
||||
const auto persistent_id = ActiveSettings::GetPersistentId();
|
||||
|
||||
// first account is always our selected one
|
||||
// Load all accounts in order of persistantId; the active account keeps its natural position
|
||||
int counter = 0;
|
||||
const auto& first_acc = Account::GetAccount(persistent_id);
|
||||
FillAccountData(first_acc, online_enabled, counter);
|
||||
++counter;
|
||||
// enable multiple accounts for cafe functions (badly tested)
|
||||
//for (const auto& account : Account::GetAccounts())
|
||||
//{
|
||||
// if (first_acc.GetPersistentId() != account.GetPersistentId())
|
||||
// {
|
||||
// FillAccountData(account, online_enabled, counter);
|
||||
// ++counter;
|
||||
// }
|
||||
//}
|
||||
|
||||
cemuLog_log(LogType::Force, "IOSU_ACT: using account {} in first slot", boost::nowide::narrow(first_acc.GetMiiName()));
|
||||
|
||||
for (const auto& account : Account::GetAccounts())
|
||||
{
|
||||
if (counter >= IOSU_ACT_ACCOUNT_MAX_COUNT)
|
||||
break;
|
||||
FillAccountData(account, online_enabled, counter);
|
||||
++counter;
|
||||
}
|
||||
_actAccountCount = counter;
|
||||
_actAccountDataInitialized = true;
|
||||
|
||||
const uint8 activeSlot = iosu::act::getCurrentAccountSlot();
|
||||
const auto& active_acc = Account::GetAccount(ActiveSettings::GetPersistentId());
|
||||
cemuLog_log(LogType::Force, "IOSU_ACT: loaded {} account(s), using {} in slot {}", counter, boost::nowide::narrow(std::wstring(active_acc.GetMiiName())), activeSlot);
|
||||
}
|
||||
|
||||
bool iosuAct_isAccountDataLoaded()
|
||||
@ -135,6 +133,11 @@ bool iosuAct_isAccountDataLoaded()
|
||||
return _actAccountDataInitialized;
|
||||
}
|
||||
|
||||
int iosuAct_getNumAccounts()
|
||||
{
|
||||
return _actAccountCount;
|
||||
}
|
||||
|
||||
uint32 iosuAct_acquirePrincipalIdByAccountId(const char* nnid, uint32* pid)
|
||||
{
|
||||
NAPI::AuthInfo authInfo;
|
||||
@ -154,10 +157,17 @@ uint32 iosuAct_acquirePrincipalIdByAccountId(const char* nnid, uint32* pid)
|
||||
|
||||
sint32 iosuAct_getAccountIndexBySlot(uint8 slot)
|
||||
{
|
||||
if (slot == iosu::act::ACT_SLOT_CURRENT)
|
||||
return 0;
|
||||
if (slot == 0xFF)
|
||||
return 0; // ?
|
||||
if (slot == iosu::act::ACT_SLOT_CURRENT || slot == 0xFF)
|
||||
{
|
||||
// find the active account's actual index by persistent ID
|
||||
const uint32 persistent_id = ActiveSettings::GetPersistentId();
|
||||
for (int i = 0; i < _actAccountCount; i++)
|
||||
{
|
||||
if (_actAccountData[i].isValid && _actAccountData[i].persistentId == persistent_id)
|
||||
return i;
|
||||
}
|
||||
return 0; // fallback
|
||||
}
|
||||
cemu_assert_debug(slot != 0);
|
||||
cemu_assert_debug(slot <= IOSU_ACT_ACCOUNT_MAX_COUNT);
|
||||
return slot - 1;
|
||||
@ -165,8 +175,9 @@ sint32 iosuAct_getAccountIndexBySlot(uint8 slot)
|
||||
|
||||
uint32 iosuAct_getAccountIdOfCurrentAccount()
|
||||
{
|
||||
cemu_assert_debug(_actAccountData[0].isValid);
|
||||
return _actAccountData[0].persistentId;
|
||||
const sint32 index = iosuAct_getAccountIndexBySlot(iosu::act::ACT_SLOT_CURRENT);
|
||||
cemu_assert_debug(_actAccountData[index].isValid);
|
||||
return _actAccountData[index].persistentId;
|
||||
}
|
||||
|
||||
// IOSU act API interface
|
||||
@ -386,7 +397,13 @@ namespace iosu
|
||||
{
|
||||
uint8 getCurrentAccountSlot()
|
||||
{
|
||||
return 1;
|
||||
const uint32 persistent_id = ActiveSettings::GetPersistentId();
|
||||
for (int i = 0; i < _actAccountCount; i++)
|
||||
{
|
||||
if (_actAccountData[i].isValid && _actAccountData[i].persistentId == persistent_id)
|
||||
return (uint8)(i + 1); // slots are 1-based
|
||||
}
|
||||
return 1; // fallback
|
||||
}
|
||||
|
||||
actAccountData_t* GetAccountBySlotNo(uint8 slotNo)
|
||||
@ -538,6 +555,44 @@ namespace iosu
|
||||
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)
|
||||
{
|
||||
if (accountSlot != ACT_SLOT_CURRENT)
|
||||
|
||||
@ -50,6 +50,7 @@ namespace iosu
|
||||
bool getScreenname(uint8 slot, uint16 screenname[ACT_NICKNAME_LENGTH]);
|
||||
bool getCountryIndex(uint8 slot, uint32* countryIndex);
|
||||
bool GetPersistentId(uint8 slot, uint32* persistentId);
|
||||
uint32 GetMiiImage(uint8 slot, uint32 imageType, void* outBuf, uint32 bufferSize, uint32* outImageSize);
|
||||
|
||||
std::string getAccountId2(uint8 slot);
|
||||
|
||||
@ -121,4 +122,5 @@ struct iosuActCemuRequest_t
|
||||
|
||||
uint32 iosuAct_getAccountIdOfCurrentAccount();
|
||||
|
||||
bool iosuAct_isAccountDataLoaded();
|
||||
bool iosuAct_isAccountDataLoaded();
|
||||
int iosuAct_getNumAccounts();
|
||||
@ -7,8 +7,6 @@
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Common/CafeString.h"
|
||||
|
||||
sint32 numAccounts = 1;
|
||||
|
||||
#define actPrepareRequest() \
|
||||
StackAllocator<iosuActCemuRequest_t> _buf_actRequest; \
|
||||
StackAllocator<ioBufferVector_t> _buf_bufferVector; \
|
||||
@ -23,6 +21,10 @@ StackAllocator<iosuActCemuRequest_t> _buf_actRequest; \
|
||||
iosuActCemuRequest_t* actRequest = _buf_actRequest.GetPointer(); \
|
||||
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)
|
||||
{
|
||||
if ((iosError & 0x80000000) != 0)
|
||||
@ -109,7 +111,7 @@ namespace act
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint32 AcquireIndependentServiceToken(independentServiceToken_t* token, const char* clientId, uint32 cacheDurationInSeconds)
|
||||
{
|
||||
memset(token, 0, sizeof(independentServiceToken_t));
|
||||
@ -134,14 +136,36 @@ namespace act
|
||||
|
||||
sint32 GetUtcOffsetEx(sint64be* pOutOffset, uint8 slotNo)
|
||||
{
|
||||
|
||||
if (!pOutOffset)
|
||||
return 0xc0712c80;
|
||||
return ACTResult_NullPointer;
|
||||
|
||||
*pOutOffset = GetUtcOffset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 IsPasswordCacheEnabledEx(uint8 slot)
|
||||
{
|
||||
// todo: read the value from the individual account via IOSU
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 IsPasswordCacheEnabled()
|
||||
{
|
||||
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)
|
||||
{
|
||||
// return a placeholder timezone id for now
|
||||
@ -186,7 +210,7 @@ void nnActExport_CreateConsoleAccount(PPCInterpreter_t* hCPU)
|
||||
void nnActExport_GetNumOfAccounts(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "nn_act.GetNumOfAccounts()");
|
||||
osLib_returnFromFunction(hCPU, numAccounts); // account count
|
||||
osLib_returnFromFunction(hCPU, iosuAct_getNumAccounts());
|
||||
}
|
||||
|
||||
void nnActExport_IsSlotOccupied(PPCInterpreter_t* hCPU)
|
||||
@ -402,13 +426,6 @@ void nnActExport_GetMiiEx(PPCInterpreter_t* hCPU)
|
||||
osLib_returnFromFunction(hCPU, r);
|
||||
}
|
||||
|
||||
void nnActExport_GetMiiImageEx(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "GetMiiImageEx unimplemented");
|
||||
|
||||
osLib_returnFromFunction(hCPU, 0);
|
||||
}
|
||||
|
||||
void nnActExport_GetMiiName(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "GetMiiName(0x{:08x})", hCPU->gpr[3]);
|
||||
@ -418,7 +435,7 @@ void nnActExport_GetMiiName(PPCInterpreter_t* hCPU)
|
||||
|
||||
uint32 r = nn::act::GetMiiEx(&miiData, iosu::act::ACT_SLOT_CURRENT);
|
||||
// extract name
|
||||
sint32 miiNameLength = 0;
|
||||
sint32 miiNameLength = 0;
|
||||
for (sint32 i = 0; i < MII_FFL_NAME_LENGTH; i++)
|
||||
{
|
||||
miiName[i] = miiData->miiName[i];
|
||||
@ -567,8 +584,8 @@ void nnActExport_GetDefaultAccount(PPCInterpreter_t* hCPU)
|
||||
|
||||
void nnActExport_GetSlotNo(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
// id of active account
|
||||
osLib_returnFromFunction(hCPU, 1); // 1 is the first slot (0 is invalid)
|
||||
// returns the 1-based slot number of the currently active account
|
||||
osLib_returnFromFunction(hCPU, iosu::act::getCurrentAccountSlot());
|
||||
}
|
||||
|
||||
void nnActExport_GetSlotNoEx(PPCInterpreter_t* hCPU)
|
||||
@ -731,7 +748,7 @@ namespace nn::act
|
||||
|
||||
osLib_addFunction("nn_act", "GetMii__Q2_2nn3actFP12FFLStoreData", nnActExport_GetMii);
|
||||
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", "GetMiiNameEx__Q2_2nn3actFPwUc", nnActExport_GetMiiNameEx);
|
||||
|
||||
@ -754,6 +771,8 @@ namespace nn::act
|
||||
osLib_addFunction("nn_act", "AcquirePrincipalIdByAccountId__Q2_2nn3actFPUiPA17_CcUi", nnActExport_AcquirePrincipalIdByAccountId);
|
||||
|
||||
cafeExportRegisterFunc(nn::act::GetErrorCode, "nn_act", "GetErrorCode__Q2_2nn3actFRCQ2_2nn6Result", LogType::Placeholder);
|
||||
cafeExportRegisterFunc(nn::act::IsPasswordCacheEnabled, "nn_act", "IsPasswordCacheEnabled__Q2_2nn3actFv", LogType::Placeholder);
|
||||
cafeExportRegisterFunc(nn::act::IsPasswordCacheEnabledEx, "nn_act", "IsPasswordCacheEnabledEx__Q2_2nn3actFUc", LogType::Placeholder);
|
||||
|
||||
// placeholders / incomplete implementations
|
||||
osLib_addFunction("nn_act", "HasNfsAccount__Q2_2nn3actFv", nnActExport_HasNfsAccount);
|
||||
|
||||
@ -9,6 +9,22 @@ struct independentServiceToken_t
|
||||
};
|
||||
static_assert(sizeof(independentServiceToken_t) == 0x201); // todo - verify size
|
||||
|
||||
// Passed to GetMiiImageEx. Each value maps to miiimgXX.dat in the account folder.
|
||||
// Sizes and storage formats confirmed from real Wii U MLC dumps.
|
||||
enum class ACTMiiImageType : uint32
|
||||
{
|
||||
FaceIcon = 0, // 128x128 BGRA TGA, stored raw (miiimg00.dat)
|
||||
FaceExpression1 = 1, // 96x96 BGRA TGA, zlib-compressed (miiimg01.dat)
|
||||
FaceExpression2 = 2, // 96x96 BGRA TGA, zlib-compressed (miiimg02.dat)
|
||||
FaceExpression3 = 3, // 96x96 BGRA TGA, zlib-compressed (miiimg03.dat)
|
||||
FaceExpression4 = 4, // 96x96 BGRA TGA, zlib-compressed (miiimg04.dat)
|
||||
FaceExpression5 = 5, // 96x96 BGRA TGA, zlib-compressed (miiimg05.dat)
|
||||
FaceExpression6 = 6, // 96x96 BGRA TGA, zlib-compressed (miiimg06.dat)
|
||||
FullBody = 7, // 270x360 BGRA TGA, zlib-compressed (miiimg07.dat) - full standing body render
|
||||
FaceIconAlt = 8, // 128x128 BGRA TGA, zlib-compressed (miiimg08.dat)
|
||||
};
|
||||
static constexpr uint32 ACT_MII_IMAGE_TYPE_MAX = 8;
|
||||
|
||||
namespace nn
|
||||
{
|
||||
namespace act
|
||||
|
||||
Loading…
Reference in New Issue
Block a user