Initial support for title switching + better Wii U menu compatibility (#907)

This commit is contained in:
Exzap 2023-07-21 13:54:07 +02:00 committed by GitHub
parent bfbeeae6f6
commit 2200cc0ddf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
95 changed files with 2549 additions and 746 deletions

View file

@ -113,19 +113,6 @@ void DebugLogStackTrace(OSThread_t* thread, MPTR sp)
}
}
void coreinitExport_OSPanic(PPCInterpreter_t* hCPU)
{
cemuLog_log(LogType::Force, "OSPanic!\n");
cemuLog_log(LogType::Force, "File: {}:{}\n", (const char*)memory_getPointerFromVirtualOffset(hCPU->gpr[3]), hCPU->gpr[4]);
cemuLog_log(LogType::Force, "Msg: {}\n", (const char*)memory_getPointerFromVirtualOffset(hCPU->gpr[5]));
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
#ifdef CEMU_DEBUG_ASSERT
assert_dbg();
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
osLib_returnFromFunction(hCPU, 0);
}
typedef struct
{
/* +0x00 */ uint32be name;
@ -296,6 +283,17 @@ namespace coreinit
return 0;
}
void OSPanic(const char* file, sint32 lineNumber, const char* msg)
{
cemuLog_log(LogType::Force, "OSPanic!");
cemuLog_log(LogType::Force, "File: {}:{}", file, lineNumber);
cemuLog_log(LogType::Force, "Msg: {}", msg);
DebugLogStackTrace(coreinit::OSGetCurrentThread(), coreinit::OSGetStackPointer());
#ifdef CEMU_DEBUG_ASSERT
while (true) std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
}
void InitializeCore()
{
cafeExportRegister("coreinit", OSGetCoreId, LogType::CoreinitThread);
@ -313,6 +311,8 @@ namespace coreinit
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSPanic, LogType::Placeholder);
}
};

View file

@ -117,6 +117,12 @@ namespace coreinit
return currentTick >= g_soonestAlarm;
}
static void Reset()
{
g_activeAlarmList.clear();
g_soonestAlarm = 0;
}
public:
struct ComparatorFireTime
{
@ -282,11 +288,21 @@ namespace coreinit
return alarm->userData;
}
void OSAlarm_resetAll()
void OSAlarm_Shutdown()
{
cemu_assert_debug(g_activeAlarms.empty());
cemu_assert_debug(false);
__OSLockScheduler();
if(g_activeAlarms.empty())
{
__OSUnlockScheduler();
return;
}
for(auto& itr : g_activeAlarms)
{
OSHostAlarmDestroy(itr.second);
}
g_activeAlarms.clear();
OSHostAlarm::Reset();
__OSUnlockScheduler();
}
void _OSAlarmThread(PPCInterpreter_t* hCPU)

View file

@ -45,7 +45,7 @@ namespace coreinit
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData);
void OSSetPeriodicAlarm(OSAlarm_t* OSAlarm, uint64 startTick, uint64 periodTick, MPTR OSAlarmHandler);
void OSAlarm_resetAll();
void OSAlarm_Shutdown();
void alarm_update();

View file

@ -64,6 +64,24 @@ sint32 _GetArgLength(const char* arg)
return c;
}
static std::string GetLaunchArgs()
{
std::string argStr = CafeSystem::GetForegroundTitleArgStr();
if(std::vector<std::string> overrideArgs; CafeSystem::GetOverrideArgStr(overrideArgs))
{
// args are overriden by launch directive (OSLaunchTitleByPath)
// keep the rpx path but use the arguments from the override
if (size_t pos = argStr.find(' '); pos != std::string::npos)
argStr.resize(pos);
for(size_t i=0; i<overrideArgs.size(); i++)
{
argStr += " ";
argStr += overrideArgs[i];
}
}
return argStr;
}
void CafeInit()
{
// extract executable filename
@ -85,7 +103,8 @@ void CafeInit()
_AddArg(rpxFileName.data(), rpxFileName.size());
strcpy((char*)_coreinitInfo->argStorage, std::string(rpxFileName).c_str());
std::string _argStr = CafeSystem::GetForegroundTitleArgStr();
std::string _argStr = GetLaunchArgs();
CafeSystem::UnsetOverrideArgs(); // make sure next launch doesn't accidentally use the same arguments
const char* argString = _argStr.c_str();
// attach parameters from arg string
if (argString && argString[0] != '\0')

View file

@ -103,13 +103,12 @@ void coreinitExport_MCP_TitleListByAppType(PPCInterpreter_t* hCPU)
void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
{
cemuLog_logDebug(LogType::Force, "MCP_TitleList(...) unimplemented");
ppcDefineParamU32(mcpHandle, 0);
ppcDefineParamU32BEPtr(countOutput, 1);
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 2);
ppcDefineParamU32(titleListBufferSize, 3);
// todo -> Other parameters
// todo -> Other parameters?
mcpPrepareRequest();
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST;
@ -120,6 +119,8 @@ void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
*countOutput = mcpRequest->titleListRequest.titleCount;
cemuLog_logDebug(LogType::Force, "MCP_TitleList(...) returned {} titles", (uint32)mcpRequest->titleListRequest.titleCount);
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
}
@ -186,7 +187,6 @@ void coreinitExport_MCP_GetTitleInfoByTitleAndDevice(PPCInterpreter_t* hCPU)
}
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
}
namespace coreinit
@ -200,7 +200,7 @@ namespace coreinit
systemVersion->n0 = 0x5;
systemVersion->n1 = 0x5;
systemVersion->n2 = 0x2;
systemVersion->n2 = 0x5;
// todo: Load this from \sys\title\00050010\10041200\content\version.bin
osLib_returnFromFunction(hCPU, 0);
@ -236,57 +236,56 @@ namespace coreinit
}
#pragma pack(1)
typedef struct
struct MCPDevice_t
{
/* +0x000 */ char storageName[0x90]; // the name in the storage path
/* +0x000 */ char storageName[0x8]; // the name in the storage path (mlc, slc, usb?) // volumeId at +8
/* +0x008 */ char volumeId[16]; //
/* +0x018 */ char ukn[0x90 - 0x18];
/* +0x090 */ char storagePath[0x280 - 1]; // /vol/storage_%s%02x
/* +0x30F */ uint32be storageSubindexOrMask; // the id in the storage path, but this might also be a MASK of indices (e.g. 1 -> Only device 1, 7 -> Device 1,2,3) men.rpx expects 0xF (or 0x7?) to be set for MLC, SLC and USB for MLC_FullDeviceList
/* +0x30F */ uint32be flags; // men.rpx checks for 0x2 and 0x8
uint8 ukn313[4];
uint8 ukn317[4];
}MCPDevice_t;
};
#pragma pack()
static_assert(sizeof(MCPDevice_t) == 0x31B, "MCPDevice_t has invalid size");
static_assert(offsetof(MCPDevice_t, storagePath) == 0x090, "MCPDevice_t.storagePath has invalid offset");
static_assert(offsetof(MCPDevice_t, storageSubindexOrMask) == 0x30F, "MCPDevice_t.storageSubindex has invalid offset");
static_assert(offsetof(MCPDevice_t, ukn313) == 0x313, "MCPDevice_t.ukn313 has invalid offset");
static_assert(offsetof(MCPDevice_t, ukn317) == 0x317, "MCPDevice_t.ukn317 has invalid offset");
static_assert(sizeof(MCPDevice_t) == 0x31B);
static_assert(sizeof(MCPDevice_t) == 0x31B);
static_assert(offsetof(MCPDevice_t, storagePath) == 0x90);
static_assert(offsetof(MCPDevice_t, flags) == 0x30F);
static_assert(offsetof(MCPDevice_t, ukn313) == 0x313);
static_assert(offsetof(MCPDevice_t, ukn317) == 0x317);
void MCP_DeviceListEx(uint32 mcpHandle, uint32be* deviceCount, MCPDevice_t* deviceList, uint32 deviceListSize, bool returnFullList)
{
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
if (maxDeviceCount < 3*3)
assert_dbg();
cemu_assert(maxDeviceCount >= 2);
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
memset(deviceList, 0, deviceListSize);
sint32 index = 0;
for (sint32 f = 0; f < 1; f++)
{
// 0
strcpy(deviceList[index].storageName, "mlc");
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
// 1
strcpy(deviceList[index].storageName, "slc");
deviceList[index].storageSubindexOrMask = 0xF; // bitmask?
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
// 2
strcpy(deviceList[index].storageName, "usb");
deviceList[index].storageSubindexOrMask = 0xF;
sprintf(deviceList[index].storagePath, "/vol/storage_%s%02x", deviceList[index].storageName, (sint32)deviceList[index].storageSubindexOrMask);
index++;
}
uint32 flags = 2 | 8;
// flag 2 is necessary for Wii U menu and Friend List to load
// if we dont set flag 0x8 then Wii U menu will show a disk loading icon and screen
// slc
strcpy(deviceList[index].storageName, "slc");
strcpy(deviceList[index].volumeId, "VOLID_SLC");
deviceList[index].flags = flags;
strcpy(deviceList[index].storagePath, "/vol/system_slc"); // unsure
index++;
// mlc
strcpy(deviceList[index].storageName, "mlc");
strcpy(deviceList[index].volumeId, "VOLID_MLC");
deviceList[index].flags = flags;
sprintf(deviceList[index].storagePath, "/vol/storage_mlc01");
index++;
// we currently dont emulate USB storage
*deviceCount = index;
}
void export_MCP_DeviceList(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(mcpHandle, 0);
@ -306,12 +305,12 @@ namespace coreinit
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
// 0
strcpy(deviceList[0].storageName, "mlc");
deviceList[0].storageSubindexOrMask = (0x01); // bitmask?
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].storageSubindexOrMask);
deviceList[0].flags = (0x01); // bitmask?
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].flags);
// 1
strcpy(deviceList[1].storageName, "slc");
deviceList[1].storageSubindexOrMask = (0x01); // bitmask?
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].storageSubindexOrMask);
deviceList[1].flags = (0x01); // bitmask?
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].flags);
// 2
//strcpy(deviceList[2].storageName, "usb");
@ -360,6 +359,8 @@ namespace coreinit
// this callback is to let the app know when the title list changed?
//PPCCoreCallback(callbackMPTR); // -> If we trigger the callback then the menu will repeat with a call to MCP_GetTitleList(), MCP_DeviceList() and MCP_TitleListUpdateGetNext
osLib_returnFromFunction(hCPU, 0);
}
@ -387,6 +388,34 @@ namespace coreinit
osLib_returnFromFunction(hCPU, 0);
}
uint32 MCP_UpdateClearContextAsync(uint32 mcpHandle, betype<MPTR>* callbackPtr)
{
cemuLog_logDebug(LogType::Force, "MCP_UpdateClearContextAsync() - stubbed");
uint32 clearContextResult = 0;
PPCCoreCallback(*callbackPtr, clearContextResult);
return 0;
}
uint32 MCP_InstallUtilGetTitleEnability(uint32 mcpHandle, uint32be* enabilityOutput, MCPTitleInfo* title)
{
*enabilityOutput = 1;
return 0;
}
uint32 MCP_GetEcoSettings(uint32 mcpHandle, uint32be* flagCaffeineEnable, uint32be* uknFlag2, uint32be* uknFlag3)
{
*flagCaffeineEnable = 1; // returning 1 here will stop the Wii U Menu from showing the Quick Start setup dialogue
*uknFlag2 = 0;
*uknFlag3 = 0;
return 0;
}
uint32 MCP_RightCheckLaunchable(uint32 mcpHandle, uint64 titleId, uint32be* launchableOut)
{
*launchableOut = 1;
return 0;
}
void InitializeMCP()
{
osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open);
@ -408,6 +437,12 @@ namespace coreinit
osLib_addFunction("coreinit", "MCP_UpdateCheckContext", export_MCP_UpdateCheckContext);
osLib_addFunction("coreinit", "MCP_TitleListUpdateGetNext", export_MCP_TitleListUpdateGetNext);
osLib_addFunction("coreinit", "MCP_GetOverlayAppInfo", export_MCP_GetOverlayAppInfo);
cafeExportRegister("coreinit", MCP_UpdateClearContextAsync, LogType::Placeholder);
cafeExportRegister("coreinit", MCP_InstallUtilGetTitleEnability, LogType::Placeholder);
cafeExportRegister("coreinit", MCP_RightCheckLaunchable, LogType::Placeholder);
cafeExportRegister("coreinit", MCP_GetEcoSettings, LogType::Placeholder);
}
}
@ -552,6 +587,27 @@ void coreinitExport_UCReadSysConfig(PPCInterpreter_t* hCPU)
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
/* caffeine settings (Quick Start) */
else if (_strcmpi(ucParam->settingName, "caffeine.enable") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 1);
}
else if (_strcmpi(ucParam->settingName, "caffeine.ad_enable") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "caffeine.push_enable") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else if (_strcmpi(ucParam->settingName, "caffeine.drcled_enable") == 0)
{
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
}
else
{
cemuLog_logDebug(LogType::Force, "Unsupported SCI value: {} Size {:08x}", ucParam->settingName, ucParam->ukn4_size);

View file

@ -615,10 +615,24 @@ namespace coreinit
cemu_assert_unimplemented();
}
void MEMResetToDefaultState()
{
for (auto& it : sHeapBaseHandle)
it = nullptr;
g_heapTableCount = 0;
g_slockInitialized = false;
g_listsInitialized = false;
gDefaultHeap = nullptr;
memset(&g_list1, 0, sizeof(g_list1));
memset(&g_list2, 0, sizeof(g_list2));
memset(&g_list3, 0, sizeof(g_list3));
}
void InitializeMEM()
{
for (auto& it : sHeapBaseHandle)
it = nullptr;
MEMResetToDefaultState();
cafeExportRegister("coreinit", CoreInitDefaultHeap, LogType::CoreinitMem);

View file

@ -1,5 +1,8 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/Filesystem/fsc.h"
#include <pugixml.hpp>
namespace coreinit
{
@ -309,14 +312,20 @@ namespace coreinit
cemu_assert_unimplemented();
}
void COSWarn()
void COSWarn(int moduleId, const char* format)
{
cemu_assert_debug(false);
char buffer[1024 * 2];
int prefixLen = sprintf(buffer, "[COSWarn-%d] ", moduleId);
sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, ppcInterpreterCurrentInstance, 2);
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen);
}
void OSLogPrintf()
void OSLogPrintf(int ukn1, int ukn2, int ukn3, const char* format)
{
cemu_assert_debug(false);
char buffer[1024 * 2];
int prefixLen = sprintf(buffer, "[OSLogPrintf-%d-%d-%d] ", ukn1, ukn2, ukn3);
sint32 len = ppcSprintf(format, buffer + prefixLen, sizeof(buffer) - prefixLen, ppcInterpreterCurrentInstance, 4);
WriteCafeConsole(CafeLogType::OSCONSOLE, buffer, len + prefixLen);
}
void OSConsoleWrite(const char* strPtr, sint32 length)
@ -341,19 +350,124 @@ namespace coreinit
return true;
}
uint32 s_sdkVersion;
uint32 __OSGetProcessSDKVersion()
{
return s_sdkVersion;
}
// move this to CafeSystem.cpp?
void OSLauncherThread(uint64 titleId)
{
CafeSystem::ShutdownTitle();
CafeSystem::PrepareForegroundTitle(titleId);
CafeSystem::RequestRecreateCanvas();
CafeSystem::LaunchForegroundTitle();
}
uint32 __LaunchByTitleId(uint64 titleId, uint32 argc, MEMPTR<char>* argv)
{
// prepare argument buffer
#if 0
char argumentBuffer[4096];
uint32 argumentBufferLength = 0;
char* argWriter = argumentBuffer;
for(uint32 i=0; i<argc; i++)
{
const char* arg = argv[i];
uint32 argLength = strlen(arg);
if((argumentBufferLength + argLength + 1) >= sizeof(argumentBuffer))
{
// argument buffer full
cemuLog_logDebug(LogType::Force, "LaunchByTitleId: argument buffer full");
return 0x80000000;
}
memcpy(argWriter, arg, argLength);
argWriter[argLength] = '\0';
argWriter += argLength + 1;
argumentBufferLength += argLength + 1;
}
#endif
// normally the above buffer is passed to the PPC kernel via syscall 0x2B and then
// the kernel forwards it to IOSU MCP when requesting a title launch
// but for now we HLE most of the launching code and can just set the argument array directly
std::vector<std::string> argArray;
for(uint32 i=0; i<argc; i++)
argArray.emplace_back(argv[i]);
CafeSystem::SetOverrideArgs(argArray);
// spawn launcher thread (this current thread will be destroyed during relaunch)
std::thread launcherThread(OSLauncherThread, titleId);
launcherThread.detach();
// suspend this thread
coreinit::OSSuspendThread(coreinit::OSGetCurrentThread());
return 0;
}
uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc)
{
char appXmlPath[1024];
cemu_assert_debug(argc == 0); // custom args not supported yet
if(pathLength >= (sizeof(appXmlPath) - 32))
{
// path too long
cemuLog_logDebug(LogType::Force, "OSLaunchTitleByPathl: path too long");
return 0x80000000;
}
// read app.xml to get the titleId
memcpy(appXmlPath, path, pathLength);
appXmlPath[pathLength] = '\0';
strcat(appXmlPath, "/code/app.xml");
sint32 status;
auto fscfile = fsc_open(appXmlPath, FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &status);
if (!fscfile)
{
cemuLog_logDebug(LogType::Force, "OSLaunchTitleByPathl: failed to open target app.xml");
return 0x80000000;
}
uint32 size = fsc_getFileSize(fscfile);
std::vector<uint8> tmpData(size);
fsc_readFile(fscfile, tmpData.data(), size);
fsc_close(fscfile);
// parse app.xml to get the titleId
pugi::xml_document app_doc;
if (!app_doc.load_buffer_inplace(tmpData.data(), tmpData.size()))
return false;
uint64 titleId = std::stoull(app_doc.child("app").child("title_id").child_value(), nullptr, 16);
if(titleId == 0)
{
cemuLog_logDebug(LogType::Force, "OSLaunchTitleByPathl: failed to parse titleId from app.xml");
return 0x80000000;
}
__LaunchByTitleId(titleId, 0, nullptr);
return 0;
}
uint32 OSRestartGame(uint32 argc, MEMPTR<char>* argv)
{
__LaunchByTitleId(CafeSystem::GetForegroundTitleId(), argc, argv);
return 0;
}
void miscInit()
{
s_sdkVersion = CafeSystem::GetForegroundTitleSDKVersion();
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
cafeExportRegister("coreinit", __OSGetProcessSDKVersion, LogType::Placeholder);
g_homeButtonMenuEnabled = true; // enabled by default
// Disney Infinity 2.0 actually relies on home button menu being enabled by default. If it's false it will crash due to calling erreula->IsAppearHomeNixSign() before initializing erreula
cafeExportRegister("coreinit", OSIsHomeButtonMenuEnabled, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSEnableHomeButtonMenu, LogType::CoreinitThread);
cafeExportRegister("coreinit", OSLaunchTitleByPathl, LogType::Placeholder);
cafeExportRegister("coreinit", OSRestartGame, LogType::Placeholder);
}
};

View file

@ -2,5 +2,9 @@
namespace coreinit
{
uint32 __OSGetProcessSDKVersion();
uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc);
uint32 OSRestartGame(uint32 argc, MEMPTR<char>* argv);
void miscInit();
};

View file

@ -20,8 +20,6 @@ SlimRWLock srwlock_activeThreadList;
MPTR activeThread[256];
sint32 activeThreadCount = 0;
MPTR exitThreadPtr = 0;
void nnNfp_update();
namespace coreinit
@ -198,8 +196,6 @@ namespace coreinit
return __currentCoreThread[currentInstance->spr.UPIR];
}
MPTR funcPtr_threadEntry = 0;
void threadEntry(PPCInterpreter_t* hCPU)
{
OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(hCPU);
@ -223,12 +219,6 @@ namespace coreinit
void coreinitExport_OSExitThreadDepr(PPCInterpreter_t* hCPU);
void initFunctionPointers()
{
exitThreadPtr = PPCInterpreter_makeCallableExportDepr(coreinitExport_OSExitThreadDepr);
funcPtr_threadEntry = PPCInterpreter_makeCallableExportDepr(threadEntry);
}
void OSCreateThreadInternal(OSThread_t* thread, uint32 entryPoint, MPTR stackLowerBaseAddr, uint32 stackSize, uint8 affinityMask, OSThread_t::THREAD_TYPE threadType)
{
cemu_assert_debug(thread != nullptr); // make thread struct mandatory. Caller can always use SysAllocator
@ -236,7 +226,7 @@ namespace coreinit
bool isThreadStillActive = __OSIsThreadActive(thread);
if (isThreadStillActive)
{
// workaround for games that restart threads to quickly
// workaround for games that restart threads before they correctly entered stopped/moribund state
// seen in Fast Racing Neo at boot (0x020617BC OSCreateThread)
cemuLog_log(LogType::Force, "Game attempting to re-initialize existing thread");
while ((thread->state == OSThread_t::THREAD_STATE::STATE_READY || thread->state == OSThread_t::THREAD_STATE::STATE_RUNNING) && thread->suspendCounter == 0)
@ -257,10 +247,6 @@ namespace coreinit
}
cemu_assert_debug(__OSIsThreadActive(thread) == false);
__OSUnlockScheduler();
initFunctionPointers();
if (thread == nullptr)
thread = (OSThread_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(OSThread_t), 32));
memset(thread, 0x00, sizeof(OSThread_t));
// init signatures
thread->SetMagic();
@ -277,8 +263,8 @@ namespace coreinit
// init misc stuff
thread->attr = affinityMask;
thread->context.setAffinity(affinityMask);
thread->context.srr0 = funcPtr_threadEntry;
thread->context.lr = _swapEndianU32(exitThreadPtr);
thread->context.srr0 = PPCInterpreter_makeCallableExportDepr(threadEntry);
thread->context.lr = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(coreinitExport_OSExitThreadDepr));
thread->id = 0x8000; // Warriors Orochi 3 softlocks if this is zero due to confusing threads (_OSActivateThread should set this?)
// init ugqr
thread->context.gqr[0] = 0x00000000;
@ -360,8 +346,8 @@ namespace coreinit
// todo - this should fully reinitialize the thread?
thread->entrypoint = _swapEndianU32(funcAddress);
thread->context.srr0 = coreinit::funcPtr_threadEntry;
thread->context.lr = _swapEndianU32(exitThreadPtr);
thread->context.srr0 = PPCInterpreter_makeCallableExportDepr(threadEntry);
thread->context.lr = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(coreinitExport_OSExitThreadDepr));
thread->context.gpr[3] = _swapEndianU32(numParam);
thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam));
thread->suspendCounter = 0; // verify
@ -1018,6 +1004,18 @@ namespace coreinit
return selectedThread;
}
void __OSDeleteAllActivePPCThreads()
{
__OSLockScheduler();
while(activeThreadCount > 0)
{
MEMPTR<OSThread_t> t{activeThread[0]};
t->state = OSThread_t::THREAD_STATE::STATE_NONE;
__OSDeactivateThread(t.GetPtr());
}
__OSUnlockScheduler();
}
void __OSCheckSystemEvents()
{
// AX update
@ -1202,7 +1200,7 @@ namespace coreinit
g_schedulerThreadHandles.emplace_back(it.native_handle());
}
// shuts down all scheduler host threads and deletes all fibers and their state
// shuts down all scheduler host threads and deletes all fibers and ppc threads
void OSSchedulerEnd()
{
std::unique_lock _lock(sSchedulerStateMtx);
@ -1384,9 +1382,10 @@ namespace coreinit
for (sint32 i = 0; i < PPC_CORE_COUNT; i++)
__currentCoreThread[i] = nullptr;
__OSInitDefaultThreads();
__OSInitDefaultThreads();
__OSInitTerminatorThreads();
}
}
}
void coreinit_suspendThread(OSThread_t* OSThreadBE, sint32 count)

View file

@ -606,6 +606,7 @@ namespace coreinit
void __OSQueueThreadDeallocation(OSThread_t* thread);
bool __OSIsThreadActive(OSThread_t* thread);
void __OSDeleteAllActivePPCThreads();
}
#pragma pack()

View file

@ -45,6 +45,12 @@ namespace GX2
sint32 gx2WriteGatherCurrentMainCoreIndex = -1;
bool gx2WriteGatherInited = false;
void GX2WriteGather_ResetToDefaultState()
{
gx2WriteGatherCurrentMainCoreIndex = -1;
gx2WriteGatherInited = false;
}
void GX2Init_writeGather() // init write gather, make current core
{
if (gx2WriteGatherPipe.gxRingBuffer == NULL)
@ -289,7 +295,6 @@ namespace GX2
void GX2CommandInit()
{
cafeExportRegister("gx2", GX2BeginDisplayList, LogType::GX2);
cafeExportRegister("gx2", GX2BeginDisplayListEx, LogType::GX2);
cafeExportRegister("gx2", GX2EndDisplayList, LogType::GX2);
@ -305,4 +310,9 @@ namespace GX2
cafeExportRegister("gx2", GX2PatchDisplayList, LogType::GX2);
}
void GX2CommandResetToDefaultState()
{
GX2WriteGather_ResetToDefaultState();
}
}

View file

@ -97,5 +97,6 @@ namespace GX2
void GX2DirectCallDisplayList(void* addr, uint32 size);
void GX2Init_writeGather();
void GX2CommandInit();
void GX2CommandInit();
void GX2CommandResetToDefaultState();
}

View file

@ -308,4 +308,15 @@ namespace GX2
coreinit::OSInitEvent(s_updateRetirementEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
coreinit::OSInitSemaphore(s_eventCbQueueSemaphore, 0);
}
void GX2EventResetToDefaultState()
{
s_callbackThreadLaunched = false;
s_lastRetirementTimestamp = 0;
for(auto& it : s_eventCallback)
{
it.callbackFuncPtr = nullptr;
it.userData = nullptr;
}
}
}

View file

@ -2,9 +2,10 @@
namespace GX2
{
void GX2EventInit();
void GX2Init_event();
void GX2EventResetToDefaultState();
void GX2EventInit();
void GX2WaitForVsync();
void GX2WaitForFlip();
void GX2DrawDone();

View file

@ -115,6 +115,9 @@ namespace GX2
void _GX2DriverReset()
{
LatteGPUState.gx2InitCalled = 0;
sGX2MainCoreIndex = 0;
GX2CommandResetToDefaultState();
GX2EventResetToDefaultState();
}
sint32 GX2GetMainCoreId(PPCInterpreter_t* hCPU)

View file

@ -43,8 +43,11 @@ namespace acp
return ACPStatus::SUCCESS;
}
bool sSaveDirMounted{false};
ACPStatus ACPMountSaveDir()
{
cemu_assert_debug(!sSaveDirMounted);
uint64 titleId = CafeSystem::GetForegroundTitleId();
uint32 high = GetTitleIdHigh(titleId) & (~0xC);
uint32 low = GetTitleIdLow(titleId);
@ -56,6 +59,13 @@ namespace acp
return _ACPConvertResultToACPStatus(&mountResult, "ACPMountSaveDir", 0x60);
}
ACPStatus ACPUnmountSaveDir()
{
cemu_assert_debug(!sSaveDirMounted);
fsc_unmount("/vol/save/", FSC_PRIORITY_BASE);
return ACPStatus::SUCCESS;
}
uint64 _acpGetTimestamp()
{
return coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK;
@ -434,12 +444,12 @@ namespace acp
ppcDefineParamU32(deviceId, 3);
if (deviceId != 3)
assert_dbg();
cemuLog_logDebug(LogType::Force, "ACPGetTitleMetaXmlByDevice(): Unsupported deviceId");
acpPrepareRequest();
acpRequest->requestCode = IOSU_ACP_GET_TITLE_META_XML;
acpRequest->ptr = acpMetaXml;
acpRequest->titleId = CafeSystem::GetForegroundTitleId();
acpRequest->titleId = titleId;//CafeSystem::GetForegroundTitleId();
__depr__IOS_Ioctlv(IOS_DEVICE_ACP_MAIN, IOSU_ACP_REQUEST_CEMU, 1, 1, acpBufferVector);

View file

@ -20,6 +20,7 @@ namespace acp
ACPStatus ACPGetApplicationBox(uint32be* applicationBox, uint64 titleId);
ACPStatus ACPMountSaveDir();
ACPStatus ACPUnmountSaveDir();
ACPStatus ACPCreateSaveDir(uint32 persistentId, ACPDeviceType type);
ACPStatus ACPUpdateSaveTimeStamp(uint32 persistentId, uint64 titleId, ACPDeviceType deviceType);;

View file

@ -1483,7 +1483,7 @@ std::string nnBossNsDataExport_GetPath(nsData_t* nsData)
if (title_id == 0)
title_id = CafeSystem::GetForegroundTitleId();
fs::path path = fmt::format(L"cemuBossStorage/{:08x}/{:08x}/user/{:08x}", (uint32)(title_id >> 32), (uint32)(title_id & 0xFFFFFFFF), accountId);
fs::path path = fmt::format("cemuBossStorage/{:08x}/{:08x}/user/{:08x}", (uint32)(title_id >> 32), (uint32)(title_id & 0xFFFFFFFF), accountId);
path /= nsData->storage.storageName;
path /= nsData->name;
return path.string();
@ -1578,6 +1578,13 @@ void nnBossNsDataExport_getSize(PPCInterpreter_t* hCPU)
osLib_returnFromFunction64(hCPU, fileSize);
}
uint64 nnBossNsData_GetCreatedTime(nsData_t* nsData)
{
cemuLog_logDebug(LogType::Force, "nn_boss.NsData_GetCreatedTime() not implemented. Returning 0");
uint64 createdTime = 0;
return createdTime;
}
uint32 nnBossNsData_read(nsData_t* nsData, uint64* sizeOutBE, void* buffer, sint32 length)
{
FSCVirtualFile* fscStorageFile = nullptr;
@ -1797,6 +1804,7 @@ void nnBoss_load()
osLib_addFunction("nn_boss", "DeleteRealFileWithHistory__Q3_2nn4boss6NsDataFv", nnBossNsDataExport_DeleteRealFileWithHistory);
osLib_addFunction("nn_boss", "Exist__Q3_2nn4boss6NsDataCFv", nnBossNsDataExport_Exist);
osLib_addFunction("nn_boss", "GetSize__Q3_2nn4boss6NsDataCFv", nnBossNsDataExport_getSize);
cafeExportRegisterFunc(nnBossNsData_GetCreatedTime, "nn_boss", "GetCreatedTime__Q3_2nn4boss6NsDataCFv", LogType::Placeholder);
osLib_addFunction("nn_boss", "Read__Q3_2nn4boss6NsDataFPvUi", nnBossNsDataExport_read);
osLib_addFunction("nn_boss", "Read__Q3_2nn4boss6NsDataFPLPvUi", nnBossNsDataExport_readWithSizeOut);
osLib_addFunction("nn_boss", "Seek__Q3_2nn4boss6NsDataFLQ3_2nn4boss12PositionBase", nnBossNsDataExport_seek);

View file

@ -1,27 +1,91 @@
#include "nn_ndm.h"
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/nn_common.h"
namespace nn
{
namespace ndm
{
void nnNdmExport_GetDaemonStatus(PPCInterpreter_t* hCPU)
enum class DAEMON_NAME : uint32
{
// parameters:
// r3 pointer to status integer (out)
// r4 daemon name (integer)
cemuLog_logDebug(LogType::Force, "nn_ndm.GetDaemonStatus(...) - hack");
// status codes:
// 1 - running? Download Manager (scope.rpx) expects this to return 1 (or zero). Otherwise it will display downloads as disabled
memory_writeU32(hCPU->gpr[3], 1);
// 2 - running?
// 3 - suspended?
osLib_returnFromFunction(hCPU, 0);
UKN_0, // Boss related?
UKN_1, // Download Manager? scope.rpx (Download Manager app) expects this to have status 0 or 1. Otherwise it will display downloads as disabled
UKN_2,
};
enum class DAEMON_STATUS : uint32
{
STATUS_UKN_0 = 0, // probably: Ready or initializing?
RUNNING = 1, // most likely running, but not 100% sure
STATUS_UKN_2 = 2, // probably: ready, starting or something like that?
SUSPENDED = 3,
};
constexpr size_t NUM_DAEMONS = 3;
DAEMON_STATUS s_daemonStatus[NUM_DAEMONS];
uint32 s_initializeRefCount;
uint32 Initialize()
{
s_initializeRefCount++;
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NDM, 0);
}
uint32 IsInitialized()
{
return s_initializeRefCount != 0 ? 1 : 0;
}
uint32 Finalize()
{
if(s_initializeRefCount == 0)
return BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_NDM, 0);
s_initializeRefCount++;
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NDM, 0);
}
uint32 GetDaemonStatus(betype<DAEMON_STATUS>* statusOut, DAEMON_NAME daemonName)
{
size_t daemonIndex = (size_t)daemonName;
if(daemonIndex >= NUM_DAEMONS)
return BUILD_NN_RESULT(NN_RESULT_LEVEL_STATUS, NN_RESULT_MODULE_NN_NDM, 0);
*statusOut = s_daemonStatus[daemonIndex];
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NDM, 0);
}
uint32 SuspendDaemons(uint32 daemonNameBitmask)
{
for(size_t i=0; i<NUM_DAEMONS; i++)
{
if(daemonNameBitmask & (1 << i))
s_daemonStatus[i] = DAEMON_STATUS::SUSPENDED;
}
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NDM, 0);
}
uint32 ResumeDaemons(uint32 daemonNameBitmask)
{
for(size_t i=0; i<NUM_DAEMONS; i++)
{
if(daemonNameBitmask & (1 << i))
s_daemonStatus[i] = DAEMON_STATUS::RUNNING;
}
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NDM, 0);
}
void load()
{
osLib_addFunction("nn_ndm", "GetDaemonStatus__Q2_2nn3ndmFPQ4_2nn3ndm7IDaemon6StatusQ4_2nn3ndm4Cafe10DaemonName", nnNdmExport_GetDaemonStatus);
for(size_t i=0; i<NUM_DAEMONS; i++)
s_daemonStatus[i] = DAEMON_STATUS::RUNNING;
s_initializeRefCount = 0;
cafeExportRegisterFunc(Initialize, "nn_ndm", "Initialize__Q2_2nn3ndmFv", LogType::Placeholder);
cafeExportRegisterFunc(Finalize, "nn_ndm", "Finalize__Q2_2nn3ndmFv", LogType::Placeholder);
cafeExportRegisterFunc(IsInitialized, "nn_ndm", "IsInitialized__Q2_2nn3ndmFv", LogType::Placeholder);
cafeExportRegisterFunc(GetDaemonStatus, "nn_ndm", "GetDaemonStatus__Q2_2nn3ndmFPQ4_2nn3ndm7IDaemon6StatusQ4_2nn3ndm4Cafe10DaemonName", LogType::Placeholder);
cafeExportRegisterFunc(SuspendDaemons, "nn_ndm", "SuspendDaemons__Q2_2nn3ndmFUi", LogType::Placeholder);
cafeExportRegisterFunc(ResumeDaemons, "nn_ndm", "ResumeDaemons__Q2_2nn3ndmFUi", LogType::Placeholder);
}
}
}

View file

@ -2,6 +2,7 @@
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
#include "Cafe/IOSU/legacy/iosu_nim.h"
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
#include "Cafe/OS/libs/nn_common.h"
#define nimPrepareRequest() \
StackAllocator<iosu::nim::iosuNimCemuRequest_t> _buf_nimRequest; \
@ -61,8 +62,6 @@ namespace nn
void export_GetNumTitlePackages(PPCInterpreter_t* hCPU)
{
cemuLog_logDebug(LogType::Force, "GetNumTitlePackages() - placeholder");
nimPrepareRequest();
nimRequest->requestCode = IOSU_NIM_GET_PACKAGE_COUNT;
@ -152,9 +151,10 @@ namespace nn
{
cemuLog_logDebug(LogType::Force, "QuerySchedulerStatus() - placeholder");
// scheduler status seems to a be a 32bit value?
// scheduler status seems to be either a 4 byte array or 8 byte array (or structs)?
// scope.rpx only checks the second byte and if it matches 0x01 then the scheduler is considered paused/stopped (displays that downloads are inactive)
// men.rpx checks the first byte for == 1 and if true, it will show the download manager icon as downloading
// downloads disabled:
//memory_writeU32(hCPU->gpr[3], (0x00010000));
// downloads enabled:
@ -163,24 +163,44 @@ namespace nn
osLib_returnFromFunction(hCPU, 0);
}
typedef struct
struct nimResultError
{
uint32be iosError;
uint32be ukn04;
}nimResultError_t; // size unknown, but probably is 0x8
};
void export_ConstructResultError(PPCInterpreter_t* hCPU)
void ConstructResultError(nimResultError* resultError, uint32be* nimErrorCodePtr, uint32 uknParam)
{
cemuLog_logDebug(LogType::Force, "Construct__Q3_2nn3nim11ResultErrorFQ2_2nn6Resulti() - placeholder");
ppcDefineParamTypePtr(resultError, nimResultError_t, 0);
ppcDefineParamU32BEPtr(nimErrorCodePtr, 1);
ppcDefineParamU32(uknParam, 2);
resultError->iosError = 0;
uint32 nnResultCode = *nimErrorCodePtr;
resultError->iosError = nnResultCode;
resultError->ukn04 = uknParam;
osLib_returnFromFunction(hCPU, 0);
if (nnResultCode == 0xFFFFFFFF)
{
// not a valid code, used by a Wii U menu
return;
}
// IOS errors need to be translated
if ( (nnResultCode&0x18000000) == 0x18000000)
{
// alternative error format
cemu_assert_unimplemented();
}
else
{
auto moduleId = nn::nnResult_GetModule(nnResultCode);
if (moduleId == NN_RESULT_MODULE_NN_IOS)
{
// ios error
cemu_assert_unimplemented();
}
else
{
// other error
resultError->iosError = 0;
}
}
}
void export_GetECommerceInfrastructureCountry(PPCInterpreter_t* hCPU)
@ -272,7 +292,7 @@ namespace nn
osLib_addFunction("nn_nim", "GetIconDatabaseEntries__Q2_2nn3nimFPQ3_2nn3nim17IconDatabaseEntryPCULUi", export_GetIconDatabaseEntries);
osLib_addFunction("nn_nim", "Construct__Q3_2nn3nim11ResultErrorFQ2_2nn6Resulti", export_ConstructResultError);
cafeExportRegisterFunc(ConstructResultError, "nn_nim", "Construct__Q3_2nn3nim11ResultErrorFQ2_2nn6Resulti", LogType::Placeholder);
osLib_addFunction("nn_nim", "MakeTitlePackageTaskConfigAutoUsingBgInstallPolicy__Q3_2nn3nim4utilFULiQ3_2nn4Cafe9TitleType", export_MakeTitlePackageTaskConfigAutoUsingBgInstallPolicy);
osLib_addFunction("nn_nim", "CalculateTitleInstallSize__Q2_2nn3nimFPLRCQ3_2nn3nim22TitlePackageTaskConfigPCUsUi", export_CalculateTitleInstallSize);

View file

@ -4,6 +4,7 @@
#include "nn_olv_UploadCommunityTypes.h"
#include "nn_olv_DownloadCommunityTypes.h"
#include "nn_olv_UploadFavoriteTypes.h"
#include "nn_olv_PostTypes.h"
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
@ -288,15 +289,16 @@ namespace nn
loadOliveUploadCommunityTypes();
loadOliveDownloadCommunityTypes();
loadOliveUploadFavoriteTypes();
loadOlivePostAndTopicTypes();
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);
osLib_addFunction("nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetMiiNickname);
osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag);
osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText);
// osLib_addFunction("nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", exportDownloadPostData_TestFlags);
// osLib_addFunction("nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetPostId);
// osLib_addFunction("nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetMiiNickname);
// osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag);
// osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText);
osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken);

View file

@ -0,0 +1,311 @@
#include "Cafe/OS/libs/nn_olv/nn_olv_Common.h"
#include "nn_olv_PostTypes.h"
#include "Cemu/ncrypto/ncrypto.h" // for base64 decoder
#include "util/helpers/helpers.h"
#include <pugixml.hpp>
#include <zlib.h>
namespace nn
{
namespace olv
{
template<size_t TLength>
uint32 SetStringUC2(uint16be(&str)[TLength], std::string_view sv, bool unescape = false)
{
if(unescape)
{
// todo
}
std::wstring ws = boost::nowide::widen(sv);
size_t copyLen = std::min<size_t>(TLength-1, ws.size());
for(size_t i=0; i<copyLen; i++)
str[i] = ws[i];
str[copyLen] = '\0';
return copyLen;
}
bool ParseXml_DownloadedDataBase(DownloadedDataBase& obj, pugi::xml_node& xmlNode)
{
// todo:
// app_data, body, painting, name
pugi::xml_node tokenNode;
if(tokenNode = xmlNode.child("body"); tokenNode)
{
//cemu_assert_unimplemented();
obj.bodyTextLength = SetStringUC2(obj.bodyText, tokenNode.child_value(), true);
if(obj.bodyTextLength > 0)
obj.SetFlag(DownloadedDataBase::FLAGS::HAS_BODY_TEXT);
}
if(tokenNode = xmlNode.child("feeling_id"); tokenNode)
{
obj.feeling = ConvertString<sint8>(tokenNode.child_value());
if(obj.feeling < 0 || obj.feeling >= 5)
{
cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: feeling_id out of range");
return false;
}
}
if(tokenNode = xmlNode.child("id"); tokenNode)
{
std::string_view id_sv = tokenNode.child_value();
if(id_sv.size() > 22)
{
cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: id too long");
return false;
}
memcpy(obj.postId, id_sv.data(), id_sv.size());
obj.postId[id_sv.size()] = '\0';
}
if(tokenNode = xmlNode.child("is_autopost"); tokenNode)
{
uint8 isAutopost = ConvertString<sint8>(tokenNode.child_value());
if(isAutopost == 1)
obj.SetFlag(DownloadedDataBase::FLAGS::IS_AUTOPOST);
else if(isAutopost == 0)
obj.SetFlag(DownloadedDataBase::FLAGS::IS_NOT_AUTOPOST);
else
{
cemuLog_log(LogType::Force, "DownloadedDataBase::ParseXml: is_autopost has invalid value");
return false;
}
}
if(tokenNode = xmlNode.child("empathy_added"); tokenNode)
{
if(ConvertString<sint32>(tokenNode.child_value()) > 0)
obj.SetFlag(DownloadedDataBase::FLAGS::HAS_EMPATHY_ADDED);
}
if(tokenNode = xmlNode.child("is_spoiler"); tokenNode)
{
if(ConvertString<sint32>(tokenNode.child_value()) > 0)
obj.SetFlag(DownloadedDataBase::FLAGS::IS_SPOILER);
}
if(tokenNode = xmlNode.child("mii"); tokenNode)
{
std::vector<uint8> miiData = NCrypto::base64Decode(tokenNode.child_value());
if(miiData.size() != 96)
{
cemuLog_log(LogType::Force, "[Olive-XML] DownloadedSystemTopicData mii data is not valid (incorrect size)");
return false;
}
memcpy(obj.miiData, miiData.data(), miiData.size());
obj.SetFlag(DownloadedDataBase::FLAGS::HAS_MII_DATA);
}
if(tokenNode = xmlNode.child("pid"); tokenNode)
{
obj.userPid = ConvertString<uint32>(tokenNode.child_value());
}
if(tokenNode = xmlNode.child("screen_name"); tokenNode)
{
SetStringUC2(obj.miiNickname, tokenNode.child_value(), true);
}
if(tokenNode = xmlNode.child("region_id"); tokenNode)
{
obj.regionId = ConvertString<uint32>(tokenNode.child_value());
}
if(tokenNode = xmlNode.child("platform_id"); tokenNode)
{
obj.platformId = ConvertString<uint8>(tokenNode.child_value());
}
if(tokenNode = xmlNode.child("language_id"); tokenNode)
{
obj.languageId = ConvertString<uint8>(tokenNode.child_value());
}
if(tokenNode = xmlNode.child("country_id"); tokenNode)
{
obj.countryId = ConvertString<uint8>(tokenNode.child_value());
}
return true;
}
bool ParseXML_DownloadedPostData(DownloadedPostData& obj, pugi::xml_node& xmlNode)
{
pugi::xml_node tokenNode;
if(tokenNode = xmlNode.child("community_id"); tokenNode)
obj.communityId = ConvertString<uint32>(tokenNode.child_value());
if(tokenNode = xmlNode.child("empathy_count"); tokenNode)
obj.empathyCount = ConvertString<uint32>(tokenNode.child_value());
if(tokenNode = xmlNode.child("reply_count"); tokenNode)
obj.commentCount = ConvertString<uint32>(tokenNode.child_value());
return ParseXml_DownloadedDataBase(obj.downloadedDataBase, xmlNode);
}
bool ParseXML_DownloadedSystemPostData(hidden::DownloadedSystemPostData& obj, pugi::xml_node& xmlNode)
{
pugi::xml_node tokenNode;
if(tokenNode = xmlNode.child("title_id"); tokenNode)
obj.titleId = ConvertString<uint64>(tokenNode.child_value());
return ParseXML_DownloadedPostData(obj.downloadedPostData, xmlNode);
}
bool ParseXML_DownloadedTopicData(DownloadedTopicData& obj, pugi::xml_node& xmlNode)
{
pugi::xml_node tokenNode;
if(tokenNode = xmlNode.child("community_id"); tokenNode)
obj.communityId = ConvertString<uint32>(tokenNode.child_value());
return true;
}
bool Parse_DownloadedSystemTopicData(hidden::DownloadedSystemTopicData& obj, pugi::xml_node& xmlNode)
{
if(!ParseXML_DownloadedTopicData(obj.downloadedTopicData, xmlNode))
return false;
pugi::xml_node tokenNode;
if(tokenNode = xmlNode.child("name"); tokenNode)
{
SetStringUC2(obj.titleText, tokenNode.child_value(), true);
obj.downloadedTopicData.SetFlag(DownloadedTopicData::FLAGS::HAS_TITLE);
}
if(tokenNode = xmlNode.child("is_recommended"); tokenNode)
{
uint32 isRecommended = ConvertString<uint32>(tokenNode.child_value());
if(isRecommended != 0)
obj.downloadedTopicData.SetFlag(DownloadedTopicData::FLAGS::IS_RECOMMENDED);
}
if(tokenNode = xmlNode.child("title_id"); tokenNode)
{
obj.titleId = ConvertString<uint64>(tokenNode.child_value());
}
if(tokenNode = xmlNode.child("title_ids"); tokenNode)
{
cemu_assert_unimplemented();
}
if(tokenNode = xmlNode.child("icon"); tokenNode)
{
std::vector<uint8> iconData = NCrypto::base64Decode(tokenNode.child_value());
if(iconData.size() > sizeof(obj.iconData))
{
cemuLog_log(LogType::Force, "[Olive-XML] DownloadedSystemTopicData icon data is not valid");
return false;
}
obj.iconDataSize = iconData.size();
memcpy(obj.iconData, iconData.data(), iconData.size());
obj.downloadedTopicData.SetFlag(DownloadedTopicData::FLAGS::HAS_ICON_DATA);
}
return true;
}
uint32 GetSystemTopicDataListFromRawData(hidden::DownloadedSystemTopicDataList* downloadedSystemTopicDataList, hidden::DownloadedSystemPostData* downloadedSystemPostData, uint32be* postCountOut, uint32 postCountMax, void* xmlData, uint32 xmlDataSize)
{
// copy xmlData into a temporary buffer since load_buffer_inplace will modify it
std::vector<uint8> buffer;
buffer.resize(xmlDataSize);
memcpy(buffer.data(), xmlData, xmlDataSize);
pugi::xml_document doc;
if (!doc.load_buffer_inplace(buffer.data(), xmlDataSize, pugi::parse_default, pugi::xml_encoding::encoding_utf8))
return -1;
memset(downloadedSystemTopicDataList, 0, sizeof(hidden::DownloadedSystemTopicDataList));
downloadedSystemTopicDataList->topicDataNum = 0;
cemu_assert_debug(doc.child("result").child("topics"));
size_t postCount = 0;
// parse topics
for (pugi::xml_node topicsChildNode : doc.child("result").child("topics").children())
{
const char* name = topicsChildNode.name();
cemuLog_logDebug(LogType::Force, "topicsChildNode.name() = {}", name);
if (strcmp(topicsChildNode.name(), "topic"))
continue;
// parse topic
if(downloadedSystemTopicDataList->topicDataNum > 10)
{
cemuLog_log(LogType::Force, "[Olive-XML] DownloadedSystemTopicDataList exceeded maximum topic count (10)");
return false;
}
auto& topicEntry = downloadedSystemTopicDataList->topicData[downloadedSystemTopicDataList->topicDataNum];
memset(&topicEntry, 0, sizeof(hidden::DownloadedSystemTopicDataList::DownloadedSystemTopicWrapped));
Parse_DownloadedSystemTopicData(topicEntry.downloadedSystemTopicData, topicsChildNode);
downloadedSystemTopicDataList->topicDataNum = downloadedSystemTopicDataList->topicDataNum + 1;
topicEntry.postDataNum = 0;
// parse all posts within the current topic
for (pugi::xml_node personNode : topicsChildNode.child("people").children("person"))
{
for (pugi::xml_node postNode : personNode.child("posts").children("post"))
{
if(postCount >= postCountMax)
{
cemuLog_log(LogType::Force, "[Olive-XML] GetSystemTopicDataListFromRawData exceeded maximum post count");
return false;
}
auto& postEntry = downloadedSystemPostData[postCount];
memset(&postEntry, 0, sizeof(hidden::DownloadedSystemPostData));
bool r = ParseXML_DownloadedSystemPostData(postEntry, postNode);
if(!r)
{
cemuLog_log(LogType::Force, "[Olive-XML] DownloadedSystemPostData parsing failed");
return false;
}
postCount++;
// add post to topic
if(topicEntry.postDataNum >= hidden::DownloadedSystemTopicDataList::MAX_POSTS_PER_TOPIC)
{
cemuLog_log(LogType::Force, "[Olive-XML] DownloadedSystemTopicDataList has too many posts for a single topic (up to {})", hidden::DownloadedSystemTopicDataList::MAX_POSTS_PER_TOPIC);
return false;
}
topicEntry.postDataList[topicEntry.postDataNum] = &postEntry;
topicEntry.postDataNum = topicEntry.postDataNum + 1;
}
}
}
*postCountOut = postCount;
return 0;
}
void loadOlivePostAndTopicTypes()
{
cafeExportRegisterFunc(GetSystemTopicDataListFromRawData, "nn_olv", "GetSystemTopicDataListFromRawData__Q3_2nn3olv6hiddenFPQ4_2nn3olv6hidden29DownloadedSystemTopicDataListPQ4_2nn3olv6hidden24DownloadedSystemPostDataPUiUiPCUcT4", LogType::None);
// DownloadedDataBase getters
cafeExportRegisterFunc(DownloadedDataBase::TestFlags, "nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetUserPid, "nn_olv", "GetUserPid__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetPostDate, "nn_olv", "GetPostDate__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetFeeling, "nn_olv", "GetFeeling__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetRegionId, "nn_olv", "GetRegionId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetPlatformId, "nn_olv", "GetPlatformId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetLanguageId, "nn_olv", "GetLanguageId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetCountryId, "nn_olv", "GetCountryId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetExternalUrl, "nn_olv", "GetExternalUrl__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetMiiData1, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFP12FFLStoreData", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetMiiNickname, "nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetBodyText, "nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetBodyMemo, "nn_olv", "GetBodyMemo__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetTopicTag, "nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetAppData, "nn_olv", "GetAppData__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetAppDataSize, "nn_olv", "GetAppDataSize__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
cafeExportRegisterFunc(DownloadedDataBase::GetMiiData2, "nn_olv", "GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFv", LogType::None);
// DownloadedPostData getters
cafeExportRegisterFunc(DownloadedPostData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedPostData::GetEmpathyCount, "nn_olv", "GetEmpathyCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedPostData::GetCommentCount, "nn_olv", "GetCommentCount__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None);
cafeExportRegisterFunc(DownloadedPostData::GetPostId, "nn_olv", "GetPostId__Q3_2nn3olv18DownloadedPostDataCFv", LogType::None);
// DownloadedSystemPostData getters
cafeExportRegisterFunc(hidden::DownloadedSystemPostData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden24DownloadedSystemPostDataCFv", LogType::None);
// DownloadedTopicData getters
cafeExportRegisterFunc(DownloadedTopicData::GetCommunityId, "nn_olv", "GetCommunityId__Q3_2nn3olv19DownloadedTopicDataCFv", LogType::None);
// DownloadedSystemTopicData getters
cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::TestFlags, "nn_olv", "TestFlags__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFUi", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleId, "nn_olv", "GetTitleId__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIdNum, "nn_olv", "GetTitleIdNum__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleText, "nn_olv", "GetTitleText__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPwUi", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicData::GetTitleIconData, "nn_olv", "GetTitleIconData__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPUcPUiUi", LogType::None);
// DownloadedSystemTopicDataList getters
cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicDataNum, "nn_olv", "GetDownloadedSystemTopicDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFv", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostDataNum, "nn_olv", "GetDownloadedSystemPostDataNum__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemTopicData, "nn_olv", "GetDownloadedSystemTopicData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFi", LogType::None);
cafeExportRegisterFunc(hidden::DownloadedSystemTopicDataList::GetDownloadedSystemPostData, "nn_olv", "GetDownloadedSystemPostData__Q4_2nn3olv6hidden29DownloadedSystemTopicDataListCFiT1", LogType::None);
}
}
}

View file

@ -0,0 +1,430 @@
#pragma once
#include <zlib.h>
namespace nn
{
namespace olv
{
struct DownloadedDataBase
{
enum class FLAGS : uint32
{
HAS_BODY_TEXT = 0x01,
HAS_BODY_MEMO = 0x02,
HAS_EXTERNAL_IMAGE = 0x04,
HAS_EXTERNAL_BINARY_DATA = 0x08,
HAS_MII_DATA = 0x10,
HAS_EXTERNAL_URL = 0x20,
HAS_APP_DATA = 0x40,
HAS_EMPATHY_ADDED = 0x80,
IS_AUTOPOST = 0x100,
IS_SPOILER = 0x200,
IS_NOT_AUTOPOST = 0x400, // autopost flag was explicitly set to false
};
void Reset()
{
memset(this, 0, sizeof(DownloadedDataBase));
}
void SetFlag(FLAGS flag)
{
flags = (FLAGS)((uint32)flags.value() | (uint32)flag);
}
betype<FLAGS> flags;
uint32be userPid;
uint8be postId[32]; // string, up to 22 characters but the buffer is 32 bytes
uint64be postDate;
sint8be feeling;
uint8be _padding0031[3];
uint32be regionId;
uint8be platformId;
uint8be languageId;
uint8be countryId;
uint8be _padding003B;
uint16be bodyText[256];
uint32be bodyTextLength;
uint8be compressedMemoBody[40960];
uint32be compressedMemoBodySize;
uint16be topicTag[152];
uint8be appData[1024];
uint32be appDataLength;
uint8be externalBinaryUrl[256];
uint32be externalBinaryDataSize;
uint8be externalImageDataUrl[256];
uint32be externalImageDataSize;
uint8be externalURL[256];
uint8be miiData[96];
uint16be miiNickname[16];
uint32be _paddingAB00[1344];
uint32be uknC000_someVTableMaybe;
uint32be uknC004;
// getters
// TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi
static bool TestFlags(DownloadedDataBase* _this, DownloadedDataBase::FLAGS flag)
{
return HAS_FLAG((uint32)_this->flags.value(), (uint32)flag);
}
// GetUserPid__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetUserPid(DownloadedDataBase* _this)
{
return _this->userPid;
}
// GetPostDate__Q3_2nn3olv18DownloadedDataBaseCFv
static uint64 GetPostDate(DownloadedDataBase* _this)
{
return _this->postDate;
}
// GetFeeling__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetFeeling(DownloadedDataBase* _this)
{
if(_this->feeling >= 6)
return 0;
return _this->feeling;
}
// GetRegionId__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetRegionId(DownloadedDataBase* _this)
{
return _this->regionId;
}
// GetPlatformId__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetPlatformId(DownloadedDataBase* _this)
{
return _this->platformId;
}
// GetLanguageId__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetLanguageId(DownloadedDataBase* _this)
{
return _this->languageId;
}
// GetCountryId__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetCountryId(DownloadedDataBase* _this)
{
return _this->countryId;
}
// GetExternalUrl__Q3_2nn3olv18DownloadedDataBaseCFv
static uint8be* GetExternalUrl(DownloadedDataBase* _this)
{
if (!TestFlags(_this, FLAGS::HAS_EXTERNAL_URL))
return nullptr;
return _this->externalURL;
}
// GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFP12FFLStoreData
static nnResult GetMiiData1(DownloadedDataBase* _this, void* miiDataOut)
{
if (!TestFlags(_this, FLAGS::HAS_MII_DATA))
return OLV_RESULT_MISSING_DATA;
if (!miiDataOut)
return OLV_RESULT_INVALID_PTR;
memcpy(miiDataOut, _this->miiData, 96);
return OLV_RESULT_SUCCESS;
}
// GetMiiData__Q3_2nn3olv18DownloadedDataBaseCFv
static uint8be* GetMiiData2(DownloadedDataBase* _this)
{
if (!TestFlags(_this, FLAGS::HAS_MII_DATA))
return nullptr;
return _this->miiData;
}
// GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv
static uint16be* GetMiiNickname(DownloadedDataBase* _this)
{
if (_this->miiNickname[0] == 0)
return nullptr;
return _this->miiNickname;
}
// GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi
static nnResult GetBodyText(DownloadedDataBase* _this, uint16be* bodyTextOut, uint32 maxLength)
{
if (!bodyTextOut)
return OLV_RESULT_INVALID_PTR;
if (maxLength == 0)
return OLV_RESULT_NOT_ENOUGH_SIZE;
uint32 outputLength = std::min<uint32>(_this->bodyTextLength, maxLength);
olv_wstrncpy((char16_t*)bodyTextOut, (char16_t*)_this->bodyText, _this->bodyTextLength);
return OLV_RESULT_SUCCESS;
}
// GetBodyMemo__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi
static nnResult GetBodyMemo(DownloadedDataBase* _this, uint8be* bodyMemoOut, uint32* bodyMemoSizeOut, uint32 maxSize)
{
if (!bodyMemoOut)
return OLV_RESULT_INVALID_PTR;
if (maxSize < 0x2582C)
return OLV_RESULT_NOT_ENOUGH_SIZE;
if (!TestFlags(_this, FLAGS::HAS_BODY_MEMO))
return OLV_RESULT_MISSING_DATA;
// uncompress TGA
uLongf decompressedSize = maxSize;
if (uncompress((uint8*)bodyMemoOut, &decompressedSize, (uint8*)_this->compressedMemoBody, _this->compressedMemoBodySize) != Z_OK)
{
cemuLog_log(LogType::Force, "DownloadedSystemTopicData::GetTitleIconData: uncompress failed");
return OLV_RESULT_INVALID_TEXT_FIELD; // status
}
if(bodyMemoSizeOut)
*bodyMemoSizeOut = decompressedSize;
// todo - verify TGA header
return OLV_RESULT_SUCCESS;
}
// GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv
static uint16be* GetTopicTag(DownloadedDataBase* _this)
{
return _this->topicTag;
}
// GetAppData__Q3_2nn3olv18DownloadedDataBaseCFPUcPUiUi
static nnResult GetAppData(DownloadedDataBase* _this, uint8be* appDataOut, uint32* appDataSizeOut, uint32 maxSize)
{
if (!appDataOut)
return OLV_RESULT_INVALID_PTR;
if (!TestFlags(_this, FLAGS::HAS_APP_DATA))
return OLV_RESULT_MISSING_DATA;
uint32 outputSize = std::min<uint32>(maxSize, _this->appDataLength);
memcpy(appDataOut, _this->appData, outputSize);
if(appDataSizeOut)
*appDataSizeOut = outputSize;
return OLV_RESULT_SUCCESS;
}
// GetAppDataSize__Q3_2nn3olv18DownloadedDataBaseCFv
static uint32 GetAppDataSize(DownloadedDataBase* _this)
{
return _this->appDataLength;
}
// GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv
static uint8be* GetPostId(DownloadedDataBase* _this)
{
return _this->postId;
}
// todo:
// DownloadExternalImageData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi
// GetExternalImageDataSize__Q3_2nn3olv18DownloadedDataBaseCFv
// DownloadExternalBinaryData__Q3_2nn3olv18DownloadedDataBaseCFPvPUiUi
// GetExternalBinaryDataSize__Q3_2nn3olv18DownloadedDataBaseCFv
};
static_assert(sizeof(DownloadedDataBase) == 0xC008);
struct DownloadedPostData
{
DownloadedDataBase downloadedDataBase;
uint32be communityId;
uint32be empathyCount;
uint32be commentCount;
uint32be paddingC014[125]; // probably unused?
// getters
// GetCommunityId__Q3_2nn3olv18DownloadedPostDataCFv
static uint32 GetCommunityId(DownloadedPostData* _this)
{
return _this->communityId;
}
// GetEmpathyCount__Q3_2nn3olv18DownloadedPostDataCFv
static uint32 GetEmpathyCount(DownloadedPostData* _this)
{
return _this->empathyCount;
}
// GetCommentCount__Q3_2nn3olv18DownloadedPostDataCFv
static uint32 GetCommentCount(DownloadedPostData* _this)
{
return _this->commentCount;
}
// GetPostId__Q3_2nn3olv18DownloadedPostDataCFv
static uint8be* GetPostId(DownloadedPostData* _this)
{
return _this->downloadedDataBase.postId;
}
};
static_assert(sizeof(DownloadedPostData) == 0xC208);
struct DownloadedTopicData
{
enum class FLAGS
{
IS_RECOMMENDED = 0x01,
HAS_TITLE = 0x02,
HAS_ICON_DATA = 0x04,
};
betype<FLAGS> flags;
uint32be communityId;
int ukn[1022];
void SetFlag(FLAGS flag)
{
flags = (FLAGS)((uint32)flags.value() | (uint32)flag);
}
// GetCommunityId__Q3_2nn3olv19DownloadedTopicDataCFv
static uint32 GetCommunityId(DownloadedTopicData* _this)
{
return _this->communityId;
}
};
static_assert(sizeof(DownloadedTopicData) == 0x1000);
namespace hidden
{
struct DownloadedSystemPostData
{
DownloadedPostData downloadedPostData;
uint64be titleId;
uint32be uknC210[124];
uint32be uknC400;
uint32be uknC404;
// getters
// GetTitleId__Q4_2nn3olv6hidden24DownloadedSystemPostDataCFv
static uint64 GetTitleId(DownloadedSystemPostData* _this)
{
return _this->titleId;
}
};
static_assert(sizeof(DownloadedSystemPostData) == 0xC408);
struct DownloadedSystemTopicData
{
DownloadedTopicData downloadedTopicData;
uint64be titleId;
uint16be titleText[128];
uint8be ukn1108[256];
uint8be iconData[0x1002C];
uint32be iconDataSize;
uint64be titleIds[32];
uint32be titleIdsCount;
uint32be ukn1133C[1841];
// implement getters as static methods for compatibility with CafeExportRegisterFunc()
// TestFlags__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFUi
static bool TestFlags(DownloadedSystemTopicData* _this, DownloadedTopicData::FLAGS flag)
{
return HAS_FLAG((uint32)_this->downloadedTopicData.flags.value(), (uint32)flag);
}
// GetTitleId__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv
static uint64 GetTitleId(DownloadedSystemTopicData* _this)
{
return _this->titleId;
}
// GetTitleIdNum__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFv
static uint32 GetTitleIdNum(DownloadedSystemTopicData* _this)
{
return _this->titleIdsCount;
}
// GetTitleText__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPwUi
static nnResult GetTitleText(DownloadedSystemTopicData* _this, uint16be* titleTextOut, uint32 maxLength)
{
if (!TestFlags(_this, DownloadedTopicData::FLAGS::HAS_TITLE))
return OLV_RESULT_MISSING_DATA;
if (!titleTextOut)
return OLV_RESULT_INVALID_PTR;
memset(titleTextOut, 0, maxLength * sizeof(uint16be));
if (maxLength > 128)
maxLength = 128;
olv_wstrncpy((char16_t*)titleTextOut, (char16_t*)_this->titleText, maxLength);
return OLV_RESULT_SUCCESS;
}
// GetTitleIconData__Q4_2nn3olv6hidden25DownloadedSystemTopicDataCFPUcPUiUi
static nnResult GetTitleIconData(DownloadedSystemTopicData* _this, void* iconDataOut, uint32be* iconSizeOut, uint32 iconDataMaxSize)
{
if (!TestFlags(_this, DownloadedTopicData::FLAGS::HAS_ICON_DATA))
return OLV_RESULT_MISSING_DATA;
if (!iconDataOut)
return OLV_RESULT_INVALID_PTR;
if (iconDataMaxSize < 0x1002C)
return OLV_RESULT_NOT_ENOUGH_SIZE;
uLongf decompressedSize = iconDataMaxSize;
if (uncompress((uint8*)iconDataOut, &decompressedSize, (uint8*)_this->iconData, _this->iconDataSize) != Z_OK)
{
cemuLog_log(LogType::Force, "DownloadedSystemTopicData::GetTitleIconData: uncompress failed");
return OLV_RESULT_INVALID_TEXT_FIELD; // status
}
*iconSizeOut = decompressedSize;
// todo - check for TGA
return OLV_RESULT_SUCCESS;
}
};
static_assert(sizeof(DownloadedSystemTopicData) == 0x13000);
struct DownloadedSystemTopicDataList
{
static constexpr size_t MAX_TOPIC_COUNT = 10;
static constexpr size_t MAX_POSTS_PER_TOPIC = 300;
// 0x134B8 sized wrapper of DownloadedSystemTopicData
struct DownloadedSystemTopicWrapped
{
DownloadedSystemTopicData downloadedSystemTopicData;
uint32be postDataNum;
MEMPTR<DownloadedSystemPostData> postDataList[MAX_POSTS_PER_TOPIC];
uint32 uknPadding;
};
static_assert(offsetof(DownloadedSystemTopicWrapped, postDataNum) == 0x13000);
static_assert(sizeof(DownloadedSystemTopicWrapped) == 0x134B8);
static uint32 GetDownloadedSystemTopicDataNum(DownloadedSystemTopicDataList* _this)
{
return _this->topicDataNum;
};
static uint32 GetDownloadedSystemPostDataNum(DownloadedSystemTopicDataList* _this, uint32 topicIndex)
{
if(topicIndex >= MAX_TOPIC_COUNT)
return 0;
return _this->topicData[topicIndex].postDataNum;
};
static DownloadedSystemTopicData* GetDownloadedSystemTopicData(DownloadedSystemTopicDataList* _this, uint32 topicIndex)
{
if(topicIndex >= MAX_TOPIC_COUNT)
return nullptr;
return &_this->topicData[topicIndex].downloadedSystemTopicData;
};
static DownloadedSystemPostData* GetDownloadedSystemPostData(DownloadedSystemTopicDataList* _this, sint32 topicIndex, sint32 postIndex)
{
if (topicIndex >= MAX_TOPIC_COUNT || postIndex >= MAX_POSTS_PER_TOPIC)
return nullptr;
return _this->topicData[topicIndex].postDataList[postIndex];
}
// member variables
uint32be topicDataNum;
uint32be ukn4;
DownloadedSystemTopicWrapped topicData[MAX_TOPIC_COUNT];
uint32be uknC0F38[50];
};
static_assert(sizeof(DownloadedSystemTopicDataList) == 0xC1000);
}
void loadOlivePostAndTopicTypes();
}
}

View file

@ -203,6 +203,11 @@ namespace save
return ConvertACPToSaveStatus(status);
}
SAVEStatus SAVEUnmountSaveDir()
{
return ConvertACPToSaveStatus(acp::ACPUnmountSaveDir());
}
void _CheckAndMoveLegacySaves()
{
const uint64 titleId = CafeSystem::GetForegroundTitleId();
@ -1518,6 +1523,8 @@ namespace save
void load()
{
osLib_addFunction("nn_save", "SAVEInit", export_SAVEInit);
osLib_addFunction("nn_save", "SAVEInitSaveDir", export_SAVEInitSaveDir);
osLib_addFunction("nn_save", "SAVEGetSharedDataTitlePath", export_SAVEGetSharedDataTitlePath);
@ -1570,5 +1577,15 @@ namespace save
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationAsync", export_SAVEOpenDirOtherNormalApplicationAsync);
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationVariationAsync", export_SAVEOpenDirOtherNormalApplicationVariationAsync);
}
void ResetToDefaultState()
{
if(g_nn_save->initialized)
{
SAVEUnmountSaveDir();
g_nn_save->initialized = false;
}
}
}
}

View file

@ -5,6 +5,7 @@ namespace nn
namespace save
{
void load();
void ResetToDefaultState();
bool GetPersistentIdEx(uint8 accountSlot, uint32* persistentId);
}

View file

@ -0,0 +1,150 @@
#include "nn_spm.h"
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/OS/libs/nn_common.h"
namespace nn
{
namespace spm
{
struct StorageIndex
{
void SetInvalid() { idHigh = 0; idLow = 0; }
void Set(uint64 id) { idHigh = id >> 32; idLow = id & 0xFFFFFFFF; }
uint64 Get() const { return ((uint64)idHigh << 32) | (uint64)idLow; }
uint32be idHigh;
uint32be idLow;
};
enum class CemuStorageIndex
{
MLC = 1,
SLC = 2,
USB = 3,
};
static_assert(sizeof(StorageIndex) == 8);
struct VolumeId
{
char id[16];
};
static_assert(sizeof(VolumeId) == 16);
enum class StorageType : uint32
{
RAW,
WFS,
};
struct StorageInfo
{
char mountPath[640]; // For example: /vol/storage_usb01
char connectionType[8]; // usb
char formatStr[8]; // raw / wfs
uint8 ukn[4];
betype<StorageType> type;
VolumeId volumeId;
};
static_assert(sizeof(StorageInfo) == 680);
struct StorageListItem
{
StorageIndex index;
uint32be ukn04;
betype<StorageType> type;
};
static_assert(sizeof(StorageListItem) == 16);
sint32 GetDefaultExtendedStorageVolumeId(StorageIndex* storageIndex)
{
cemuLog_logDebug(LogType::Force, "GetDefaultExtendedStorageVolumeId() - stub");
storageIndex->SetInvalid(); // we dont emulate USB storage yet
return 0;
}
sint32 GetExtendedStorageIndex(StorageIndex* storageIndex)
{
cemuLog_logDebug(LogType::Force, "GetExtendedStorageIndex() - stub");
storageIndex->SetInvalid(); // we dont emulate USB storage yet
return -1; // this fails if there is none?
}
// nn::spm::GetStorageList((nn::spm::StorageListItem *, unsigned int))
uint32 GetStorageList(StorageListItem* storageList, uint32 maxItems)
{
cemu_assert(maxItems >= 2);
uint32 numItems = 0;
// This should only return USB storages?
// If we return two entries (for SLC and MLC supposedly) then the Wii U menu will complain about two usb storages
// // mlc
// storageList[numItems].index.Set((uint32)CemuStorageIndex::MLC);
// storageList[numItems].ukn04 = 0;
// storageList[numItems].type = StorageType::WFS;
// numItems++;
// // slc
// storageList[numItems].index.Set((uint32)CemuStorageIndex::SLC);
// storageList[numItems].ukn04 = 0;
// storageList[numItems].type = StorageType::WFS;
numItems++;
return numItems;
}
sint32 GetStorageInfo(StorageInfo* storageInfo, StorageIndex* storageIndex)
{
cemuLog_logDebug(LogType::Force, "GetStorageInfo() - stub");
if(storageIndex->Get() == (uint64)CemuStorageIndex::MLC)
{
cemu_assert_unimplemented();
}
else if(storageIndex->Get() == (uint64)CemuStorageIndex::SLC)
{
cemu_assert_unimplemented();
}
else
{
cemu_assert_unimplemented();
}
return 0;
}
sint32 VolumeId_Compare(VolumeId* volumeIdThis, VolumeId* volumeIdOther)
{
auto r = strncmp(volumeIdThis->id, volumeIdOther->id, 16);
cemuLog_logDebug(LogType::Force, "VolumeId_Compare(\"{}\", \"{}\")", volumeIdThis->id, volumeIdOther->id);
return (sint32)r;
}
sint32 WaitStateUpdated(uint64be* waitState)
{
// WaitStateUpdated__Q2_2nn3spmFPUL
cemuLog_logDebug(LogType::Force, "WaitStateUpdated() called");
*waitState = 1;
return 0;
}
void load()
{
cafeExportRegisterFunc(GetDefaultExtendedStorageVolumeId, "nn_spm", "GetDefaultExtendedStorageVolumeId__Q2_2nn3spmFv", LogType::Placeholder);
cafeExportRegisterFunc(GetExtendedStorageIndex, "nn_spm", "GetExtendedStorageIndex__Q2_2nn3spmFPQ3_2nn3spm12StorageIndex", LogType::Placeholder);
cafeExportRegisterFunc(GetStorageList, "nn_spm", "GetStorageList__Q2_2nn3spmFPQ3_2nn3spm15StorageListItemUi", LogType::Placeholder);
cafeExportRegisterFunc(GetStorageInfo, "nn_spm", "GetStorageInfo__Q2_2nn3spmFPQ3_2nn3spm11StorageInfoQ3_2nn3spm12StorageIndex", LogType::Placeholder);
cafeExportRegisterFunc(VolumeId_Compare, "nn_spm", "Compare__Q3_2nn3spm8VolumeIdCFRCQ3_2nn3spm8VolumeId", LogType::Placeholder);
cafeExportRegisterFunc(WaitStateUpdated, "nn_spm", "WaitStateUpdated__Q2_2nn3spmFPUL", LogType::Placeholder);
}
}
}

View file

@ -0,0 +1,9 @@
#pragma once
namespace nn
{
namespace spm
{
void load();
}
}

View file

@ -21,4 +21,4 @@ void nnUdsExport___sti___11_uds_Api_cpp_f5d9abb2(PPCInterpreter_t* hCPU)
void nnUds_load()
{
osLib_addFunction("nn_uds", "__sti___11_uds_Api_cpp_f5d9abb2", nnUdsExport___sti___11_uds_Api_cpp_f5d9abb2);
}
}

View file

@ -209,6 +209,8 @@ namespace snd_core
};
void AXVPB_Init();
void AXResetToDefaultState();
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex);
AXVPB* AXAcquireVoiceEx(uint32 priority, MPTR callbackEx, MPTR userParam);

View file

@ -8,11 +8,12 @@ namespace snd_core
{
sndGeneric_t sndGeneric;
void resetToDefaultState()
void AXResetToDefaultState()
{
memset(&sndGeneric, 0x00, sizeof(sndGeneric));
resetNumProcessedFrames();
}
AXVBP_Reset();
}
bool AXIsInit()
{
@ -77,14 +78,13 @@ namespace snd_core
void AXQuit()
{
cemuLog_logDebug(LogType::Force, "AXQuit called from 0x{:08x}", ppcInterpreterCurrentInstance->spr.LR);
// clean up
AXResetCallbacks();
AXVoiceList_ResetFreeVoiceList();
// todo - should we wait to make sure any active callbacks are finished with execution before we exit AXQuit?
AXResetCallbacks();
// todo - should we wait to make sure any active callbacks are finished with execution before we exit AXQuit?
// request worker thread stop and wait until complete
AXIst_StopThread();
// clean up subsystems
AXVBP_Reset();
sndGeneric.isInitialized = false;
// request worker thread stop and wait until complete
AXIst_StopThread();
}
sint32 AXGetMaxVoices()
@ -500,7 +500,7 @@ namespace snd_core
void loadExports()
{
resetToDefaultState();
AXResetToDefaultState();
loadExportsSndCore1();
loadExportsSndCore2();
@ -513,7 +513,9 @@ namespace snd_core
void reset()
{
sndGeneric.isInitialized = false;
AXOut_reset();
AXResetToDefaultState();
sndGeneric.isInitialized = false;
}
}

View file

@ -194,7 +194,6 @@ namespace snd_core
std::vector<AXVPB*>& AXVoiceList_GetListByPriority(uint32 priority);
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices();
void AXVoiceList_ResetFreeVoiceList();
inline AXVPBInternal_t* GetInternalVoice(const AXVPB* vpb)
{
@ -206,6 +205,8 @@ namespace snd_core
return (uint32)vpb->index;
}
void AXVBP_Reset();
// AXIst
void AXIst_InitThread();
OSThread_t* AXIst_GetThread();

View file

@ -958,6 +958,7 @@ namespace snd_core
void AXIst_InitThread()
{
__AXIstIsProcessingFrame = false;
// create ist message queue
OSInitMessageQueue(__AXIstThreadMsgQueue.GetPtr(), __AXIstThreadMsgArray.GetPtr(), 0x10);
// create thread

View file

@ -40,11 +40,6 @@ namespace snd_core
return vpb;
}
void AXVoiceList_ResetFreeVoiceList()
{
__AXFreeVoices.clear();
}
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices()
{
return __AXFreeVoices;
@ -80,6 +75,13 @@ namespace snd_core
return __AXVoicesPerPriority[priority];
}
void AXVoiceList_Reset()
{
__AXFreeVoices.clear();
for (uint32 i = 0; i < AX_PRIORITY_MAX; i++)
__AXVoicesPerPriority[i].clear();
}
SysAllocator<AXVPBInternal_t, AX_MAX_VOICES> _buffer__AXVPBInternalVoiceArray;
AXVPBInternal_t* __AXVPBInternalVoiceArray;
@ -445,14 +447,22 @@ namespace snd_core
__AXVoiceListSpinlock.unlock();
}
void __AXVPBResetVoices()
{
__AXVPBInternalVoiceArray = _buffer__AXVPBInternalVoiceArray.GetPtr();
__AXVPBInternalVoiceShadowCopyArrayPtr = _buffer__AXVPBInternalVoiceShadowCopyArray.GetPtr();
__AXVPBArrayPtr = _buffer__AXVPBArray.GetPtr();
__AXVPBItdArrayPtr = _buffer__AXVPBItdArray.GetPtr();
memset(__AXVPBInternalVoiceShadowCopyArrayPtr, 0, sizeof(AXVPBInternal_t)*AX_MAX_VOICES);
memset(__AXVPBInternalVoiceArray, 0, sizeof(AXVPBInternal_t)*AX_MAX_VOICES);
memset(__AXVPBItdArrayPtr, 0, sizeof(AXVPBItd)*AX_MAX_VOICES);
memset(__AXVPBArrayPtr, 0, sizeof(AXVPB)*AX_MAX_VOICES);
}
void AXVPBInit()
{
__AXVPBInternalVoiceArray = _buffer__AXVPBInternalVoiceArray.GetPtr();
memset(__AXVPBInternalVoiceShadowCopyArrayPtr, 0, sizeof(AXVPBInternal_t)*AX_MAX_VOICES);
memset(__AXVPBInternalVoiceArray, 0, sizeof(AXVPBInternal_t)*AX_MAX_VOICES);
memset(__AXVPBItdArrayPtr, 0, sizeof(AXVPBItd)*AX_MAX_VOICES);
memset(__AXVPBArrayPtr, 0, sizeof(AXVPB)*AX_MAX_VOICES);
__AXVPBResetVoices();
for (sint32 i = 0; i < AX_MAX_VOICES; i++)
{
AXVPBItd* itd = __AXVPBItdArrayPtr + i;
@ -494,12 +504,16 @@ namespace snd_core
void AXVPB_Init()
{
__AXVPBInternalVoiceShadowCopyArrayPtr = _buffer__AXVPBInternalVoiceShadowCopyArray.GetPtr();
__AXVPBArrayPtr = _buffer__AXVPBArray.GetPtr();
__AXVPBItdArrayPtr = _buffer__AXVPBItdArray.GetPtr();
__AXVPBResetVoices();
AXVPBInit();
}
void AXVBP_Reset()
{
AXVoiceList_Reset();
__AXVPBResetVoices();
}
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex)
{
if (device == AX_DEV_TV)

View file

@ -2,6 +2,7 @@
#include "sysapp.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
typedef struct
{
@ -469,7 +470,6 @@ void sysappExport__SYSGetEShopArgs(PPCInterpreter_t* hCPU)
void sysappExport_SYSGetUPIDFromTitleID(PPCInterpreter_t* hCPU)
{
ppcDefineParamU64(titleId, 0);
cemuLog_logDebug(LogType::Force, "SYSGetUPIDFromTitleID(0x{:08x}{:08x})", hCPU->gpr[3], hCPU->gpr[4]);
uint32 titleIdHigh = (titleId >> 32);
uint32 titleIdLow = (uint32)(titleId & 0xFFFFFFFF);
if ((titleIdHigh & 0xFFFF0000) != 0x50000)
@ -541,32 +541,67 @@ void sysappExport_SYSGetVodArgs(PPCInterpreter_t* hCPU)
osLib_returnFromFunction(hCPU, 1);
}
struct SysLauncherArgs18
{
uint64be caller_id; // titleId
uint64be launch_title; // titleId
uint32be mode;
uint32be slot_id;
};
static_assert(sizeof(SysLauncherArgs18) == 0x18);
struct SysLauncherArgs28
{
uint32 ukn00;
uint32 ukn04;
uint32 ukn08;
uint32 ukn0C;
uint32 ukn10; // caller title id? (8 byte)
uint32 ukn14;
uint32 ukn18; // launched title id? (8 byte)
uint32 ukn1C;
uint32 ukn20; // mode
uint32 ukn24; // slot
// standard args above
uint64be caller_id; // titleId
uint64be launch_title; // titleId
uint32be mode;
uint32be slot_id;
};
static_assert(sizeof(SysLauncherArgs28) == 0x28);
void sysappExport__SYSGetLauncherArgs(PPCInterpreter_t* hCPU)
uint32 _SYSGetLauncherArgs(void* argsOut)
{
cemuLog_logDebug(LogType::Force, "_SYSGetLauncherArgs(0x{:08x}) - todo", hCPU->gpr[3]);
uint32 sdkVersion = coreinit::__OSGetProcessSDKVersion();
if(sdkVersion < 21103)
{
// old format
SysLauncherArgs18* launcherArgs18 = (SysLauncherArgs18*)argsOut;
memset(launcherArgs18, 0, sizeof(SysLauncherArgs18));
}
else
{
// new format
SysLauncherArgs28* launcherArgs28 = (SysLauncherArgs28*)argsOut;
memset(launcherArgs28, 0, sizeof(SysLauncherArgs28));
}
return 0; // return argument is todo
}
// todo: Handle OS library version. Older versions used a different struct (only 0x18 bytes?)
//ppcDefineParamStructPtr(launcherArgs, SysLauncherArgs, 0);
//memset(launcherArgs, 0, sizeof(SysLauncherArgs));
struct SysAccountArgs18
{
uint32be ukn00;
uint32be ukn04;
uint32be ukn08;
uint32be ukn0C;
// shares part above with Standard arg
uint32be slotId; // "slot_id"
uint32be mode; // "mode"
};
uint32 _SYSGetAccountArgs(SysAccountArgs18* argsOut)
{
memset(argsOut, 0, sizeof(SysAccountArgs18));
osLib_returnFromFunction(hCPU, 0); // return argument is todo (probably number of args?)
// sysDeserializeStandardArguments_t ?
return 0;
}
void sysappExport_SYSGetStandardResult(PPCInterpreter_t* hCPU)
@ -574,9 +609,44 @@ void sysappExport_SYSGetStandardResult(PPCInterpreter_t* hCPU)
cemuLog_logDebug(LogType::Force, "SYSGetStandardResult(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5]);
memset(memory_getPointerFromVirtualOffset(hCPU->gpr[3]), 0, 4);
// r3 = uint32be* output
// r4 = pointer to data to parse?
// r5 = size to parse?
osLib_returnFromFunction(hCPU, 0);
}
namespace sysapp
{
void SYSClearSysArgs()
{
cemuLog_logDebug(LogType::Force, "SYSClearSysArgs()");
coreinit::__OSClearCopyData();
}
uint32 _SYSLaunchTitleByPathFromLauncher(const char* path, uint32 pathLength)
{
coreinit::__OSClearCopyData();
_SYSAppendCallerInfo();
return coreinit::OSLaunchTitleByPathl(path, pathLength, 0);
}
uint32 SYSRelaunchTitle(uint32 argc, MEMPTR<char>* argv)
{
// calls ACPCheckSelfTitleNotReferAccountLaunch?
coreinit::__OSClearCopyData();
_SYSAppendCallerInfo();
return coreinit::OSRestartGame(argc, argv);
}
void load()
{
cafeExportRegisterFunc(SYSClearSysArgs, "sysapp", "SYSClearSysArgs", LogType::Placeholder);
cafeExportRegisterFunc(_SYSLaunchTitleByPathFromLauncher, "sysapp", "_SYSLaunchTitleByPathFromLauncher", LogType::Placeholder);
cafeExportRegisterFunc(SYSRelaunchTitle, "sysapp", "SYSRelaunchTitle", LogType::Placeholder);
}
}
// register sysapp functions
void sysapp_load()
{
@ -592,6 +662,10 @@ void sysapp_load()
osLib_addFunction("sysapp", "SYSGetVodArgs", sysappExport_SYSGetVodArgs);
osLib_addFunction("sysapp", "_SYSGetLauncherArgs", sysappExport__SYSGetLauncherArgs);
osLib_addFunction("sysapp", "SYSGetStandardResult", sysappExport_SYSGetStandardResult);
cafeExportRegisterFunc(_SYSGetLauncherArgs, "sysapp", "_SYSGetLauncherArgs", LogType::Placeholder);
cafeExportRegisterFunc(_SYSGetAccountArgs, "sysapp", "_SYSGetAccountArgs", LogType::Placeholder);
sysapp::load();
}