mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-26 02:54:17 +01:00
Initial support for title switching + better Wii U menu compatibility (#907)
This commit is contained in:
parent
bfbeeae6f6
commit
2200cc0ddf
@ -1,12 +1,13 @@
|
|||||||
#include "Account.h"
|
#include "Account.h"
|
||||||
#include "util/helpers/helpers.h"
|
#include "util/helpers/helpers.h"
|
||||||
#include "util/helpers/SystemException.h"
|
#include "util/helpers/SystemException.h"
|
||||||
|
#include "util/helpers/StringHelpers.h"
|
||||||
#include "config/ActiveSettings.h"
|
#include "config/ActiveSettings.h"
|
||||||
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
||||||
#include "Common/FileStream.h"
|
#include "Common/FileStream.h"
|
||||||
|
#include <boost/random/uniform_int.hpp>
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <boost/random/uniform_int.hpp>
|
|
||||||
|
|
||||||
std::vector<Account> Account::s_account_list;
|
std::vector<Account> Account::s_account_list;
|
||||||
|
|
||||||
@ -460,15 +461,14 @@ OnlineValidator Account::ValidateOnlineFiles() const
|
|||||||
|
|
||||||
void Account::ParseFile(class FileStream* file)
|
void Account::ParseFile(class FileStream* file)
|
||||||
{
|
{
|
||||||
std::vector<std::string> buffer;
|
std::vector<uint8> buffer;
|
||||||
|
buffer.resize(file->GetSize());
|
||||||
std::string tmp;
|
if( file->readData(buffer.data(), buffer.size()) != buffer.size())
|
||||||
while (file->readLine(tmp))
|
throw std::system_error(AccountErrc::ParseError);
|
||||||
buffer.emplace_back(tmp);
|
for (const auto& s : StringHelpers::StringLineIterator(buffer))
|
||||||
for (const auto& s : buffer)
|
|
||||||
{
|
{
|
||||||
std::string_view view = s;
|
std::string_view view = s;
|
||||||
const auto find = view.find(L'=');
|
const auto find = view.find('=');
|
||||||
if (find == std::string_view::npos)
|
if (find == std::string_view::npos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -240,6 +240,8 @@ add_library(CemuCafe
|
|||||||
IOSU/nn/iosu_nn_service.h
|
IOSU/nn/iosu_nn_service.h
|
||||||
IOSU/PDM/iosu_pdm.cpp
|
IOSU/PDM/iosu_pdm.cpp
|
||||||
IOSU/PDM/iosu_pdm.h
|
IOSU/PDM/iosu_pdm.h
|
||||||
|
IOSU/ODM/iosu_odm.cpp
|
||||||
|
IOSU/ODM/iosu_odm.h
|
||||||
OS/common/OSCommon.cpp
|
OS/common/OSCommon.cpp
|
||||||
OS/common/OSCommon.h
|
OS/common/OSCommon.h
|
||||||
OS/common/OSUtil.h
|
OS/common/OSUtil.h
|
||||||
@ -399,6 +401,8 @@ add_library(CemuCafe
|
|||||||
OS/libs/nn_idbe/nn_idbe.h
|
OS/libs/nn_idbe/nn_idbe.h
|
||||||
OS/libs/nn_ndm/nn_ndm.cpp
|
OS/libs/nn_ndm/nn_ndm.cpp
|
||||||
OS/libs/nn_ndm/nn_ndm.h
|
OS/libs/nn_ndm/nn_ndm.h
|
||||||
|
OS/libs/nn_spm/nn_spm.cpp
|
||||||
|
OS/libs/nn_spm/nn_spm.h
|
||||||
OS/libs/nn_nfp/AmiiboCrypto.h
|
OS/libs/nn_nfp/AmiiboCrypto.h
|
||||||
OS/libs/nn_nfp/nn_nfp.cpp
|
OS/libs/nn_nfp/nn_nfp.cpp
|
||||||
OS/libs/nn_nfp/nn_nfp.h
|
OS/libs/nn_nfp/nn_nfp.h
|
||||||
@ -416,6 +420,8 @@ add_library(CemuCafe
|
|||||||
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
|
OS/libs/nn_olv/nn_olv_UploadCommunityTypes.h
|
||||||
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
|
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
|
||||||
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
|
OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.h
|
||||||
|
OS/libs/nn_olv/nn_olv_PostTypes.cpp
|
||||||
|
OS/libs/nn_olv/nn_olv_PostTypes.h
|
||||||
OS/libs/nn_pdm/nn_pdm.cpp
|
OS/libs/nn_pdm/nn_pdm.cpp
|
||||||
OS/libs/nn_pdm/nn_pdm.h
|
OS/libs/nn_pdm/nn_pdm.h
|
||||||
OS/libs/nn_save/nn_save.cpp
|
OS/libs/nn_save/nn_save.cpp
|
||||||
@ -464,8 +470,6 @@ add_library(CemuCafe
|
|||||||
OS/RPL/rpl_structs.h
|
OS/RPL/rpl_structs.h
|
||||||
OS/RPL/rpl_symbol_storage.cpp
|
OS/RPL/rpl_symbol_storage.cpp
|
||||||
OS/RPL/rpl_symbol_storage.h
|
OS/RPL/rpl_symbol_storage.h
|
||||||
TitleList/BaseInfo.cpp
|
|
||||||
TitleList/BaseInfo.h
|
|
||||||
TitleList/GameInfo.h
|
TitleList/GameInfo.h
|
||||||
TitleList/ParsedMetaXml.h
|
TitleList/ParsedMetaXml.h
|
||||||
TitleList/SaveInfo.cpp
|
TitleList/SaveInfo.cpp
|
||||||
|
@ -45,8 +45,9 @@
|
|||||||
// IOSU initializer functions
|
// IOSU initializer functions
|
||||||
#include "Cafe/IOSU/kernel/iosu_kernel.h"
|
#include "Cafe/IOSU/kernel/iosu_kernel.h"
|
||||||
#include "Cafe/IOSU/fsa/iosu_fsa.h"
|
#include "Cafe/IOSU/fsa/iosu_fsa.h"
|
||||||
|
#include "Cafe/IOSU/ODM/iosu_odm.h"
|
||||||
|
|
||||||
// Cafe OS initializer functions
|
// Cafe OS initializer and shutdown functions
|
||||||
#include "Cafe/OS/libs/avm/avm.h"
|
#include "Cafe/OS/libs/avm/avm.h"
|
||||||
#include "Cafe/OS/libs/drmapp/drmapp.h"
|
#include "Cafe/OS/libs/drmapp/drmapp.h"
|
||||||
#include "Cafe/OS/libs/TCL/TCL.h"
|
#include "Cafe/OS/libs/TCL/TCL.h"
|
||||||
@ -61,6 +62,7 @@
|
|||||||
#include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
|
#include "Cafe/OS/libs/nn_cmpt/nn_cmpt.h"
|
||||||
#include "Cafe/OS/libs/nn_ccr/nn_ccr.h"
|
#include "Cafe/OS/libs/nn_ccr/nn_ccr.h"
|
||||||
#include "Cafe/OS/libs/nn_temp/nn_temp.h"
|
#include "Cafe/OS/libs/nn_temp/nn_temp.h"
|
||||||
|
#include "Cafe/OS/libs/nn_save/nn_save.h"
|
||||||
|
|
||||||
// HW interfaces
|
// HW interfaces
|
||||||
#include "Cafe/HW/SI/si.h"
|
#include "Cafe/HW/SI/si.h"
|
||||||
@ -284,7 +286,7 @@ struct
|
|||||||
|
|
||||||
static_assert(sizeof(SharedDataEntry) == 0x1C);
|
static_assert(sizeof(SharedDataEntry) == 0x1C);
|
||||||
|
|
||||||
uint32 loadSharedData()
|
uint32 LoadSharedData()
|
||||||
{
|
{
|
||||||
// check if font files are dumped
|
// check if font files are dumped
|
||||||
bool hasAllShareddataFiles = true;
|
bool hasAllShareddataFiles = true;
|
||||||
@ -421,49 +423,40 @@ void cemu_initForGame()
|
|||||||
coreinit::OSRunThread(initialThread, PPCInterpreter_makeCallableExportDepr(coreinit_start), 0, nullptr);
|
coreinit::OSRunThread(initialThread, PPCInterpreter_makeCallableExportDepr(coreinit_start), 0, nullptr);
|
||||||
// init AX and start AX I/O thread
|
// init AX and start AX I/O thread
|
||||||
snd_core::AXOut_init();
|
snd_core::AXOut_init();
|
||||||
// init ppc recompiler
|
|
||||||
PPCRecompiler_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cemu_deinitForGame()
|
|
||||||
{
|
|
||||||
// reset audio
|
|
||||||
snd_core::AXOut_reset();
|
|
||||||
snd_core::reset();
|
|
||||||
// reset alarms
|
|
||||||
coreinit::OSAlarm_resetAll();
|
|
||||||
// delete all threads
|
|
||||||
PPCCore_deleteAllThreads();
|
|
||||||
// reset mount paths
|
|
||||||
fsc_unmountAll();
|
|
||||||
// reset RPL loader
|
|
||||||
RPLLoader_ResetState();
|
|
||||||
// reset GX2
|
|
||||||
GX2::_GX2DriverReset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace CafeSystem
|
namespace CafeSystem
|
||||||
{
|
{
|
||||||
void InitVirtualMlcStorage();
|
void InitVirtualMlcStorage();
|
||||||
void MlcStorageMountTitle(TitleInfo& titleInfo);
|
void MlcStorageMountTitle(TitleInfo& titleInfo);
|
||||||
|
void MlcStorageUnmountAllTitles();
|
||||||
|
|
||||||
|
static bool s_initialized = false;
|
||||||
|
static SystemImplementation* s_implementation{nullptr};
|
||||||
bool sLaunchModeIsStandalone = false;
|
bool sLaunchModeIsStandalone = false;
|
||||||
|
std::optional<std::vector<std::string>> s_overrideArgs;
|
||||||
|
|
||||||
bool sSystemRunning = false;
|
bool sSystemRunning = false;
|
||||||
TitleId sForegroundTitleId = 0;
|
TitleId sForegroundTitleId = 0;
|
||||||
|
|
||||||
GameInfo2 sGameInfo_ForegroundTitle;
|
GameInfo2 sGameInfo_ForegroundTitle;
|
||||||
|
|
||||||
|
// initialize all subsystems which are persistent and don't depend on a game running
|
||||||
void Initialize()
|
void Initialize()
|
||||||
{
|
{
|
||||||
static bool s_initialized = false;
|
|
||||||
if (s_initialized)
|
if (s_initialized)
|
||||||
return;
|
return;
|
||||||
s_initialized = true;
|
s_initialized = true;
|
||||||
|
// init core systems
|
||||||
|
fsc_init();
|
||||||
|
memory_init();
|
||||||
|
PPCCore_init();
|
||||||
|
RPLLoader_InitState();
|
||||||
// allocate memory for all SysAllocators
|
// allocate memory for all SysAllocators
|
||||||
// must happen before all COS modules, but also before iosu::kernel::Init()
|
// must happen before COS module init, but also before iosu::kernel::Initialize()
|
||||||
SysAllocatorContainer::GetInstance().Initialize();
|
SysAllocatorContainer::GetInstance().Initialize();
|
||||||
// init IOSU
|
// init IOSU
|
||||||
|
iosuCrypto_init();
|
||||||
iosu::kernel::Initialize();
|
iosu::kernel::Initialize();
|
||||||
iosu::fsa::Initialize();
|
iosu::fsa::Initialize();
|
||||||
iosuIoctl_init();
|
iosuIoctl_init();
|
||||||
@ -476,6 +469,7 @@ namespace CafeSystem
|
|||||||
iosu::boss_init();
|
iosu::boss_init();
|
||||||
iosu::nim::Initialize();
|
iosu::nim::Initialize();
|
||||||
iosu::pdm::Initialize();
|
iosu::pdm::Initialize();
|
||||||
|
iosu::odm::Initialize();
|
||||||
// init Cafe OS
|
// init Cafe OS
|
||||||
avm::Initialize();
|
avm::Initialize();
|
||||||
drmapp::Initialize();
|
drmapp::Initialize();
|
||||||
@ -493,11 +487,43 @@ namespace CafeSystem
|
|||||||
HW_SI::Initialize();
|
HW_SI::Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetImplementation(SystemImplementation* impl)
|
||||||
|
{
|
||||||
|
s_implementation = impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
cemu_assert_debug(s_initialized);
|
||||||
|
// if a title is running, shut it down
|
||||||
|
if (sSystemRunning)
|
||||||
|
ShutdownTitle();
|
||||||
|
// shutdown persistent subsystems
|
||||||
|
iosu::odm::Shutdown();
|
||||||
|
iosu::act::Stop();
|
||||||
|
iosu::mcp::Shutdown();
|
||||||
|
iosu::fsa::Shutdown();
|
||||||
|
s_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GetInternalVirtualCodeFolder()
|
std::string GetInternalVirtualCodeFolder()
|
||||||
{
|
{
|
||||||
return "/internal/current_title/code/";
|
return "/internal/current_title/code/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MountBaseDirectories()
|
||||||
|
{
|
||||||
|
const auto mlc = ActiveSettings::GetMlcPath();
|
||||||
|
FSCDeviceHostFS_Mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), FSC_PRIORITY_BASE);
|
||||||
|
FSCDeviceHostFS_Mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), FSC_PRIORITY_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnmountBaseDirectories()
|
||||||
|
{
|
||||||
|
fsc_unmount("/vol/storage_mlc01/", FSC_PRIORITY_BASE);
|
||||||
|
fsc_unmount("/cemuBossStorage/", FSC_PRIORITY_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
|
STATUS_CODE LoadAndMountForegroundTitle(TitleId titleId)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
|
cemuLog_log(LogType::Force, "Mounting title {:016x}", (uint64)titleId);
|
||||||
@ -559,10 +585,33 @@ namespace CafeSystem
|
|||||||
return STATUS_CODE::SUCCESS;
|
return STATUS_CODE::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnmountForegroundTitle()
|
||||||
|
{
|
||||||
|
if(sLaunchModeIsStandalone)
|
||||||
|
return;
|
||||||
|
cemu_assert_debug(sGameInfo_ForegroundTitle.IsValid()); // unmounting title which was never mounted?
|
||||||
|
if (!sGameInfo_ForegroundTitle.IsValid())
|
||||||
|
return;
|
||||||
|
sGameInfo_ForegroundTitle.GetBase().Unmount("/vol/content");
|
||||||
|
sGameInfo_ForegroundTitle.GetBase().Unmount(GetInternalVirtualCodeFolder());
|
||||||
|
if (sGameInfo_ForegroundTitle.HasUpdate())
|
||||||
|
{
|
||||||
|
if(auto& update = sGameInfo_ForegroundTitle.GetUpdate(); update.IsValid())
|
||||||
|
{
|
||||||
|
update.Unmount("/vol/content");
|
||||||
|
update.Unmount(GetInternalVirtualCodeFolder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto aocList = sGameInfo_ForegroundTitle.GetAOC();
|
||||||
|
if (!aocList.empty())
|
||||||
|
{
|
||||||
|
TitleInfo& titleAOC = aocList[0];
|
||||||
|
titleAOC.Unmount(fmt::format("/vol/aoc{:016x}", titleAOC.GetAppTitleId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
STATUS_CODE SetupExecutable()
|
STATUS_CODE SetupExecutable()
|
||||||
{
|
{
|
||||||
// mount mlc directories
|
|
||||||
fscDeviceHostFS_mapBaseDirectories_deprecated();
|
|
||||||
// set rpx path from cos.xml if available
|
// set rpx path from cos.xml if available
|
||||||
_pathToBaseExecutable = _pathToExecutable;
|
_pathToBaseExecutable = _pathToExecutable;
|
||||||
if (!sLaunchModeIsStandalone)
|
if (!sLaunchModeIsStandalone)
|
||||||
@ -597,26 +646,37 @@ namespace CafeSystem
|
|||||||
return STATUS_CODE::SUCCESS;
|
return STATUS_CODE::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupMemorySpace()
|
||||||
|
{
|
||||||
|
memory_mapForCurrentTitle();
|
||||||
|
LoadSharedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyMemorySpace()
|
||||||
|
{
|
||||||
|
memory_unmapForCurrentTitle();
|
||||||
|
}
|
||||||
|
|
||||||
STATUS_CODE PrepareForegroundTitle(TitleId titleId)
|
STATUS_CODE PrepareForegroundTitle(TitleId titleId)
|
||||||
{
|
{
|
||||||
CafeTitleList::WaitForMandatoryScan();
|
CafeTitleList::WaitForMandatoryScan();
|
||||||
sLaunchModeIsStandalone = false;
|
sLaunchModeIsStandalone = false;
|
||||||
|
_pathToExecutable.clear();
|
||||||
TitleIdParser tip(titleId);
|
TitleIdParser tip(titleId);
|
||||||
if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE)
|
if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC || tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE)
|
||||||
cemuLog_log(LogType::Force, "Launched titleId is not the base of a title");
|
cemuLog_log(LogType::Force, "Launched titleId is not the base of a title");
|
||||||
|
// mount mlc storage
|
||||||
|
MountBaseDirectories();
|
||||||
// mount title folders
|
// mount title folders
|
||||||
STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
|
STATUS_CODE r = LoadAndMountForegroundTitle(titleId);
|
||||||
if (r != STATUS_CODE::SUCCESS)
|
if (r != STATUS_CODE::SUCCESS)
|
||||||
return r;
|
return r;
|
||||||
// map memory
|
// setup memory space and PPC recompiler
|
||||||
memory_mapForCurrentTitle();
|
SetupMemorySpace();
|
||||||
// load RPX
|
PPCRecompiler_init();
|
||||||
r = SetupExecutable();
|
r = SetupExecutable(); // load RPX
|
||||||
if (r != STATUS_CODE::SUCCESS)
|
if (r != STATUS_CODE::SUCCESS)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
loadSharedData();
|
|
||||||
InitVirtualMlcStorage();
|
InitVirtualMlcStorage();
|
||||||
return STATUS_CODE::SUCCESS;
|
return STATUS_CODE::SUCCESS;
|
||||||
}
|
}
|
||||||
@ -655,10 +715,11 @@ namespace CafeSystem
|
|||||||
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
|
uint32 h = generateHashFromRawRPXData(execData->data(), execData->size());
|
||||||
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
|
sForegroundTitleId = 0xFFFFFFFF00000000ULL | (uint64)h;
|
||||||
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
|
cemuLog_log(LogType::Force, "Generated placeholder TitleId: {:016x}", sForegroundTitleId);
|
||||||
|
// setup memory space and ppc recompiler
|
||||||
|
SetupMemorySpace();
|
||||||
|
PPCRecompiler_init();
|
||||||
// load executable
|
// load executable
|
||||||
memory_mapForCurrentTitle();
|
|
||||||
SetupExecutable();
|
SetupExecutable();
|
||||||
loadSharedData();
|
|
||||||
InitVirtualMlcStorage();
|
InitVirtualMlcStorage();
|
||||||
return STATUS_CODE::SUCCESS;
|
return STATUS_CODE::SUCCESS;
|
||||||
}
|
}
|
||||||
@ -703,6 +764,13 @@ namespace CafeSystem
|
|||||||
return sGameInfo_ForegroundTitle.GetVersion();
|
return sGameInfo_ForegroundTitle.GetVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetForegroundTitleSDKVersion()
|
||||||
|
{
|
||||||
|
if (sLaunchModeIsStandalone)
|
||||||
|
return 999999;
|
||||||
|
return sGameInfo_ForegroundTitle.GetSDKVersion();
|
||||||
|
}
|
||||||
|
|
||||||
CafeConsoleRegion GetForegroundTitleRegion()
|
CafeConsoleRegion GetForegroundTitleRegion()
|
||||||
{
|
{
|
||||||
if (sLaunchModeIsStandalone)
|
if (sLaunchModeIsStandalone)
|
||||||
@ -740,6 +808,26 @@ namespace CafeSystem
|
|||||||
return sGameInfo_ForegroundTitle.GetBase().GetArgStr();
|
return sGameInfo_ForegroundTitle.GetBase().GetArgStr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when switching titles custom parameters can be passed, returns true if override args are used
|
||||||
|
bool GetOverrideArgStr(std::vector<std::string>& args)
|
||||||
|
{
|
||||||
|
args.clear();
|
||||||
|
if(!s_overrideArgs)
|
||||||
|
return false;
|
||||||
|
args = *s_overrideArgs;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOverrideArgs(std::span<std::string> args)
|
||||||
|
{
|
||||||
|
s_overrideArgs = std::vector<std::string>(args.begin(), args.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnsetOverrideArgs()
|
||||||
|
{
|
||||||
|
s_overrideArgs = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
// pick platform region based on title region
|
// pick platform region based on title region
|
||||||
CafeConsoleRegion GetPlatformRegion()
|
CafeConsoleRegion GetPlatformRegion()
|
||||||
{
|
{
|
||||||
@ -756,24 +844,7 @@ namespace CafeSystem
|
|||||||
|
|
||||||
void UnmountCurrentTitle()
|
void UnmountCurrentTitle()
|
||||||
{
|
{
|
||||||
TitleInfo& titleBase = sGameInfo_ForegroundTitle.GetBase();
|
UnmountForegroundTitle();
|
||||||
if (titleBase.IsValid())
|
|
||||||
titleBase.UnmountAll();
|
|
||||||
if (sGameInfo_ForegroundTitle.HasUpdate())
|
|
||||||
{
|
|
||||||
TitleInfo& titleUpdate = sGameInfo_ForegroundTitle.GetUpdate();
|
|
||||||
if (titleUpdate.IsValid())
|
|
||||||
titleUpdate.UnmountAll();
|
|
||||||
}
|
|
||||||
if (sGameInfo_ForegroundTitle.HasAOC())
|
|
||||||
{
|
|
||||||
auto titleInfoList = sGameInfo_ForegroundTitle.GetAOC();
|
|
||||||
for(auto& it : titleInfoList)
|
|
||||||
{
|
|
||||||
if (it.IsValid())
|
|
||||||
it.UnmountAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fsc_unmount("/internal/code/", FSC_PRIORITY_BASE);
|
fsc_unmount("/internal/code/", FSC_PRIORITY_BASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,12 +854,22 @@ namespace CafeSystem
|
|||||||
return;
|
return;
|
||||||
coreinit::OSSchedulerEnd();
|
coreinit::OSSchedulerEnd();
|
||||||
Latte_Stop();
|
Latte_Stop();
|
||||||
|
// reset Cafe OS userspace modules
|
||||||
|
snd_core::reset();
|
||||||
|
coreinit::OSAlarm_Shutdown();
|
||||||
|
GX2::_GX2DriverReset();
|
||||||
|
nn::save::ResetToDefaultState();
|
||||||
|
coreinit::__OSDeleteAllActivePPCThreads();
|
||||||
|
RPLLoader_ResetState();
|
||||||
|
// stop time tracking
|
||||||
iosu::pdm::Stop();
|
iosu::pdm::Stop();
|
||||||
iosu::act::Stop();
|
// reset Cemu subsystems
|
||||||
iosu::mcp::Shutdown();
|
PPCRecompiler_Shutdown();
|
||||||
iosu::fsa::Shutdown();
|
|
||||||
GraphicPack2::Reset();
|
GraphicPack2::Reset();
|
||||||
UnmountCurrentTitle();
|
UnmountCurrentTitle();
|
||||||
|
MlcStorageUnmountAllTitles();
|
||||||
|
UnmountBaseDirectories();
|
||||||
|
DestroyMemorySpace();
|
||||||
sSystemRunning = false;
|
sSystemRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,10 +919,7 @@ namespace CafeSystem
|
|||||||
}
|
}
|
||||||
TitleId titleId = titleInfo.GetAppTitleId();
|
TitleId titleId = titleInfo.GetAppTitleId();
|
||||||
if (m_mlcMountedTitles.find(titleId) != m_mlcMountedTitles.end())
|
if (m_mlcMountedTitles.find(titleId) != m_mlcMountedTitles.end())
|
||||||
{
|
|
||||||
cemu_assert_suspicious(); // already mounted
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
std::string mlcStoragePath = GetMlcStoragePath(titleId);
|
std::string mlcStoragePath = GetMlcStoragePath(titleId);
|
||||||
TitleInfo* mountTitleInfo = new TitleInfo(titleInfo);
|
TitleInfo* mountTitleInfo = new TitleInfo(titleInfo);
|
||||||
if (!mountTitleInfo->Mount(mlcStoragePath, "", FSC_PRIORITY_BASE))
|
if (!mountTitleInfo->Mount(mlcStoragePath, "", FSC_PRIORITY_BASE))
|
||||||
@ -868,6 +946,16 @@ namespace CafeSystem
|
|||||||
MlcStorageMountTitle(it);
|
MlcStorageMountTitle(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MlcStorageUnmountAllTitles()
|
||||||
|
{
|
||||||
|
for(auto& it : m_mlcMountedTitles)
|
||||||
|
{
|
||||||
|
std::string mlcStoragePath = GetMlcStoragePath(it.first);
|
||||||
|
it.second->Unmount(mlcStoragePath);
|
||||||
|
}
|
||||||
|
m_mlcMountedTitles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
uint32 GetRPXHashBase()
|
uint32 GetRPXHashBase()
|
||||||
{
|
{
|
||||||
return currentBaseApplicationHash;
|
return currentBaseApplicationHash;
|
||||||
@ -878,4 +966,9 @@ namespace CafeSystem
|
|||||||
return currentUpdatedApplicationHash;
|
return currentUpdatedApplicationHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestRecreateCanvas()
|
||||||
|
{
|
||||||
|
s_implementation->CafeRecreateCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
namespace CafeSystem
|
namespace CafeSystem
|
||||||
{
|
{
|
||||||
|
class SystemImplementation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void CafeRecreateCanvas() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
enum class STATUS_CODE
|
enum class STATUS_CODE
|
||||||
{
|
{
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
@ -15,13 +21,21 @@ namespace CafeSystem
|
|||||||
};
|
};
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
void SetImplementation(SystemImplementation* impl);
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
STATUS_CODE PrepareForegroundTitle(TitleId titleId);
|
STATUS_CODE PrepareForegroundTitle(TitleId titleId);
|
||||||
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
|
STATUS_CODE PrepareForegroundTitleFromStandaloneRPX(const fs::path& path);
|
||||||
void LaunchForegroundTitle();
|
void LaunchForegroundTitle();
|
||||||
bool IsTitleRunning();
|
bool IsTitleRunning();
|
||||||
|
|
||||||
|
bool GetOverrideArgStr(std::vector<std::string>& args);
|
||||||
|
void SetOverrideArgs(std::span<std::string> args);
|
||||||
|
void UnsetOverrideArgs();
|
||||||
|
|
||||||
TitleId GetForegroundTitleId();
|
TitleId GetForegroundTitleId();
|
||||||
uint16 GetForegroundTitleVersion();
|
uint16 GetForegroundTitleVersion();
|
||||||
|
uint32 GetForegroundTitleSDKVersion();
|
||||||
CafeConsoleRegion GetForegroundTitleRegion();
|
CafeConsoleRegion GetForegroundTitleRegion();
|
||||||
CafeConsoleRegion GetPlatformRegion();
|
CafeConsoleRegion GetPlatformRegion();
|
||||||
std::string GetForegroundTitleName();
|
std::string GetForegroundTitleName();
|
||||||
@ -37,6 +51,8 @@ namespace CafeSystem
|
|||||||
|
|
||||||
uint32 GetRPXHashBase();
|
uint32 GetRPXHashBase();
|
||||||
uint32 GetRPXHashUpdated();
|
uint32 GetRPXHashUpdated();
|
||||||
|
|
||||||
|
void RequestRecreateCanvas();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RPLModule* applicationRPX;
|
extern RPLModule* applicationRPX;
|
||||||
|
@ -6,7 +6,7 @@ struct FSCMountPathNode
|
|||||||
std::string path;
|
std::string path;
|
||||||
std::vector<FSCMountPathNode*> subnodes;
|
std::vector<FSCMountPathNode*> subnodes;
|
||||||
FSCMountPathNode* parent;
|
FSCMountPathNode* parent;
|
||||||
// device target and path (if subnodes is empty)
|
// associated device target and path
|
||||||
fscDeviceC* device{ nullptr };
|
fscDeviceC* device{ nullptr };
|
||||||
void* ctx{ nullptr };
|
void* ctx{ nullptr };
|
||||||
std::string deviceTargetPath; // the destination base path for the device, utf8
|
std::string deviceTargetPath; // the destination base path for the device, utf8
|
||||||
@ -17,6 +17,25 @@ struct FSCMountPathNode
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssignDevice(fscDeviceC* device, void* ctx, std::string_view deviceBasePath)
|
||||||
|
{
|
||||||
|
this->device = device;
|
||||||
|
this->ctx = ctx;
|
||||||
|
this->deviceTargetPath = deviceBasePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnassignDevice()
|
||||||
|
{
|
||||||
|
this->device = nullptr;
|
||||||
|
this->ctx = nullptr;
|
||||||
|
this->deviceTargetPath.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRootNode() const
|
||||||
|
{
|
||||||
|
return !parent;
|
||||||
|
}
|
||||||
|
|
||||||
~FSCMountPathNode()
|
~FSCMountPathNode()
|
||||||
{
|
{
|
||||||
for (auto& itr : subnodes)
|
for (auto& itr : subnodes)
|
||||||
@ -141,9 +160,7 @@ sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDev
|
|||||||
fscLeave();
|
fscLeave();
|
||||||
return FSC_STATUS_INVALID_PATH;
|
return FSC_STATUS_INVALID_PATH;
|
||||||
}
|
}
|
||||||
node->device = fscDevice;
|
node->AssignDevice(fscDevice, ctx, targetPathWithSlash);
|
||||||
node->ctx = ctx;
|
|
||||||
node->deviceTargetPath = std::move(targetPathWithSlash);
|
|
||||||
fscLeave();
|
fscLeave();
|
||||||
return FSC_STATUS_OK;
|
return FSC_STATUS_OK;
|
||||||
}
|
}
|
||||||
@ -160,14 +177,13 @@ bool fsc_unmount(std::string_view mountPath, sint32 priority)
|
|||||||
}
|
}
|
||||||
cemu_assert(mountPathNode->priority == priority);
|
cemu_assert(mountPathNode->priority == priority);
|
||||||
cemu_assert(mountPathNode->device);
|
cemu_assert(mountPathNode->device);
|
||||||
// delete node
|
// unassign device
|
||||||
while (mountPathNode && mountPathNode->parent)
|
mountPathNode->UnassignDevice();
|
||||||
|
// prune empty branch
|
||||||
|
while (mountPathNode && !mountPathNode->IsRootNode() && mountPathNode->subnodes.empty() && !mountPathNode->device)
|
||||||
{
|
{
|
||||||
FSCMountPathNode* parent = mountPathNode->parent;
|
FSCMountPathNode* parent = mountPathNode->parent;
|
||||||
cemu_assert(!(!mountPathNode->subnodes.empty() && mountPathNode->device));
|
std::erase(parent->subnodes, mountPathNode);
|
||||||
if (!mountPathNode->subnodes.empty())
|
|
||||||
break;
|
|
||||||
parent->subnodes.erase(std::find(parent->subnodes.begin(), parent->subnodes.end(), mountPathNode));
|
|
||||||
delete mountPathNode;
|
delete mountPathNode;
|
||||||
mountPathNode = parent;
|
mountPathNode = parent;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,6 @@ bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destination
|
|||||||
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
|
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
|
||||||
|
|
||||||
// hostFS device
|
// hostFS device
|
||||||
void fscDeviceHostFS_mapBaseDirectories_deprecated();
|
|
||||||
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
||||||
|
|
||||||
// redirect device
|
// redirect device
|
||||||
|
@ -289,13 +289,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void fscDeviceHostFS_mapBaseDirectories_deprecated()
|
|
||||||
{
|
|
||||||
const auto mlc = ActiveSettings::GetMlcPath();
|
|
||||||
fsc_mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
|
||||||
fsc_mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
|
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
|
||||||
{
|
{
|
||||||
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
|
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
|
||||||
|
@ -100,11 +100,6 @@ PPCInterpreter_t* PPCCore_executeCallbackInternal(uint32 functionMPTR)
|
|||||||
return hCPU;
|
return hCPU;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPCCore_deleteAllThreads()
|
|
||||||
{
|
|
||||||
assert_dbg();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PPCCore_init()
|
void PPCCore_init()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,6 @@ HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);
|
|||||||
|
|
||||||
// HLE scheduler
|
// HLE scheduler
|
||||||
|
|
||||||
void PPCCore_deleteAllThreads();
|
|
||||||
void PPCInterpreter_relinquishTimeslice();
|
void PPCInterpreter_relinquishTimeslice();
|
||||||
|
|
||||||
void PPCCore_boostQuantum(sint32 numCycles);
|
void PPCCore_boostQuantum(sint32 numCycles);
|
||||||
|
@ -289,11 +289,16 @@ void PPCRecompiler_recompileAtAddress(uint32 address)
|
|||||||
bool r = PPCRecompiler_makeRecompiledFunctionActive(address, range, func, functionEntryPoints);
|
bool r = PPCRecompiler_makeRecompiledFunctionActive(address, range, func, functionEntryPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::thread s_threadRecompiler;
|
||||||
|
std::atomic_bool s_recompilerThreadStopSignal{false};
|
||||||
|
|
||||||
void PPCRecompiler_thread()
|
void PPCRecompiler_thread()
|
||||||
{
|
{
|
||||||
SetThreadName("PPCRecompiler_thread");
|
SetThreadName("PPCRecompiler_thread");
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
if(s_recompilerThreadStopSignal)
|
||||||
|
return;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
// asynchronous recompilation:
|
// asynchronous recompilation:
|
||||||
// 1) take address from queue
|
// 1) take address from queue
|
||||||
@ -326,7 +331,12 @@ void PPCRecompiler_thread()
|
|||||||
|
|
||||||
#define PPC_REC_ALLOC_BLOCK_SIZE (4*1024*1024) // 4MB
|
#define PPC_REC_ALLOC_BLOCK_SIZE (4*1024*1024) // 4MB
|
||||||
|
|
||||||
std::bitset<(MEMORY_CODEAREA_ADDR + MEMORY_CODEAREA_SIZE) / PPC_REC_ALLOC_BLOCK_SIZE> ppcRecompiler_reservedBlockMask;
|
constexpr uint32 PPCRecompiler_GetNumAddressSpaceBlocks()
|
||||||
|
{
|
||||||
|
return (MEMORY_CODEAREA_ADDR + MEMORY_CODEAREA_SIZE + PPC_REC_ALLOC_BLOCK_SIZE - 1) / PPC_REC_ALLOC_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::bitset<PPCRecompiler_GetNumAddressSpaceBlocks()> ppcRecompiler_reservedBlockMask;
|
||||||
|
|
||||||
void PPCRecompiler_reserveLookupTableBlock(uint32 offset)
|
void PPCRecompiler_reserveLookupTableBlock(uint32 offset)
|
||||||
{
|
{
|
||||||
@ -496,16 +506,9 @@ void PPCRecompiler_init()
|
|||||||
MemMapper::AllocateMemory(&(ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom), sizeof(PPCRecompilerInstanceData_t) - offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom), MemMapper::PAGE_PERMISSION::P_RW, true);
|
MemMapper::AllocateMemory(&(ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom), sizeof(PPCRecompilerInstanceData_t) - offsetof(PPCRecompilerInstanceData_t, _x64XMM_xorNegateMaskBottom), MemMapper::PAGE_PERMISSION::P_RW, true);
|
||||||
PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions();
|
PPCRecompilerX64Gen_generateRecompilerInterfaceFunctions();
|
||||||
|
|
||||||
uint32 codeRegionEnd = RPLLoader_GetMaxCodeOffset();
|
PPCRecompiler_allocateRange(0, 0x1000); // the first entry is used for fallback to interpreter
|
||||||
codeRegionEnd = (codeRegionEnd + PPC_REC_ALLOC_BLOCK_SIZE - 1) & ~(PPC_REC_ALLOC_BLOCK_SIZE - 1);
|
PPCRecompiler_allocateRange(mmuRange_TRAMPOLINE_AREA.getBase(), mmuRange_TRAMPOLINE_AREA.getSize());
|
||||||
|
PPCRecompiler_allocateRange(mmuRange_CODECAVE.getBase(), mmuRange_CODECAVE.getSize());
|
||||||
uint32 codeRegionSize = codeRegionEnd - PPC_REC_CODE_AREA_START;
|
|
||||||
cemuLog_logDebug(LogType::Force, "Allocating recompiler tables for range 0x{:08x}-0x{:08x}", PPC_REC_CODE_AREA_START, codeRegionEnd);
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < codeRegionSize; i += PPC_REC_ALLOC_BLOCK_SIZE)
|
|
||||||
{
|
|
||||||
PPCRecompiler_reserveLookupTableBlock(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// init x64 recompiler instance data
|
// init x64 recompiler instance data
|
||||||
ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[0] = 1ULL << 63ULL;
|
ppcRecompilerInstanceData->_x64XMM_xorNegateMaskBottom[0] = 1ULL << 63ULL;
|
||||||
@ -589,6 +592,33 @@ void PPCRecompiler_init()
|
|||||||
ppcRecompilerEnabled = true;
|
ppcRecompilerEnabled = true;
|
||||||
|
|
||||||
// launch recompilation thread
|
// launch recompilation thread
|
||||||
std::thread t_recompiler(PPCRecompiler_thread);
|
s_recompilerThreadStopSignal = false;
|
||||||
t_recompiler.detach();
|
s_threadRecompiler = std::thread(PPCRecompiler_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPCRecompiler_Shutdown()
|
||||||
|
{
|
||||||
|
// shut down recompiler thread
|
||||||
|
s_recompilerThreadStopSignal = true;
|
||||||
|
if(s_threadRecompiler.joinable())
|
||||||
|
s_threadRecompiler.join();
|
||||||
|
// clean up queues
|
||||||
|
while(!PPCRecompilerState.targetQueue.empty())
|
||||||
|
PPCRecompilerState.targetQueue.pop();
|
||||||
|
PPCRecompilerState.invalidationRanges.clear();
|
||||||
|
// clean range store
|
||||||
|
rangeStore_ppcRanges.clear();
|
||||||
|
// clean up memory
|
||||||
|
uint32 numBlocks = PPCRecompiler_GetNumAddressSpaceBlocks();
|
||||||
|
for(uint32 i=0; i<numBlocks; i++)
|
||||||
|
{
|
||||||
|
if(!ppcRecompiler_reservedBlockMask[i])
|
||||||
|
continue;
|
||||||
|
// deallocate
|
||||||
|
uint64 offset = i * PPC_REC_ALLOC_BLOCK_SIZE;
|
||||||
|
MemMapper::FreeMemory(&(ppcRecompilerInstanceData->ppcRecompilerFuncTable[offset/4]), (PPC_REC_ALLOC_BLOCK_SIZE/4)*sizeof(void*), true);
|
||||||
|
MemMapper::FreeMemory(&(ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[offset/4]), (PPC_REC_ALLOC_BLOCK_SIZE/4)*sizeof(void*), true);
|
||||||
|
// mark as unmapped
|
||||||
|
ppcRecompiler_reservedBlockMask[i] = false;
|
||||||
|
}
|
||||||
}
|
}
|
@ -373,6 +373,7 @@ extern PPCRecompilerInstanceData_t* ppcRecompilerInstanceData;
|
|||||||
extern bool ppcRecompilerEnabled;
|
extern bool ppcRecompilerEnabled;
|
||||||
|
|
||||||
void PPCRecompiler_init();
|
void PPCRecompiler_init();
|
||||||
|
void PPCRecompiler_Shutdown();
|
||||||
|
|
||||||
void PPCRecompiler_allocateRange(uint32 startAddress, uint32 size);
|
void PPCRecompiler_allocateRange(uint32 startAddress, uint32 size);
|
||||||
|
|
||||||
|
@ -173,4 +173,5 @@ void LatteRenderTarget_updateViewport();
|
|||||||
// Latte emulation control
|
// Latte emulation control
|
||||||
void Latte_Start();
|
void Latte_Start();
|
||||||
void Latte_Stop();
|
void Latte_Stop();
|
||||||
bool Latte_IsActive();
|
bool Latte_GetStopSignal(); // returns true if stop was requested or if in stopped state
|
||||||
|
void LatteThread_Exit();
|
@ -96,7 +96,7 @@ void LatteAsyncCommands_waitUntilAllProcessed()
|
|||||||
void LatteAsyncCommands_checkAndExecute()
|
void LatteAsyncCommands_checkAndExecute()
|
||||||
{
|
{
|
||||||
// quick check if queue is empty (requires no lock)
|
// quick check if queue is empty (requires no lock)
|
||||||
if (!Latte_IsActive())
|
if (Latte_GetStopSignal())
|
||||||
LatteThread_Exit();
|
LatteThread_Exit();
|
||||||
if (LatteAsyncCommandQueue.empty())
|
if (LatteAsyncCommandQueue.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -257,6 +257,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_map.empty();
|
||||||
|
}
|
||||||
|
|
||||||
const std::map<InternalRange, TNodeObject*>& getAll() const { return m_map; };
|
const std::map<InternalRange, TNodeObject*>& getAll() const { return m_map; };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -455,48 +460,6 @@ public:
|
|||||||
}
|
}
|
||||||
if(m_invalidationRangeEnd <= m_invalidationRangeBegin)
|
if(m_invalidationRangeEnd <= m_invalidationRangeBegin)
|
||||||
m_hasInvalidation = false;
|
m_hasInvalidation = false;
|
||||||
|
|
||||||
//if (resRangeBegin <= m_invalidationRangeBegin)
|
|
||||||
//{
|
|
||||||
// // shrink/replace invalidation range from the bottom
|
|
||||||
// uint32 uploadBegin = m_invalidationRangeBegin;//std::max(m_invalidationRangeBegin, resRangeBegin);
|
|
||||||
// uint32 uploadEnd = std::min(resRangeEnd, m_invalidationRangeEnd);
|
|
||||||
// cemu_assert_debug(uploadEnd >= uploadBegin);
|
|
||||||
// if (uploadBegin != uploadEnd)
|
|
||||||
// checkAndSyncModifications(uploadBegin, uploadEnd, true);
|
|
||||||
// m_invalidationRangeBegin = uploadEnd;
|
|
||||||
// cemu_assert_debug(m_invalidationRangeBegin <= m_invalidationRangeEnd);
|
|
||||||
// if (m_invalidationRangeBegin >= m_invalidationRangeEnd)
|
|
||||||
// m_hasInvalidation = false;
|
|
||||||
//}
|
|
||||||
//else if (resRangeEnd >= m_invalidationRangeEnd)
|
|
||||||
//{
|
|
||||||
// // shrink/replace invalidation range from the top
|
|
||||||
// uint32 uploadBegin = std::max(m_invalidationRangeBegin, resRangeBegin);
|
|
||||||
// uint32 uploadEnd = m_invalidationRangeEnd;// std::min(resRangeEnd, m_invalidationRangeEnd);
|
|
||||||
// cemu_assert_debug(uploadEnd >= uploadBegin);
|
|
||||||
// if (uploadBegin != uploadEnd)
|
|
||||||
// checkAndSyncModifications(uploadBegin, uploadEnd, true);
|
|
||||||
// m_invalidationRangeEnd = uploadBegin;
|
|
||||||
// cemu_assert_debug(m_invalidationRangeBegin <= m_invalidationRangeEnd);
|
|
||||||
// if (m_invalidationRangeBegin >= m_invalidationRangeEnd)
|
|
||||||
// m_hasInvalidation = false;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// // since we cant cut holes into the range upload it in it's entirety
|
|
||||||
// cemu_assert_debug(m_invalidationRangeEnd <= m_rangeEnd);
|
|
||||||
// cemu_assert_debug(m_invalidationRangeBegin >= m_rangeBegin);
|
|
||||||
// cemu_assert_debug(m_invalidationRangeBegin < m_invalidationRangeEnd);
|
|
||||||
// checkAndSyncModifications(m_invalidationRangeBegin, m_invalidationRangeEnd, true);
|
|
||||||
// m_hasInvalidation = false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// todo - dont re-upload the whole range immediately
|
|
||||||
// under ideal circumstances we would only upload the data range requested for the current draw call
|
|
||||||
// but this is a hot path so we can't check
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,6 +790,21 @@ private:
|
|||||||
static std::vector<uint32> g_deallocateQueue;
|
static std::vector<uint32> g_deallocateQueue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static void UnloadAll()
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < s_allCacheNodes.size())
|
||||||
|
{
|
||||||
|
BufferCacheNode* node = s_allCacheNodes[i];
|
||||||
|
node->ReleaseCacheMemoryImmediately();
|
||||||
|
LatteBufferCache_removeSingleNodeFromTree(node);
|
||||||
|
delete node;
|
||||||
|
}
|
||||||
|
for(auto& it : s_allCacheNodes)
|
||||||
|
delete it;
|
||||||
|
s_allCacheNodes.clear();
|
||||||
|
g_deallocateQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
static void ProcessDeallocations()
|
static void ProcessDeallocations()
|
||||||
{
|
{
|
||||||
@ -931,7 +909,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<uint32> BufferCacheNode::g_deallocateQueue;
|
std::vector<uint32> BufferCacheNode::g_deallocateQueue;
|
||||||
|
|
||||||
IntervalTree2<MPTR, BufferCacheNode> g_gpuBufferCache;
|
IntervalTree2<MPTR, BufferCacheNode> g_gpuBufferCache;
|
||||||
|
|
||||||
void LatteBufferCache_removeSingleNodeFromTree(BufferCacheNode* node)
|
void LatteBufferCache_removeSingleNodeFromTree(BufferCacheNode* node)
|
||||||
@ -1009,10 +986,16 @@ void LatteBufferCache_processDeallocations()
|
|||||||
|
|
||||||
void LatteBufferCache_init(size_t bufferSize)
|
void LatteBufferCache_init(size_t bufferSize)
|
||||||
{
|
{
|
||||||
|
cemu_assert_debug(g_gpuBufferCache.empty());
|
||||||
g_gpuBufferHeap.reset(new VHeap(nullptr, (uint32)bufferSize));
|
g_gpuBufferHeap.reset(new VHeap(nullptr, (uint32)bufferSize));
|
||||||
g_renderer->bufferCache_init((uint32)bufferSize);
|
g_renderer->bufferCache_init((uint32)bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LatteBufferCache_UnloadAll()
|
||||||
|
{
|
||||||
|
BufferCacheNode::UnloadAll();
|
||||||
|
}
|
||||||
|
|
||||||
void LatteBufferCache_getStats(uint32& heapSize, uint32& allocationSize, uint32& allocNum)
|
void LatteBufferCache_getStats(uint32& heapSize, uint32& allocationSize, uint32& allocNum)
|
||||||
{
|
{
|
||||||
g_gpuBufferHeap->getStats(heapSize, allocationSize, allocNum);
|
g_gpuBufferHeap->getStats(heapSize, allocationSize, allocNum);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void LatteBufferCache_init(size_t bufferSize);
|
void LatteBufferCache_init(size_t bufferSize);
|
||||||
|
void LatteBufferCache_UnloadAll();
|
||||||
|
|
||||||
uint32 LatteBufferCache_retrieveDataInCache(MPTR physAddress, uint32 size);
|
uint32 LatteBufferCache_retrieveDataInCache(MPTR physAddress, uint32 size);
|
||||||
void LatteBufferCache_copyStreamoutDataToCache(MPTR physAddress, uint32 size, uint32 streamoutBufferOffset);
|
void LatteBufferCache_copyStreamoutDataToCache(MPTR physAddress, uint32 size, uint32 streamoutBufferOffset);
|
||||||
|
@ -125,7 +125,7 @@ uint32 LatteCP_readU32Deprc()
|
|||||||
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
|
readDistance = (sint32)(gxRingBufferWritePtr - gxRingBufferReadPtr);
|
||||||
if (readDistance != 0)
|
if (readDistance != 0)
|
||||||
break;
|
break;
|
||||||
if (!Latte_IsActive())
|
if (Latte_GetStopSignal())
|
||||||
LatteThread_Exit();
|
LatteThread_Exit();
|
||||||
|
|
||||||
// still no command data available, do some other tasks
|
// still no command data available, do some other tasks
|
||||||
@ -172,7 +172,7 @@ void LatteCP_waitForNWords(uint32 numWords)
|
|||||||
if (readDistance >= waitDistance)
|
if (readDistance >= waitDistance)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!Latte_IsActive())
|
if (Latte_GetStopSignal())
|
||||||
LatteThread_Exit();
|
LatteThread_Exit();
|
||||||
|
|
||||||
// still no command data available, do some other tasks
|
// still no command data available, do some other tasks
|
||||||
|
@ -373,6 +373,7 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
|
|||||||
if ((colorBufferWidth < (sint32)scissorAccessWidth) ||
|
if ((colorBufferWidth < (sint32)scissorAccessWidth) ||
|
||||||
(colorBufferHeight < (sint32)scissorAccessHeight))
|
(colorBufferHeight < (sint32)scissorAccessHeight))
|
||||||
{
|
{
|
||||||
|
// log this?
|
||||||
colorBufferMask &= ~(1<<i);
|
colorBufferMask &= ~(1<<i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ LatteShaderPSInputTable* LatteSHRC_GetPSInputTable()
|
|||||||
return &_activePSImportTable;
|
return &_activePSImportTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
|
void LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
|
||||||
{
|
{
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
auto& cache = LatteSHRC_GetCacheByType(shader->shaderType);
|
auto& cache = LatteSHRC_GetCacheByType(shader->shaderType);
|
||||||
@ -156,10 +156,14 @@ bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
|
|||||||
}
|
}
|
||||||
else if (baseIt->second == shader)
|
else if (baseIt->second == shader)
|
||||||
{
|
{
|
||||||
if (baseIt->second->next)
|
cemu_assert_debug(baseIt->second == shader);
|
||||||
cache.emplace(shader->baseHash, baseIt->second->next);
|
|
||||||
else
|
|
||||||
cache.erase(baseIt);
|
cache.erase(baseIt);
|
||||||
|
if (shader->next)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(shader->baseHash == shader->next->baseHash);
|
||||||
|
cache.emplace(shader->baseHash, shader->next);
|
||||||
|
}
|
||||||
|
shader->next = 0;
|
||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -176,7 +180,7 @@ bool LatteSHRC_RemoveFromCache(LatteDecompilerShader* shader)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removed;
|
cemu_assert(removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteSHRC_RemoveFromCacheByHash(uint64 shader_base_hash, uint64 shader_aux_hash, LatteConst::ShaderType type)
|
void LatteSHRC_RemoveFromCacheByHash(uint64 shader_base_hash, uint64 shader_aux_hash, LatteConst::ShaderType type)
|
||||||
@ -1009,3 +1013,16 @@ void LatteSHRC_Init()
|
|||||||
cemu_assert_debug(sGeometryShaders.empty());
|
cemu_assert_debug(sGeometryShaders.empty());
|
||||||
cemu_assert_debug(sPixelShaders.empty());
|
cemu_assert_debug(sPixelShaders.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LatteSHRC_UnloadAll()
|
||||||
|
{
|
||||||
|
while(!sVertexShaders.empty())
|
||||||
|
LatteShader_free(sVertexShaders.begin()->second);
|
||||||
|
cemu_assert_debug(sVertexShaders.empty());
|
||||||
|
while(!sGeometryShaders.empty())
|
||||||
|
LatteShader_free(sGeometryShaders.begin()->second);
|
||||||
|
cemu_assert_debug(sGeometryShaders.empty());
|
||||||
|
while(!sPixelShaders.empty())
|
||||||
|
LatteShader_free(sPixelShaders.begin()->second);
|
||||||
|
cemu_assert_debug(sPixelShaders.empty());
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||||
|
|
||||||
void LatteSHRC_Init();
|
void LatteSHRC_Init();
|
||||||
|
void LatteSHRC_UnloadAll();
|
||||||
|
|
||||||
void LatteSHRC_ResetCachedShaderHash();
|
void LatteSHRC_ResetCachedShaderHash();
|
||||||
void LatteShaderSHRC_UpdateFetchShader();
|
void LatteShaderSHRC_UpdateFetchShader();
|
||||||
@ -117,11 +118,12 @@ void LatteShader_DumpShader(uint64 baseHash, uint64 auxHash, LatteDecompilerShad
|
|||||||
void LatteShader_DumpRawShader(uint64 baseHash, uint64 auxHash, uint32 type, uint8* programCode, uint32 programLen);
|
void LatteShader_DumpRawShader(uint64 baseHash, uint64 auxHash, uint32 type, uint8* programCode, uint32 programLen);
|
||||||
|
|
||||||
// shader cache file
|
// shader cache file
|
||||||
void LatteShaderCache_load();
|
void LatteShaderCache_Load();
|
||||||
|
void LatteShaderCache_Close();
|
||||||
|
|
||||||
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader);
|
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader);
|
||||||
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount);
|
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount);
|
||||||
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader);
|
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader);
|
||||||
|
|
||||||
// todo - sort this
|
// todo - refactor this
|
||||||
sint32 LatteDecompiler_getTextureSamplerBaseIndex(LatteConst::ShaderType shaderType);
|
sint32 LatteDecompiler_getTextureSamplerBaseIndex(LatteConst::ShaderType shaderType);
|
@ -53,7 +53,7 @@ struct
|
|||||||
sint32 pipelineFileCount;
|
sint32 pipelineFileCount;
|
||||||
}g_shaderCacheLoaderState;
|
}g_shaderCacheLoaderState;
|
||||||
|
|
||||||
FileCache* fc_shaderCacheGeneric = nullptr; // contains hardware and Cemu version independent shader information
|
FileCache* s_shaderCacheGeneric = nullptr; // contains hardware and version independent shader information
|
||||||
|
|
||||||
#define SHADER_CACHE_GENERIC_EXTRA_VERSION 2 // changing this constant will invalidate all hardware-independent cache files
|
#define SHADER_CACHE_GENERIC_EXTRA_VERSION 2 // changing this constant will invalidate all hardware-independent cache files
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ FileCache* fc_shaderCacheGeneric = nullptr; // contains hardware and Cemu versio
|
|||||||
#define SHADER_CACHE_TYPE_PIXEL (2)
|
#define SHADER_CACHE_TYPE_PIXEL (2)
|
||||||
|
|
||||||
bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderInfoSize);
|
bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderInfoSize);
|
||||||
void LatteShaderCache_loadVulkanPipelineCache(uint64 cacheTitleId);
|
void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId);
|
||||||
bool LatteShaderCache_updatePipelineLoadingProgress();
|
bool LatteShaderCache_updatePipelineLoadingProgress();
|
||||||
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines);
|
void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateFunc, bool isPipelines);
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ void LatteShaderCache_drawBackgroundImage(ImTextureID texture, int width, int he
|
|||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_load()
|
void LatteShaderCache_Load()
|
||||||
{
|
{
|
||||||
shaderCacheScreenStats.compiledShaderCount = 0;
|
shaderCacheScreenStats.compiledShaderCount = 0;
|
||||||
shaderCacheScreenStats.vertexShaderCount = 0;
|
shaderCacheScreenStats.vertexShaderCount = 0;
|
||||||
@ -251,21 +251,21 @@ void LatteShaderCache_load()
|
|||||||
LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0);
|
LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0);
|
||||||
// calculate extraVersion for transferable and precompiled shader cache
|
// calculate extraVersion for transferable and precompiled shader cache
|
||||||
uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION;
|
uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION;
|
||||||
fc_shaderCacheGeneric = FileCache::Open(pathGeneric.generic_wstring(), false, transferableExtraVersion); // legacy extra version (1.25.0 - 1.25.1b)
|
s_shaderCacheGeneric = FileCache::Open(pathGeneric, false, transferableExtraVersion); // legacy extra version (1.25.0 - 1.25.1b)
|
||||||
if(!fc_shaderCacheGeneric)
|
if(!s_shaderCacheGeneric)
|
||||||
fc_shaderCacheGeneric = FileCache::Open(pathGeneric.generic_wstring(), true, LatteShaderCache_getShaderCacheExtraVersion(cacheTitleId));
|
s_shaderCacheGeneric = FileCache::Open(pathGeneric, true, LatteShaderCache_getShaderCacheExtraVersion(cacheTitleId));
|
||||||
if(!fc_shaderCacheGeneric)
|
if(!s_shaderCacheGeneric)
|
||||||
{
|
{
|
||||||
// no shader cache available yet
|
// no shader cache available yet
|
||||||
cemuLog_log(LogType::Force, "Unable to open or create shader cache file \"{}\"", _pathToUtf8(pathGeneric));
|
cemuLog_log(LogType::Force, "Unable to open or create shader cache file \"{}\"", _pathToUtf8(pathGeneric));
|
||||||
LatteShaderCache_finish();
|
LatteShaderCache_finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fc_shaderCacheGeneric->UseCompression(false);
|
s_shaderCacheGeneric->UseCompression(false);
|
||||||
|
|
||||||
// load/compile cached shaders
|
// load/compile cached shaders
|
||||||
sint32 entryCount = fc_shaderCacheGeneric->GetMaximumFileIndex();
|
sint32 entryCount = s_shaderCacheGeneric->GetMaximumFileIndex();
|
||||||
g_shaderCacheLoaderState.shaderFileCount = fc_shaderCacheGeneric->GetFileCount();
|
g_shaderCacheLoaderState.shaderFileCount = s_shaderCacheGeneric->GetFileCount();
|
||||||
g_shaderCacheLoaderState.loadedShaderFiles = 0;
|
g_shaderCacheLoaderState.loadedShaderFiles = 0;
|
||||||
|
|
||||||
// get game background loading image
|
// get game background loading image
|
||||||
@ -304,13 +304,13 @@ void LatteShaderCache_load()
|
|||||||
|
|
||||||
auto LoadShadersUpdate = [&]() -> bool
|
auto LoadShadersUpdate = [&]() -> bool
|
||||||
{
|
{
|
||||||
if (loadIndex >= (uint32)fc_shaderCacheGeneric->GetMaximumFileIndex())
|
if (loadIndex >= (uint32)s_shaderCacheGeneric->GetMaximumFileIndex())
|
||||||
return false;
|
return false;
|
||||||
LatteShaderCache_updateCompileQueue(SHADER_CACHE_COMPILE_QUEUE_SIZE - 2);
|
LatteShaderCache_updateCompileQueue(SHADER_CACHE_COMPILE_QUEUE_SIZE - 2);
|
||||||
uint64 name1;
|
uint64 name1;
|
||||||
uint64 name2;
|
uint64 name2;
|
||||||
std::vector<uint8> fileData;
|
std::vector<uint8> fileData;
|
||||||
if (!fc_shaderCacheGeneric->GetFileByIndex(loadIndex, &name1, &name2, fileData))
|
if (!s_shaderCacheGeneric->GetFileByIndex(loadIndex, &name1, &name2, fileData))
|
||||||
{
|
{
|
||||||
loadIndex++;
|
loadIndex++;
|
||||||
return true;
|
return true;
|
||||||
@ -320,7 +320,7 @@ void LatteShaderCache_load()
|
|||||||
{
|
{
|
||||||
// something is wrong with the stored shader, remove entry from shader cache files
|
// something is wrong with the stored shader, remove entry from shader cache files
|
||||||
cemuLog_log(LogType::Force, "Shader cache entry {} invalid, deleting...", loadIndex);
|
cemuLog_log(LogType::Force, "Shader cache entry {} invalid, deleting...", loadIndex);
|
||||||
fc_shaderCacheGeneric->DeleteFile({ name1, name2 });
|
s_shaderCacheGeneric->DeleteFile({name1, name2 });
|
||||||
}
|
}
|
||||||
numLoadedShaders++;
|
numLoadedShaders++;
|
||||||
loadIndex++;
|
loadIndex++;
|
||||||
@ -343,7 +343,7 @@ void LatteShaderCache_load()
|
|||||||
LatteShaderCache_finish();
|
LatteShaderCache_finish();
|
||||||
// if Vulkan then also load pipeline cache
|
// if Vulkan then also load pipeline cache
|
||||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||||
LatteShaderCache_loadVulkanPipelineCache(cacheTitleId);
|
LatteShaderCache_LoadVulkanPipelineCache(cacheTitleId);
|
||||||
|
|
||||||
|
|
||||||
g_renderer->BeginFrame(true);
|
g_renderer->BeginFrame(true);
|
||||||
@ -376,6 +376,8 @@ void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateF
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
if (Latte_GetStopSignal())
|
||||||
|
break; // thread stop requested, cancel shader loading
|
||||||
bool r = loadUpdateFunc();
|
bool r = loadUpdateFunc();
|
||||||
if (!r)
|
if (!r)
|
||||||
break;
|
break;
|
||||||
@ -496,13 +498,15 @@ void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_loadVulkanPipelineCache(uint64 cacheTitleId)
|
void LatteShaderCache_LoadVulkanPipelineCache(uint64 cacheTitleId)
|
||||||
{
|
{
|
||||||
auto& pipelineCache = VulkanPipelineStableCache::GetInstance();
|
auto& pipelineCache = VulkanPipelineStableCache::GetInstance();
|
||||||
g_shaderCacheLoaderState.pipelineFileCount = pipelineCache.BeginLoading(cacheTitleId);
|
g_shaderCacheLoaderState.pipelineFileCount = pipelineCache.BeginLoading(cacheTitleId);
|
||||||
g_shaderCacheLoaderState.loadedPipelines = 0;
|
g_shaderCacheLoaderState.loadedPipelines = 0;
|
||||||
LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true);
|
LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true);
|
||||||
pipelineCache.EndLoading();
|
pipelineCache.EndLoading();
|
||||||
|
if(Latte_GetStopSignal())
|
||||||
|
LatteThread_Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LatteShaderCache_updatePipelineLoadingProgress()
|
bool LatteShaderCache_updatePipelineLoadingProgress()
|
||||||
@ -520,7 +524,7 @@ uint64 LatteShaderCache_getShaderNameInTransferableCache(uint64 baseHash, uint32
|
|||||||
|
|
||||||
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader)
|
void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* fetchShader, uint32 fetchShaderSize, uint8* vertexShader, uint32 vertexShaderSize, uint32* contextRegisters, bool usesGeometryShader)
|
||||||
{
|
{
|
||||||
if (!fc_shaderCacheGeneric)
|
if (!s_shaderCacheGeneric)
|
||||||
return;
|
return;
|
||||||
MemStreamWriter streamWriter(128 * 1024);
|
MemStreamWriter streamWriter(128 * 1024);
|
||||||
// header
|
// header
|
||||||
@ -539,12 +543,12 @@ void LatteShaderCache_writeSeparableVertexShader(uint64 shaderBaseHash, uint64 s
|
|||||||
// write to cache
|
// write to cache
|
||||||
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_VERTEX);
|
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_VERTEX);
|
||||||
std::span<uint8> dataBlob = streamWriter.getResult();
|
std::span<uint8> dataBlob = streamWriter.getResult();
|
||||||
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount)
|
void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* geometryShader, uint32 geometryShaderSize, uint8* gsCopyShader, uint32 gsCopyShaderSize, uint32* contextRegisters, uint32* hleSpecialState, uint32 vsRingParameterCount)
|
||||||
{
|
{
|
||||||
if (!fc_shaderCacheGeneric)
|
if (!s_shaderCacheGeneric)
|
||||||
return;
|
return;
|
||||||
MemStreamWriter streamWriter(128 * 1024);
|
MemStreamWriter streamWriter(128 * 1024);
|
||||||
// header
|
// header
|
||||||
@ -564,12 +568,12 @@ void LatteShaderCache_writeSeparableGeometryShader(uint64 shaderBaseHash, uint64
|
|||||||
// write to cache
|
// write to cache
|
||||||
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_GEOMETRY);
|
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_GEOMETRY);
|
||||||
std::span<uint8> dataBlob = streamWriter.getResult();
|
std::span<uint8> dataBlob = streamWriter.getResult();
|
||||||
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader)
|
void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 shaderAuxHash, uint8* pixelShader, uint32 pixelShaderSize, uint32* contextRegisters, bool usesGeometryShader)
|
||||||
{
|
{
|
||||||
if (!fc_shaderCacheGeneric)
|
if (!s_shaderCacheGeneric)
|
||||||
return;
|
return;
|
||||||
MemStreamWriter streamWriter(128 * 1024);
|
MemStreamWriter streamWriter(128 * 1024);
|
||||||
streamWriter.writeBE<uint8>(1 | (SHADER_CACHE_TYPE_PIXEL << 4)); // version and type (shared field)
|
streamWriter.writeBE<uint8>(1 | (SHADER_CACHE_TYPE_PIXEL << 4)); // version and type (shared field)
|
||||||
@ -585,7 +589,7 @@ void LatteShaderCache_writeSeparablePixelShader(uint64 shaderBaseHash, uint64 sh
|
|||||||
// write to cache
|
// write to cache
|
||||||
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_PIXEL);
|
uint64 shaderCacheName = LatteShaderCache_getShaderNameInTransferableCache(shaderBaseHash, SHADER_CACHE_TYPE_PIXEL);
|
||||||
std::span<uint8> dataBlob = streamWriter.getResult();
|
std::span<uint8> dataBlob = streamWriter.getResult();
|
||||||
fc_shaderCacheGeneric->AddFileAsync({ shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
s_shaderCacheGeneric->AddFileAsync({shaderCacheName, shaderAuxHash }, dataBlob.data(), dataBlob.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteShaderCache_loadOrCompileSeparableShader(LatteDecompilerShader* shader, uint64 shaderBaseHash, uint64 shaderAuxHash)
|
void LatteShaderCache_loadOrCompileSeparableShader(LatteDecompilerShader* shader, uint64 shaderBaseHash, uint64 shaderAuxHash)
|
||||||
@ -759,6 +763,23 @@ bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderIn
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LatteShaderCache_Close()
|
||||||
|
{
|
||||||
|
if(s_shaderCacheGeneric)
|
||||||
|
{
|
||||||
|
delete s_shaderCacheGeneric;
|
||||||
|
s_shaderCacheGeneric = nullptr;
|
||||||
|
}
|
||||||
|
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||||
|
RendererShaderVk::ShaderCacheLoading_Close();
|
||||||
|
else if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||||
|
RendererShaderGL::ShaderCacheLoading_Close();
|
||||||
|
|
||||||
|
// if Vulkan then also close pipeline cache
|
||||||
|
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||||
|
VulkanPipelineStableCache::GetInstance().Close();
|
||||||
|
}
|
||||||
|
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
|
||||||
void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0)
|
void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0)
|
||||||
|
@ -183,9 +183,8 @@ int Latte_ThreadEntry()
|
|||||||
|
|
||||||
// before doing anything with game specific shaders, we need to wait for graphic packs to finish loading
|
// before doing anything with game specific shaders, we need to wait for graphic packs to finish loading
|
||||||
GraphicPack2::WaitUntilReady();
|
GraphicPack2::WaitUntilReady();
|
||||||
// load/init shader cache file
|
// load disk shader cache
|
||||||
LatteShaderCache_load();
|
LatteShaderCache_Load();
|
||||||
|
|
||||||
// init registers
|
// init registers
|
||||||
Latte_LoadInitialRegisters();
|
Latte_LoadInitialRegisters();
|
||||||
// let CPU thread know the GPU is done initializing
|
// let CPU thread know the GPU is done initializing
|
||||||
@ -196,7 +195,7 @@ int Latte_ThreadEntry()
|
|||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
LatteThread_HandleOSScreen();
|
LatteThread_HandleOSScreen();
|
||||||
if (!Latte_IsActive())
|
if (Latte_GetStopSignal())
|
||||||
LatteThread_Exit();
|
LatteThread_Exit();
|
||||||
}
|
}
|
||||||
gxRingBufferReadPtr = gx2WriteGatherPipe.gxRingBuffer;
|
gxRingBufferReadPtr = gx2WriteGatherPipe.gxRingBuffer;
|
||||||
@ -232,19 +231,23 @@ void Latte_Stop()
|
|||||||
sLatteThread.join();
|
sLatteThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Latte_IsActive()
|
bool Latte_GetStopSignal()
|
||||||
{
|
{
|
||||||
return sLatteThreadRunning;
|
return !sLatteThreadRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LatteThread_Exit()
|
void LatteThread_Exit()
|
||||||
{
|
{
|
||||||
if (g_renderer)
|
if (g_renderer)
|
||||||
g_renderer->Shutdown();
|
g_renderer->Shutdown();
|
||||||
|
// clean up vertex/uniform cache
|
||||||
|
LatteBufferCache_UnloadAll();
|
||||||
// clean up texture cache
|
// clean up texture cache
|
||||||
LatteTC_UnloadAllTextures();
|
LatteTC_UnloadAllTextures();
|
||||||
// clean up runtime shader cache
|
// clean up runtime shader cache
|
||||||
// todo
|
LatteSHRC_UnloadAll();
|
||||||
|
// close disk cache
|
||||||
|
LatteShaderCache_Close();
|
||||||
// destroy renderer but make sure that g_renderer remains valid until the destructor has finished
|
// destroy renderer but make sure that g_renderer remains valid until the destructor has finished
|
||||||
if (g_renderer)
|
if (g_renderer)
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ bool s_isLoadingShaders{false};
|
|||||||
|
|
||||||
bool RendererShaderGL::loadBinary()
|
bool RendererShaderGL::loadBinary()
|
||||||
{
|
{
|
||||||
if (!g_programBinaryCache)
|
if (!s_programBinaryCache)
|
||||||
return false;
|
return false;
|
||||||
if (m_isGameShader == false || m_isGfxPackShader)
|
if (m_isGameShader == false || m_isGfxPackShader)
|
||||||
return false; // only non-custom
|
return false; // only non-custom
|
||||||
@ -25,7 +25,7 @@ bool RendererShaderGL::loadBinary()
|
|||||||
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);
|
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);
|
||||||
sint32 fileSize = 0;
|
sint32 fileSize = 0;
|
||||||
std::vector<uint8> cacheFileData;
|
std::vector<uint8> cacheFileData;
|
||||||
if (!g_programBinaryCache->GetFile({ h1, h2 }, cacheFileData))
|
if (!s_programBinaryCache->GetFile({h1, h2 }, cacheFileData))
|
||||||
return false;
|
return false;
|
||||||
if (fileSize < sizeof(uint32))
|
if (fileSize < sizeof(uint32))
|
||||||
{
|
{
|
||||||
@ -51,7 +51,7 @@ bool RendererShaderGL::loadBinary()
|
|||||||
|
|
||||||
void RendererShaderGL::storeBinary()
|
void RendererShaderGL::storeBinary()
|
||||||
{
|
{
|
||||||
if (!g_programBinaryCache)
|
if (!s_programBinaryCache)
|
||||||
return;
|
return;
|
||||||
if (!glGetProgramBinary)
|
if (!glGetProgramBinary)
|
||||||
return;
|
return;
|
||||||
@ -72,7 +72,7 @@ void RendererShaderGL::storeBinary()
|
|||||||
glGetProgramBinary(m_program, binaryLength, NULL, &binaryFormat, storedBinary.data()+sizeof(uint32));
|
glGetProgramBinary(m_program, binaryLength, NULL, &binaryFormat, storedBinary.data()+sizeof(uint32));
|
||||||
*(uint32*)(storedBinary.data() + 0) = binaryFormat;
|
*(uint32*)(storedBinary.data() + 0) = binaryFormat;
|
||||||
// store
|
// store
|
||||||
g_programBinaryCache->AddFileAsync({ h1, h2 }, storedBinary.data(), storedBinary.size());
|
s_programBinaryCache->AddFileAsync({h1, h2 }, storedBinary.data(), storedBinary.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,12 +247,7 @@ void RendererShaderGL::SetUniform4iv(sint32 location, void* data, sint32 count)
|
|||||||
|
|
||||||
void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
|
void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
|
||||||
{
|
{
|
||||||
if (g_programBinaryCache)
|
cemu_assert_debug(!s_programBinaryCache); // should not be set, ShaderCacheLoading_Close() not called?
|
||||||
{
|
|
||||||
delete g_programBinaryCache;
|
|
||||||
g_programBinaryCache = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if cache is enabled
|
// determine if cache is enabled
|
||||||
bool usePrecompiled = false;
|
bool usePrecompiled = false;
|
||||||
switch (ActiveSettings::GetPrecompiledShadersOption())
|
switch (ActiveSettings::GetPrecompiledShadersOption())
|
||||||
@ -279,9 +274,8 @@ void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId)
|
|||||||
{
|
{
|
||||||
const uint32 cacheMagic = GeneratePrecompiledCacheId();
|
const uint32 cacheMagic = GeneratePrecompiledCacheId();
|
||||||
const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId);
|
const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId);
|
||||||
const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring();
|
s_programBinaryCache = FileCache::Open(ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename), true, cacheMagic);
|
||||||
g_programBinaryCache = FileCache::Open(cachePath, true, cacheMagic);
|
if (s_programBinaryCache == nullptr)
|
||||||
if (g_programBinaryCache == nullptr)
|
|
||||||
cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename);
|
cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename);
|
||||||
}
|
}
|
||||||
s_isLoadingShaders = true;
|
s_isLoadingShaders = true;
|
||||||
@ -292,4 +286,15 @@ void RendererShaderGL::ShaderCacheLoading_end()
|
|||||||
s_isLoadingShaders = false;
|
s_isLoadingShaders = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileCache* RendererShaderGL::g_programBinaryCache{};
|
void RendererShaderGL::ShaderCacheLoading_Close()
|
||||||
|
{
|
||||||
|
if(s_programBinaryCache)
|
||||||
|
{
|
||||||
|
delete s_programBinaryCache;
|
||||||
|
s_programBinaryCache = nullptr;
|
||||||
|
}
|
||||||
|
g_compiled_shaders_total = 0;
|
||||||
|
g_compiled_shaders_async = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCache* RendererShaderGL::s_programBinaryCache{};
|
||||||
|
@ -24,6 +24,7 @@ public:
|
|||||||
|
|
||||||
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
|
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
|
||||||
static void ShaderCacheLoading_end();
|
static void ShaderCacheLoading_end();
|
||||||
|
static void ShaderCacheLoading_Close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint m_program;
|
GLuint m_program;
|
||||||
@ -37,6 +38,6 @@ private:
|
|||||||
bool m_shader_attached{ false };
|
bool m_shader_attached{ false };
|
||||||
bool m_isCompiled{ false };
|
bool m_isCompiled{ false };
|
||||||
|
|
||||||
static class FileCache* g_programBinaryCache;
|
static class FileCache* s_programBinaryCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ void Renderer::Shutdown()
|
|||||||
// imgui
|
// imgui
|
||||||
ImGui::DestroyContext(imguiTVContext);
|
ImGui::DestroyContext(imguiTVContext);
|
||||||
ImGui::DestroyContext(imguiPadContext);
|
ImGui::DestroyContext(imguiPadContext);
|
||||||
|
ImGui_ClearFonts();
|
||||||
delete imguiFontAtlas;
|
delete imguiFontAtlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,3 +451,9 @@ void RendererShaderVk::ShaderCacheLoading_end()
|
|||||||
// keep g_spirvCache open since we will write to it while the game is running
|
// keep g_spirvCache open since we will write to it while the game is running
|
||||||
s_isLoadingShadersVk = false;
|
s_isLoadingShadersVk = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererShaderVk::ShaderCacheLoading_Close()
|
||||||
|
{
|
||||||
|
delete s_spirvCache;
|
||||||
|
s_spirvCache = nullptr;
|
||||||
|
}
|
@ -23,6 +23,7 @@ class RendererShaderVk : public RendererShader
|
|||||||
public:
|
public:
|
||||||
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
|
static void ShaderCacheLoading_begin(uint64 cacheTitleId);
|
||||||
static void ShaderCacheLoading_end();
|
static void ShaderCacheLoading_end();
|
||||||
|
static void ShaderCacheLoading_Close();
|
||||||
|
|
||||||
RendererShaderVk(ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& glslCode);
|
RendererShaderVk(ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& glslCode);
|
||||||
virtual ~RendererShaderVk();
|
virtual ~RendererShaderVk();
|
||||||
|
@ -117,6 +117,15 @@ void VulkanPipelineStableCache::EndLoading()
|
|||||||
// keep cache file open for writing of new pipelines
|
// keep cache file open for writing of new pipelines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanPipelineStableCache::Close()
|
||||||
|
{
|
||||||
|
if(s_cache)
|
||||||
|
{
|
||||||
|
delete s_cache;
|
||||||
|
s_cache = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct CachedPipeline
|
struct CachedPipeline
|
||||||
{
|
{
|
||||||
struct ShaderHash
|
struct ShaderHash
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
bool UpdateLoading(uint32& pipelinesLoadedTotal, uint32& pipelinesMissingShaders);
|
bool UpdateLoading(uint32& pipelinesLoadedTotal, uint32& pipelinesMissingShaders);
|
||||||
void EndLoading();
|
void EndLoading();
|
||||||
void LoadPipelineFromCache(std::span<uint8> fileData);
|
void LoadPipelineFromCache(std::span<uint8> fileData);
|
||||||
|
void Close(); // called on title exit
|
||||||
|
|
||||||
bool HasPipelineCached(uint64 baseHash, uint64 pipelineStateHash);
|
bool HasPipelineCached(uint64 baseHash, uint64 pipelineStateHash);
|
||||||
void AddCurrentStateToCache(uint64 baseHash, uint64 pipelineStateHash);
|
void AddCurrentStateToCache(uint64 baseHash, uint64 pipelineStateHash);
|
||||||
|
@ -103,7 +103,7 @@ void MMURange::mapMem()
|
|||||||
|
|
||||||
void MMURange::unmapMem()
|
void MMURange::unmapMem()
|
||||||
{
|
{
|
||||||
cemu_assert_debug(false);
|
MemMapper::FreeMemory(memory_base + baseAddress, size, true);
|
||||||
m_isMapped = false;
|
m_isMapped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +196,15 @@ void memory_mapForCurrentTitle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void memory_unmapForCurrentTitle()
|
||||||
|
{
|
||||||
|
for (auto& itr : g_mmuRanges)
|
||||||
|
{
|
||||||
|
if (itr->isMapped() && !itr->isMappedEarly())
|
||||||
|
itr->unmapMem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void memory_logModifiedMemoryRanges()
|
void memory_logModifiedMemoryRanges()
|
||||||
{
|
{
|
||||||
auto gfxPackMappings = GraphicPack2::GetActiveRAMMappings();
|
auto gfxPackMappings = GraphicPack2::GetActiveRAMMappings();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
void memory_init();
|
void memory_init();
|
||||||
void memory_mapForCurrentTitle();
|
void memory_mapForCurrentTitle();
|
||||||
|
void memory_unmapForCurrentTitle();
|
||||||
void memory_logModifiedMemoryRanges();
|
void memory_logModifiedMemoryRanges();
|
||||||
|
|
||||||
void memory_enableOverlayArena();
|
void memory_enableOverlayArena();
|
||||||
|
155
src/Cafe/IOSU/ODM/iosu_odm.cpp
Normal file
155
src/Cafe/IOSU/ODM/iosu_odm.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include "iosu_odm.h"
|
||||||
|
#include "config/ActiveSettings.h"
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
#include "util/helpers/Semaphore.h"
|
||||||
|
#include "../kernel/iosu_kernel.h"
|
||||||
|
|
||||||
|
namespace iosu
|
||||||
|
{
|
||||||
|
namespace odm
|
||||||
|
{
|
||||||
|
using namespace iosu::kernel;
|
||||||
|
|
||||||
|
std::string s_devicePath = "/dev/odm";
|
||||||
|
std::thread s_serviceThread;
|
||||||
|
std::atomic_bool s_requestStop{false};
|
||||||
|
std::atomic_bool s_isRunning{false};
|
||||||
|
std::atomic_bool s_threadInitialized{ false };
|
||||||
|
|
||||||
|
IOSMsgQueueId s_msgQueueId;
|
||||||
|
SysAllocator<iosu::kernel::IOSMessage, 128> _s_msgBuffer;
|
||||||
|
|
||||||
|
enum class ODM_CMD_OPERATION_TYPE
|
||||||
|
{
|
||||||
|
CHECK_STATE = 4,
|
||||||
|
UKN_5 = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ODM_STATE
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
INITIAL = 1,
|
||||||
|
AUTHENTICATION = 2,
|
||||||
|
WAIT_FOR_DISC_READY = 3,
|
||||||
|
CAFE_DISC = 4,
|
||||||
|
RVL_DISC = 5,
|
||||||
|
CLEANING_DISC = 6,
|
||||||
|
INVALID_DISC = 8,
|
||||||
|
DIRTY_DISC = 9,
|
||||||
|
NO_DISC = 10,
|
||||||
|
INVALID_DRIVE = 11,
|
||||||
|
FATAL = 12,
|
||||||
|
HARD_FATAL = 13,
|
||||||
|
SHUTDOWN = 14,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ODMHandleCommandIoctl(uint32 clientHandle, IPCCommandBody* cmd, ODM_CMD_OPERATION_TYPE operationId, void* ptrIn, uint32 sizeIn, void* ptrOut, uint32 sizeOut)
|
||||||
|
{
|
||||||
|
switch(operationId)
|
||||||
|
{
|
||||||
|
case ODM_CMD_OPERATION_TYPE::CHECK_STATE:
|
||||||
|
{
|
||||||
|
*(uint32be*)ptrOut = (uint32)ODM_STATE::NO_DISC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODM_CMD_OPERATION_TYPE::UKN_5:
|
||||||
|
{
|
||||||
|
// does this return anything?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "ODMHandleCommandIoctl: Unknown operationId %d\n", (uint32)operationId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IOS_ResourceReply(cmd, IOS_ERROR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 CreateClientHandle()
|
||||||
|
{
|
||||||
|
return 1; // we dont care about handles for now
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseClientHandle(uint32 handle)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODMServiceThread()
|
||||||
|
{
|
||||||
|
s_msgQueueId = IOS_CreateMessageQueue(_s_msgBuffer.GetPtr(), _s_msgBuffer.GetCount());
|
||||||
|
cemu_assert(!IOS_ResultIsError((IOS_ERROR)s_msgQueueId));
|
||||||
|
IOS_ERROR r = IOS_RegisterResourceManager(s_devicePath.c_str(), s_msgQueueId);
|
||||||
|
cemu_assert(!IOS_ResultIsError(r));
|
||||||
|
s_threadInitialized = true;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
IOSMessage msg;
|
||||||
|
IOS_ERROR r = IOS_ReceiveMessage(s_msgQueueId, &msg, 0);
|
||||||
|
cemu_assert(!IOS_ResultIsError(r));
|
||||||
|
if (msg == 0)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(s_requestStop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
IPCCommandBody* cmd = MEMPTR<IPCCommandBody>(msg).GetPtr();
|
||||||
|
uint32 clientHandle = (uint32)cmd->devHandle;
|
||||||
|
if (cmd->cmdId == IPCCommandId::IOS_OPEN)
|
||||||
|
{
|
||||||
|
IOS_ResourceReply(cmd, (IOS_ERROR)CreateClientHandle());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (cmd->cmdId == IPCCommandId::IOS_CLOSE)
|
||||||
|
{
|
||||||
|
CloseClientHandle((IOSDevHandle)(uint32)cmd->devHandle);
|
||||||
|
IOS_ResourceReply(cmd, IOS_ERROR_OK);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (cmd->cmdId == IPCCommandId::IOS_IOCTLV)
|
||||||
|
{
|
||||||
|
uint32 requestId = cmd->args[0];
|
||||||
|
uint32 numIn = cmd->args[1];
|
||||||
|
uint32 numOut = cmd->args[2];
|
||||||
|
IPCIoctlVector* vec = MEMPTR<IPCIoctlVector>{ cmd->args[3] }.GetPtr();
|
||||||
|
IPCIoctlVector* vecIn = vec + numIn;
|
||||||
|
IPCIoctlVector* vecOut = vec + 0;
|
||||||
|
cemuLog_log(LogType::Force, "{}: Received unsupported Ioctlv cmd", s_devicePath);
|
||||||
|
IOS_ResourceReply(cmd, IOS_ERROR_INVALID);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (cmd->cmdId == IPCCommandId::IOS_IOCTL)
|
||||||
|
{
|
||||||
|
ODMHandleCommandIoctl(clientHandle, cmd, (ODM_CMD_OPERATION_TYPE)cmd->args[0].value(), MEMPTR<void>(cmd->args[1]), cmd->args[2], MEMPTR<void>(cmd->args[3]), cmd->args[4]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cemuLog_log(LogType::Force, "{}: Unsupported cmdId", s_devicePath);
|
||||||
|
cemu_assert_unimplemented();
|
||||||
|
IOS_ResourceReply(cmd, IOS_ERROR_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_threadInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize()
|
||||||
|
{
|
||||||
|
if (s_isRunning.exchange(true))
|
||||||
|
return;
|
||||||
|
s_threadInitialized = false;
|
||||||
|
s_requestStop = false;
|
||||||
|
s_serviceThread = std::thread(&ODMServiceThread);
|
||||||
|
while (!s_threadInitialized) std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
if (!s_isRunning.exchange(false))
|
||||||
|
return;
|
||||||
|
s_requestStop = true;
|
||||||
|
IOS_SendMessage(s_msgQueueId, 0, 0);
|
||||||
|
s_serviceThread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/Cafe/IOSU/ODM/iosu_odm.h
Normal file
10
src/Cafe/IOSU/ODM/iosu_odm.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace iosu
|
||||||
|
{
|
||||||
|
namespace odm
|
||||||
|
{
|
||||||
|
void Initialize();
|
||||||
|
void Shutdown();
|
||||||
|
}
|
||||||
|
}
|
@ -369,6 +369,7 @@ namespace iosu
|
|||||||
{
|
{
|
||||||
sPDMRequestExitThread.store(true);
|
sPDMRequestExitThread.store(true);
|
||||||
sPDMSem.increment();
|
sPDMSem.increment();
|
||||||
|
if(sPDMTimeTrackingThread.joinable())
|
||||||
sPDMTimeTrackingThread.join();
|
sPDMTimeTrackingThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ namespace iosu
|
|||||||
|
|
||||||
nnResult ServiceCall(uint32 serviceId, void* request, void* response) override
|
nnResult ServiceCall(uint32 serviceId, void* request, void* response) override
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Unsupported service call to /dec/act");
|
cemuLog_log(LogType::Force, "Unsupported service call to /dev/act");
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_ACT, 0);
|
return BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_ACT, 0);
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,10 @@ namespace iosu
|
|||||||
std::string titlePath = CafeSystem::GetMlcStoragePath(titleId);
|
std::string titlePath = CafeSystem::GetMlcStoragePath(titleId);
|
||||||
strcpy(titleOut.appPath, titlePath.c_str());
|
strcpy(titleOut.appPath, titlePath.c_str());
|
||||||
|
|
||||||
|
strcpy((char*)titleOut.deviceName, "mlc");
|
||||||
|
|
||||||
titleOut.osVersion = 0; // todo
|
titleOut.osVersion = 0; // todo
|
||||||
titleOut.sdkVersion = 0;
|
titleOut.sdkVersion = it->GetAppSDKVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
numTitlesCopied++;
|
numTitlesCopied++;
|
||||||
@ -73,7 +75,8 @@ namespace iosu
|
|||||||
sint32 mcpGetTitleList(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount)
|
sint32 mcpGetTitleList(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount)
|
||||||
{
|
{
|
||||||
std::unique_lock _lock(sTitleInfoMutex);
|
std::unique_lock _lock(sTitleInfoMutex);
|
||||||
*titleCount = mcpBuildTitleList(titleList, *titleCount, [](const TitleInfo& titleInfo) -> bool { return true; });
|
uint32 maxEntryCount = titleListBufferSize / sizeof(MCPTitleInfo);
|
||||||
|
*titleCount = mcpBuildTitleList(titleList, maxEntryCount, [](const TitleInfo& titleInfo) -> bool { return true; });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +89,7 @@ namespace iosu
|
|||||||
sint32 mcpGetTitleListByAppType(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount, uint32 appType)
|
sint32 mcpGetTitleListByAppType(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount, uint32 appType)
|
||||||
{
|
{
|
||||||
std::unique_lock _lock(sTitleInfoMutex);
|
std::unique_lock _lock(sTitleInfoMutex);
|
||||||
uint32 maxEntryCount = (uint32)*titleCount;
|
uint32 maxEntryCount = titleListBufferSize / sizeof(MCPTitleInfo);
|
||||||
*titleCount = mcpBuildTitleList(titleList, maxEntryCount, [appType](const TitleInfo& titleInfo) -> bool { return titleInfo.GetAppType() == appType; });
|
*titleCount = mcpBuildTitleList(titleList, maxEntryCount, [appType](const TitleInfo& titleInfo) -> bool { return titleInfo.GetAppType() == appType; });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -94,7 +97,7 @@ namespace iosu
|
|||||||
sint32 mcpGetTitleListByTitleId(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount, uint64 titleId)
|
sint32 mcpGetTitleListByTitleId(MCPTitleInfo* titleList, uint32 titleListBufferSize, uint32be* titleCount, uint64 titleId)
|
||||||
{
|
{
|
||||||
std::unique_lock _lock(sTitleInfoMutex);
|
std::unique_lock _lock(sTitleInfoMutex);
|
||||||
uint32 maxEntryCount = (uint32)*titleCount;
|
uint32 maxEntryCount = titleListBufferSize / sizeof(MCPTitleInfo);
|
||||||
*titleCount = mcpBuildTitleList(titleList, maxEntryCount, [titleId](const TitleInfo& titleInfo) -> bool { return titleInfo.GetAppTitleId() == titleId; });
|
*titleCount = mcpBuildTitleList(titleList, maxEntryCount, [titleId](const TitleInfo& titleInfo) -> bool { return titleInfo.GetAppTitleId() == titleId; });
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -143,11 +146,11 @@ namespace iosu
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deprecated
|
||||||
void iosuMcp_init()
|
void iosuMcp_init()
|
||||||
{
|
{
|
||||||
if (iosuMcp.isInitialized)
|
if (iosuMcp.isInitialized)
|
||||||
return;
|
return;
|
||||||
// start the act thread
|
|
||||||
std::thread t(iosuMcp_thread);
|
std::thread t(iosuMcp_thread);
|
||||||
t.detach();
|
t.detach();
|
||||||
iosuMcp.isInitialized = true;
|
iosuMcp.isInitialized = true;
|
||||||
|
@ -13,7 +13,8 @@ struct MCPTitleInfo
|
|||||||
// everything below is uncertain
|
// everything below is uncertain
|
||||||
/* +0x4A */ uint64be osVersion; // app.xml
|
/* +0x4A */ uint64be osVersion; // app.xml
|
||||||
/* +0x52 */ uint32be sdkVersion; // app.xml
|
/* +0x52 */ uint32be sdkVersion; // app.xml
|
||||||
/* +0x56 */ uint8 ukn[0x61 - 0x56];
|
/* +0x56 */ uint8 deviceName[10];
|
||||||
|
/* +0x60 */ uint8 uknPadding; // possibly the index of the device?
|
||||||
//move this and the stuff below
|
//move this and the stuff below
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
#include "openssl/x509.h"
|
#include "openssl/x509.h"
|
||||||
#include "openssl/ssl.h"
|
#include "openssl/ssl.h"
|
||||||
#include "util/helpers/helpers.h"
|
#include "util/helpers/helpers.h"
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "Cemu/napi/napi.h"
|
#include "Cemu/napi/napi.h"
|
||||||
#include "Cemu/ncrypto/ncrypto.h"
|
#include "Cemu/ncrypto/ncrypto.h"
|
||||||
|
#include "Cafe/CafeSystem.h"
|
||||||
|
|
||||||
namespace iosu
|
namespace iosu
|
||||||
{
|
{
|
||||||
@ -47,6 +45,13 @@ namespace iosu
|
|||||||
bool backgroundThreadStarted;
|
bool backgroundThreadStarted;
|
||||||
} g_nim = {};
|
} g_nim = {};
|
||||||
|
|
||||||
|
bool nim_CheckDownloadsDisabled()
|
||||||
|
{
|
||||||
|
// currently for the Wii U menu we disable NIM to speed up boot times
|
||||||
|
uint64 tid = CafeSystem::GetForegroundTitleId();
|
||||||
|
return tid == 0x0005001010040000 || tid == 0x0005001010040100 || tid == 0x0005001010040200;
|
||||||
|
}
|
||||||
|
|
||||||
bool nim_getLatestVersion()
|
bool nim_getLatestVersion()
|
||||||
{
|
{
|
||||||
g_nim.latestVersion = -1;
|
g_nim.latestVersion = -1;
|
||||||
@ -101,6 +106,13 @@ namespace iosu
|
|||||||
|
|
||||||
void nim_buildDownloadList()
|
void nim_buildDownloadList()
|
||||||
{
|
{
|
||||||
|
if(nim_CheckDownloadsDisabled())
|
||||||
|
{
|
||||||
|
cemuLog_logDebug(LogType::Force, "nim_buildDownloadList: Downloads are disabled for this title");
|
||||||
|
g_nim.packages.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sint32 titleCount = mcpGetTitleCount();
|
sint32 titleCount = mcpGetTitleCount();
|
||||||
MCPTitleInfo* titleList = (MCPTitleInfo*)malloc(titleCount * sizeof(MCPTitleInfo));
|
MCPTitleInfo* titleList = (MCPTitleInfo*)malloc(titleCount * sizeof(MCPTitleInfo));
|
||||||
memset(titleList, 0, titleCount * sizeof(MCPTitleInfo));
|
memset(titleList, 0, titleCount * sizeof(MCPTitleInfo));
|
||||||
@ -141,6 +153,8 @@ namespace iosu
|
|||||||
void nim_getPackagesInfo(uint64* titleIdList, sint32 count, titlePackageInfo_t* packageInfoList)
|
void nim_getPackagesInfo(uint64* titleIdList, sint32 count, titlePackageInfo_t* packageInfoList)
|
||||||
{
|
{
|
||||||
memset(packageInfoList, 0, sizeof(titlePackageInfo_t)*count);
|
memset(packageInfoList, 0, sizeof(titlePackageInfo_t)*count);
|
||||||
|
if(nim_CheckDownloadsDisabled())
|
||||||
|
return;
|
||||||
for (sint32 i = 0; i < count; i++)
|
for (sint32 i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
uint64 titleId = _swapEndianU64(titleIdList[i]);
|
uint64 titleId = _swapEndianU64(titleIdList[i]);
|
||||||
|
@ -1429,6 +1429,7 @@ void RPLLoader_InitState()
|
|||||||
rplLoaderHeap_codeArea2.setHeapBase(memory_getPointerFromVirtualOffset(MEMORY_CODEAREA_ADDR));
|
rplLoaderHeap_codeArea2.setHeapBase(memory_getPointerFromVirtualOffset(MEMORY_CODEAREA_ADDR));
|
||||||
rplLoaderHeap_workarea.setHeapBase(memory_getPointerFromVirtualOffset(MEMORY_RPLLOADER_AREA_ADDR));
|
rplLoaderHeap_workarea.setHeapBase(memory_getPointerFromVirtualOffset(MEMORY_RPLLOADER_AREA_ADDR));
|
||||||
g_heapTrampolineArea.setBaseAllocator(&rplLoaderHeap_lowerAreaCodeMem2);
|
g_heapTrampolineArea.setBaseAllocator(&rplLoaderHeap_lowerAreaCodeMem2);
|
||||||
|
RPLLoader_ResetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPLLoader_ResetState()
|
void RPLLoader_ResetState()
|
||||||
@ -1436,8 +1437,7 @@ void RPLLoader_ResetState()
|
|||||||
// unload all RPL modules
|
// unload all RPL modules
|
||||||
while (rplModuleCount > 0)
|
while (rplModuleCount > 0)
|
||||||
RPLLoader_UnloadModule(rplModuleList[0]);
|
RPLLoader_UnloadModule(rplModuleList[0]);
|
||||||
// clear dependency list
|
rplDependencyList.clear();
|
||||||
cemu_assert_debug(false);
|
|
||||||
// unload all remaining symbols
|
// unload all remaining symbols
|
||||||
rplSymbolStorage_unloadAll();
|
rplSymbolStorage_unloadAll();
|
||||||
// free all code imports
|
// free all code imports
|
||||||
@ -1448,8 +1448,6 @@ void RPLLoader_ResetState()
|
|||||||
rplLoader_applicationHasMemoryControl = false;
|
rplLoader_applicationHasMemoryControl = false;
|
||||||
rplLoader_maxCodeAddress = 0;
|
rplLoader_maxCodeAddress = 0;
|
||||||
rpl3_currentDataAllocatorAddr = 0x10000000;
|
rpl3_currentDataAllocatorAddr = 0x10000000;
|
||||||
cemu_assert_debug(rplDependencyList.empty());
|
|
||||||
rplDependencyList.clear();
|
|
||||||
_currentTLSModuleIndex = 1;
|
_currentTLSModuleIndex = 1;
|
||||||
rplLoader_sdataAddr = MPTR_NULL;
|
rplLoader_sdataAddr = MPTR_NULL;
|
||||||
rplLoader_sdata2Addr = MPTR_NULL;
|
rplLoader_sdata2Addr = MPTR_NULL;
|
||||||
|
@ -159,6 +159,7 @@ void rplSymbolStorage_unloadAll()
|
|||||||
// free strings
|
// free strings
|
||||||
for (auto it : rplSymbolStorage.list_strAllocatedBlocks)
|
for (auto it : rplSymbolStorage.list_strAllocatedBlocks)
|
||||||
free(it);
|
free(it);
|
||||||
|
rplSymbolStorage.list_strAllocatedBlocks.clear();
|
||||||
rplSymbolStorage.strAllocatorBlock = nullptr;
|
rplSymbolStorage.strAllocatorBlock = nullptr;
|
||||||
rplSymbolStorage.strAllocatorOffset = 0;
|
rplSymbolStorage.strAllocatorOffset = 0;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "Cafe/OS/libs/nn_uds/nn_uds.h"
|
#include "Cafe/OS/libs/nn_uds/nn_uds.h"
|
||||||
#include "Cafe/OS/libs/nn_nim/nn_nim.h"
|
#include "Cafe/OS/libs/nn_nim/nn_nim.h"
|
||||||
#include "Cafe/OS/libs/nn_ndm/nn_ndm.h"
|
#include "Cafe/OS/libs/nn_ndm/nn_ndm.h"
|
||||||
|
#include "Cafe/OS/libs/nn_spm/nn_spm.h"
|
||||||
#include "Cafe/OS/libs/nn_ec/nn_ec.h"
|
#include "Cafe/OS/libs/nn_ec/nn_ec.h"
|
||||||
#include "Cafe/OS/libs/nn_boss/nn_boss.h"
|
#include "Cafe/OS/libs/nn_boss/nn_boss.h"
|
||||||
#include "Cafe/OS/libs/nn_fp/nn_fp.h"
|
#include "Cafe/OS/libs/nn_fp/nn_fp.h"
|
||||||
@ -204,6 +205,7 @@ void osLib_load()
|
|||||||
nnUds_load();
|
nnUds_load();
|
||||||
nn::nim::load();
|
nn::nim::load();
|
||||||
nn::ndm::load();
|
nn::ndm::load();
|
||||||
|
nn::spm::load();
|
||||||
nn::save::load();
|
nn::save::load();
|
||||||
nsysnet_load();
|
nsysnet_load();
|
||||||
nn::fp::load();
|
nn::fp::load();
|
||||||
|
@ -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
|
typedef struct
|
||||||
{
|
{
|
||||||
/* +0x00 */ uint32be name;
|
/* +0x00 */ uint32be name;
|
||||||
@ -296,6 +283,17 @@ namespace coreinit
|
|||||||
return 0;
|
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()
|
void InitializeCore()
|
||||||
{
|
{
|
||||||
cafeExportRegister("coreinit", OSGetCoreId, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetCoreId, LogType::CoreinitThread);
|
||||||
@ -313,6 +311,8 @@ namespace coreinit
|
|||||||
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSIsOffBoot, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetBootPMFlags, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSGetSystemMode, LogType::CoreinitThread);
|
||||||
|
|
||||||
|
cafeExportRegister("coreinit", OSPanic, LogType::Placeholder);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,6 +117,12 @@ namespace coreinit
|
|||||||
return currentTick >= g_soonestAlarm;
|
return currentTick >= g_soonestAlarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Reset()
|
||||||
|
{
|
||||||
|
g_activeAlarmList.clear();
|
||||||
|
g_soonestAlarm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct ComparatorFireTime
|
struct ComparatorFireTime
|
||||||
{
|
{
|
||||||
@ -282,11 +288,21 @@ namespace coreinit
|
|||||||
return alarm->userData;
|
return alarm->userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OSAlarm_resetAll()
|
void OSAlarm_Shutdown()
|
||||||
{
|
{
|
||||||
cemu_assert_debug(g_activeAlarms.empty());
|
__OSLockScheduler();
|
||||||
|
if(g_activeAlarms.empty())
|
||||||
cemu_assert_debug(false);
|
{
|
||||||
|
__OSUnlockScheduler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(auto& itr : g_activeAlarms)
|
||||||
|
{
|
||||||
|
OSHostAlarmDestroy(itr.second);
|
||||||
|
}
|
||||||
|
g_activeAlarms.clear();
|
||||||
|
OSHostAlarm::Reset();
|
||||||
|
__OSUnlockScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _OSAlarmThread(PPCInterpreter_t* hCPU)
|
void _OSAlarmThread(PPCInterpreter_t* hCPU)
|
||||||
|
@ -45,7 +45,7 @@ namespace coreinit
|
|||||||
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData);
|
void OSSetAlarmUserData(OSAlarm_t* alarm, uint32 userData);
|
||||||
void OSSetPeriodicAlarm(OSAlarm_t* OSAlarm, uint64 startTick, uint64 periodTick, MPTR OSAlarmHandler);
|
void OSSetPeriodicAlarm(OSAlarm_t* OSAlarm, uint64 startTick, uint64 periodTick, MPTR OSAlarmHandler);
|
||||||
|
|
||||||
void OSAlarm_resetAll();
|
void OSAlarm_Shutdown();
|
||||||
|
|
||||||
void alarm_update();
|
void alarm_update();
|
||||||
|
|
||||||
|
@ -64,6 +64,24 @@ sint32 _GetArgLength(const char* arg)
|
|||||||
return c;
|
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()
|
void CafeInit()
|
||||||
{
|
{
|
||||||
// extract executable filename
|
// extract executable filename
|
||||||
@ -85,7 +103,8 @@ void CafeInit()
|
|||||||
_AddArg(rpxFileName.data(), rpxFileName.size());
|
_AddArg(rpxFileName.data(), rpxFileName.size());
|
||||||
strcpy((char*)_coreinitInfo->argStorage, std::string(rpxFileName).c_str());
|
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();
|
const char* argString = _argStr.c_str();
|
||||||
// attach parameters from arg string
|
// attach parameters from arg string
|
||||||
if (argString && argString[0] != '\0')
|
if (argString && argString[0] != '\0')
|
||||||
|
@ -103,13 +103,12 @@ void coreinitExport_MCP_TitleListByAppType(PPCInterpreter_t* hCPU)
|
|||||||
|
|
||||||
void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
|
void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "MCP_TitleList(...) unimplemented");
|
|
||||||
ppcDefineParamU32(mcpHandle, 0);
|
ppcDefineParamU32(mcpHandle, 0);
|
||||||
ppcDefineParamU32BEPtr(countOutput, 1);
|
ppcDefineParamU32BEPtr(countOutput, 1);
|
||||||
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 2);
|
ppcDefineParamStructPtr(titleList, MCPTitleInfo, 2);
|
||||||
ppcDefineParamU32(titleListBufferSize, 3);
|
ppcDefineParamU32(titleListBufferSize, 3);
|
||||||
|
|
||||||
// todo -> Other parameters
|
// todo -> Other parameters?
|
||||||
|
|
||||||
mcpPrepareRequest();
|
mcpPrepareRequest();
|
||||||
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST;
|
mcpRequest->requestCode = IOSU_MCP_GET_TITLE_LIST;
|
||||||
@ -120,6 +119,8 @@ void coreinitExport_MCP_TitleList(PPCInterpreter_t* hCPU)
|
|||||||
|
|
||||||
*countOutput = mcpRequest->titleListRequest.titleCount;
|
*countOutput = mcpRequest->titleListRequest.titleCount;
|
||||||
|
|
||||||
|
cemuLog_logDebug(LogType::Force, "MCP_TitleList(...) returned {} titles", (uint32)mcpRequest->titleListRequest.titleCount);
|
||||||
|
|
||||||
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +187,6 @@ void coreinitExport_MCP_GetTitleInfoByTitleAndDevice(PPCInterpreter_t* hCPU)
|
|||||||
}
|
}
|
||||||
|
|
||||||
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
osLib_returnFromFunction(hCPU, mcpRequest->returnCode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
@ -200,7 +200,7 @@ namespace coreinit
|
|||||||
|
|
||||||
systemVersion->n0 = 0x5;
|
systemVersion->n0 = 0x5;
|
||||||
systemVersion->n1 = 0x5;
|
systemVersion->n1 = 0x5;
|
||||||
systemVersion->n2 = 0x2;
|
systemVersion->n2 = 0x5;
|
||||||
// todo: Load this from \sys\title\00050010\10041200\content\version.bin
|
// todo: Load this from \sys\title\00050010\10041200\content\version.bin
|
||||||
|
|
||||||
osLib_returnFromFunction(hCPU, 0);
|
osLib_returnFromFunction(hCPU, 0);
|
||||||
@ -236,57 +236,56 @@ namespace coreinit
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma pack(1)
|
#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
|
/* +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 ukn313[4];
|
||||||
uint8 ukn317[4];
|
uint8 ukn317[4];
|
||||||
}MCPDevice_t;
|
};
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
static_assert(sizeof(MCPDevice_t) == 0x31B, "MCPDevice_t has invalid size");
|
static_assert(sizeof(MCPDevice_t) == 0x31B);
|
||||||
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(sizeof(MCPDevice_t) == 0x31B);
|
||||||
static_assert(offsetof(MCPDevice_t, ukn313) == 0x313, "MCPDevice_t.ukn313 has invalid offset");
|
static_assert(offsetof(MCPDevice_t, storagePath) == 0x90);
|
||||||
static_assert(offsetof(MCPDevice_t, ukn317) == 0x317, "MCPDevice_t.ukn317 has invalid offset");
|
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)
|
void MCP_DeviceListEx(uint32 mcpHandle, uint32be* deviceCount, MCPDevice_t* deviceList, uint32 deviceListSize, bool returnFullList)
|
||||||
{
|
{
|
||||||
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
|
sint32 maxDeviceCount = deviceListSize / sizeof(MCPDevice_t);
|
||||||
|
|
||||||
if (maxDeviceCount < 3*3)
|
cemu_assert(maxDeviceCount >= 2);
|
||||||
assert_dbg();
|
|
||||||
|
|
||||||
// if this doesnt return both MLC and SLC friendlist (frd.rpx) will softlock during boot
|
memset(deviceList, 0, deviceListSize);
|
||||||
|
|
||||||
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
|
|
||||||
sint32 index = 0;
|
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;
|
*deviceCount = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void export_MCP_DeviceList(PPCInterpreter_t* hCPU)
|
void export_MCP_DeviceList(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
ppcDefineParamU32(mcpHandle, 0);
|
ppcDefineParamU32(mcpHandle, 0);
|
||||||
@ -306,12 +305,12 @@ namespace coreinit
|
|||||||
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
|
memset(deviceList, 0, sizeof(MCPDevice_t) * 1);
|
||||||
// 0
|
// 0
|
||||||
strcpy(deviceList[0].storageName, "mlc");
|
strcpy(deviceList[0].storageName, "mlc");
|
||||||
deviceList[0].storageSubindexOrMask = (0x01); // bitmask?
|
deviceList[0].flags = (0x01); // bitmask?
|
||||||
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].storageSubindexOrMask);
|
sprintf(deviceList[0].storagePath, "/vol/storage_%s%02x", deviceList[0].storageName, (sint32)deviceList[0].flags);
|
||||||
// 1
|
// 1
|
||||||
strcpy(deviceList[1].storageName, "slc");
|
strcpy(deviceList[1].storageName, "slc");
|
||||||
deviceList[1].storageSubindexOrMask = (0x01); // bitmask?
|
deviceList[1].flags = (0x01); // bitmask?
|
||||||
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].storageSubindexOrMask);
|
sprintf(deviceList[1].storagePath, "/vol/storage_%s%02x", deviceList[1].storageName, (sint32)deviceList[1].flags);
|
||||||
|
|
||||||
// 2
|
// 2
|
||||||
//strcpy(deviceList[2].storageName, "usb");
|
//strcpy(deviceList[2].storageName, "usb");
|
||||||
@ -360,6 +359,8 @@ namespace coreinit
|
|||||||
|
|
||||||
// this callback is to let the app know when the title list changed?
|
// 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);
|
osLib_returnFromFunction(hCPU, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +388,34 @@ namespace coreinit
|
|||||||
osLib_returnFromFunction(hCPU, 0);
|
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()
|
void InitializeMCP()
|
||||||
{
|
{
|
||||||
osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open);
|
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_UpdateCheckContext", export_MCP_UpdateCheckContext);
|
||||||
osLib_addFunction("coreinit", "MCP_TitleListUpdateGetNext", export_MCP_TitleListUpdateGetNext);
|
osLib_addFunction("coreinit", "MCP_TitleListUpdateGetNext", export_MCP_TitleListUpdateGetNext);
|
||||||
osLib_addFunction("coreinit", "MCP_GetOverlayAppInfo", export_MCP_GetOverlayAppInfo);
|
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))
|
if (ucParam->resultPtr != _swapEndianU32(MPTR_NULL))
|
||||||
memory_writeU8(_swapEndianU32(ucParam->resultPtr), 0);
|
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
|
else
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "Unsupported SCI value: {} Size {:08x}", ucParam->settingName, ucParam->ukn4_size);
|
cemuLog_logDebug(LogType::Force, "Unsupported SCI value: {} Size {:08x}", ucParam->settingName, ucParam->ukn4_size);
|
||||||
|
@ -615,11 +615,25 @@ namespace coreinit
|
|||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeMEM()
|
void MEMResetToDefaultState()
|
||||||
{
|
{
|
||||||
for (auto& it : sHeapBaseHandle)
|
for (auto& it : sHeapBaseHandle)
|
||||||
it = nullptr;
|
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()
|
||||||
|
{
|
||||||
|
MEMResetToDefaultState();
|
||||||
|
|
||||||
cafeExportRegister("coreinit", CoreInitDefaultHeap, LogType::CoreinitMem);
|
cafeExportRegister("coreinit", CoreInitDefaultHeap, LogType::CoreinitMem);
|
||||||
|
|
||||||
osLib_addFunction("coreinit", "MEMInitAllocatorForDefaultHeap", coreinitExport_MEMInitAllocatorForDefaultHeap);
|
osLib_addFunction("coreinit", "MEMInitAllocatorForDefaultHeap", coreinitExport_MEMInitAllocatorForDefaultHeap);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include "Cafe/OS/common/OSCommon.h"
|
#include "Cafe/OS/common/OSCommon.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||||
|
#include "Cafe/CafeSystem.h"
|
||||||
|
#include "Cafe/Filesystem/fsc.h"
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
@ -309,14 +312,20 @@ namespace coreinit
|
|||||||
cemu_assert_unimplemented();
|
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)
|
void OSConsoleWrite(const char* strPtr, sint32 length)
|
||||||
@ -341,19 +350,124 @@ namespace coreinit
|
|||||||
return true;
|
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()
|
void miscInit()
|
||||||
{
|
{
|
||||||
|
s_sdkVersion = CafeSystem::GetForegroundTitleSDKVersion();
|
||||||
|
|
||||||
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
cafeExportRegister("coreinit", __os_snprintf, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSReport, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSVReport, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
cafeExportRegister("coreinit", COSWarn, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder);
|
||||||
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
|
cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder);
|
||||||
|
cafeExportRegister("coreinit", __OSGetProcessSDKVersion, LogType::Placeholder);
|
||||||
|
|
||||||
g_homeButtonMenuEnabled = true; // enabled by default
|
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
|
// 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", OSIsHomeButtonMenuEnabled, LogType::CoreinitThread);
|
||||||
cafeExportRegister("coreinit", OSEnableHomeButtonMenu, LogType::CoreinitThread);
|
cafeExportRegister("coreinit", OSEnableHomeButtonMenu, LogType::CoreinitThread);
|
||||||
|
|
||||||
|
cafeExportRegister("coreinit", OSLaunchTitleByPathl, LogType::Placeholder);
|
||||||
|
cafeExportRegister("coreinit", OSRestartGame, LogType::Placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -2,5 +2,9 @@
|
|||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
{
|
{
|
||||||
|
uint32 __OSGetProcessSDKVersion();
|
||||||
|
uint32 OSLaunchTitleByPathl(const char* path, uint32 pathLength, uint32 argc);
|
||||||
|
uint32 OSRestartGame(uint32 argc, MEMPTR<char>* argv);
|
||||||
|
|
||||||
void miscInit();
|
void miscInit();
|
||||||
};
|
};
|
@ -20,8 +20,6 @@ SlimRWLock srwlock_activeThreadList;
|
|||||||
MPTR activeThread[256];
|
MPTR activeThread[256];
|
||||||
sint32 activeThreadCount = 0;
|
sint32 activeThreadCount = 0;
|
||||||
|
|
||||||
MPTR exitThreadPtr = 0;
|
|
||||||
|
|
||||||
void nnNfp_update();
|
void nnNfp_update();
|
||||||
|
|
||||||
namespace coreinit
|
namespace coreinit
|
||||||
@ -198,8 +196,6 @@ namespace coreinit
|
|||||||
return __currentCoreThread[currentInstance->spr.UPIR];
|
return __currentCoreThread[currentInstance->spr.UPIR];
|
||||||
}
|
}
|
||||||
|
|
||||||
MPTR funcPtr_threadEntry = 0;
|
|
||||||
|
|
||||||
void threadEntry(PPCInterpreter_t* hCPU)
|
void threadEntry(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(hCPU);
|
OSThread_t* currentThread = coreinitThread_getCurrentThreadDepr(hCPU);
|
||||||
@ -223,12 +219,6 @@ namespace coreinit
|
|||||||
|
|
||||||
void coreinitExport_OSExitThreadDepr(PPCInterpreter_t* hCPU);
|
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)
|
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
|
cemu_assert_debug(thread != nullptr); // make thread struct mandatory. Caller can always use SysAllocator
|
||||||
@ -236,7 +226,7 @@ namespace coreinit
|
|||||||
bool isThreadStillActive = __OSIsThreadActive(thread);
|
bool isThreadStillActive = __OSIsThreadActive(thread);
|
||||||
if (isThreadStillActive)
|
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)
|
// seen in Fast Racing Neo at boot (0x020617BC OSCreateThread)
|
||||||
cemuLog_log(LogType::Force, "Game attempting to re-initialize existing thread");
|
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)
|
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);
|
cemu_assert_debug(__OSIsThreadActive(thread) == false);
|
||||||
__OSUnlockScheduler();
|
__OSUnlockScheduler();
|
||||||
|
|
||||||
initFunctionPointers();
|
|
||||||
if (thread == nullptr)
|
|
||||||
thread = (OSThread_t*)memory_getPointerFromVirtualOffset(coreinit_allocFromSysArea(sizeof(OSThread_t), 32));
|
|
||||||
memset(thread, 0x00, sizeof(OSThread_t));
|
memset(thread, 0x00, sizeof(OSThread_t));
|
||||||
// init signatures
|
// init signatures
|
||||||
thread->SetMagic();
|
thread->SetMagic();
|
||||||
@ -277,8 +263,8 @@ namespace coreinit
|
|||||||
// init misc stuff
|
// init misc stuff
|
||||||
thread->attr = affinityMask;
|
thread->attr = affinityMask;
|
||||||
thread->context.setAffinity(affinityMask);
|
thread->context.setAffinity(affinityMask);
|
||||||
thread->context.srr0 = funcPtr_threadEntry;
|
thread->context.srr0 = PPCInterpreter_makeCallableExportDepr(threadEntry);
|
||||||
thread->context.lr = _swapEndianU32(exitThreadPtr);
|
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?)
|
thread->id = 0x8000; // Warriors Orochi 3 softlocks if this is zero due to confusing threads (_OSActivateThread should set this?)
|
||||||
// init ugqr
|
// init ugqr
|
||||||
thread->context.gqr[0] = 0x00000000;
|
thread->context.gqr[0] = 0x00000000;
|
||||||
@ -360,8 +346,8 @@ namespace coreinit
|
|||||||
// todo - this should fully reinitialize the thread?
|
// todo - this should fully reinitialize the thread?
|
||||||
|
|
||||||
thread->entrypoint = _swapEndianU32(funcAddress);
|
thread->entrypoint = _swapEndianU32(funcAddress);
|
||||||
thread->context.srr0 = coreinit::funcPtr_threadEntry;
|
thread->context.srr0 = PPCInterpreter_makeCallableExportDepr(threadEntry);
|
||||||
thread->context.lr = _swapEndianU32(exitThreadPtr);
|
thread->context.lr = _swapEndianU32(PPCInterpreter_makeCallableExportDepr(coreinitExport_OSExitThreadDepr));
|
||||||
thread->context.gpr[3] = _swapEndianU32(numParam);
|
thread->context.gpr[3] = _swapEndianU32(numParam);
|
||||||
thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam));
|
thread->context.gpr[4] = _swapEndianU32(memory_getVirtualOffsetFromPointer(ptrParam));
|
||||||
thread->suspendCounter = 0; // verify
|
thread->suspendCounter = 0; // verify
|
||||||
@ -1018,6 +1004,18 @@ namespace coreinit
|
|||||||
return selectedThread;
|
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()
|
void __OSCheckSystemEvents()
|
||||||
{
|
{
|
||||||
// AX update
|
// AX update
|
||||||
@ -1202,7 +1200,7 @@ namespace coreinit
|
|||||||
g_schedulerThreadHandles.emplace_back(it.native_handle());
|
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()
|
void OSSchedulerEnd()
|
||||||
{
|
{
|
||||||
std::unique_lock _lock(sSchedulerStateMtx);
|
std::unique_lock _lock(sSchedulerStateMtx);
|
||||||
@ -1386,6 +1384,7 @@ namespace coreinit
|
|||||||
|
|
||||||
__OSInitDefaultThreads();
|
__OSInitDefaultThreads();
|
||||||
__OSInitTerminatorThreads();
|
__OSInitTerminatorThreads();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,6 +606,7 @@ namespace coreinit
|
|||||||
void __OSQueueThreadDeallocation(OSThread_t* thread);
|
void __OSQueueThreadDeallocation(OSThread_t* thread);
|
||||||
|
|
||||||
bool __OSIsThreadActive(OSThread_t* thread);
|
bool __OSIsThreadActive(OSThread_t* thread);
|
||||||
|
void __OSDeleteAllActivePPCThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
@ -45,6 +45,12 @@ namespace GX2
|
|||||||
sint32 gx2WriteGatherCurrentMainCoreIndex = -1;
|
sint32 gx2WriteGatherCurrentMainCoreIndex = -1;
|
||||||
bool gx2WriteGatherInited = false;
|
bool gx2WriteGatherInited = false;
|
||||||
|
|
||||||
|
void GX2WriteGather_ResetToDefaultState()
|
||||||
|
{
|
||||||
|
gx2WriteGatherCurrentMainCoreIndex = -1;
|
||||||
|
gx2WriteGatherInited = false;
|
||||||
|
}
|
||||||
|
|
||||||
void GX2Init_writeGather() // init write gather, make current core
|
void GX2Init_writeGather() // init write gather, make current core
|
||||||
{
|
{
|
||||||
if (gx2WriteGatherPipe.gxRingBuffer == NULL)
|
if (gx2WriteGatherPipe.gxRingBuffer == NULL)
|
||||||
@ -289,7 +295,6 @@ namespace GX2
|
|||||||
|
|
||||||
void GX2CommandInit()
|
void GX2CommandInit()
|
||||||
{
|
{
|
||||||
|
|
||||||
cafeExportRegister("gx2", GX2BeginDisplayList, LogType::GX2);
|
cafeExportRegister("gx2", GX2BeginDisplayList, LogType::GX2);
|
||||||
cafeExportRegister("gx2", GX2BeginDisplayListEx, LogType::GX2);
|
cafeExportRegister("gx2", GX2BeginDisplayListEx, LogType::GX2);
|
||||||
cafeExportRegister("gx2", GX2EndDisplayList, LogType::GX2);
|
cafeExportRegister("gx2", GX2EndDisplayList, LogType::GX2);
|
||||||
@ -305,4 +310,9 @@ namespace GX2
|
|||||||
cafeExportRegister("gx2", GX2PatchDisplayList, LogType::GX2);
|
cafeExportRegister("gx2", GX2PatchDisplayList, LogType::GX2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GX2CommandResetToDefaultState()
|
||||||
|
{
|
||||||
|
GX2WriteGather_ResetToDefaultState();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,4 +98,5 @@ namespace GX2
|
|||||||
|
|
||||||
void GX2Init_writeGather();
|
void GX2Init_writeGather();
|
||||||
void GX2CommandInit();
|
void GX2CommandInit();
|
||||||
|
void GX2CommandResetToDefaultState();
|
||||||
}
|
}
|
@ -308,4 +308,15 @@ namespace GX2
|
|||||||
coreinit::OSInitEvent(s_updateRetirementEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
|
coreinit::OSInitEvent(s_updateRetirementEvent, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
|
||||||
coreinit::OSInitSemaphore(s_eventCbQueueSemaphore, 0);
|
coreinit::OSInitSemaphore(s_eventCbQueueSemaphore, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GX2EventResetToDefaultState()
|
||||||
|
{
|
||||||
|
s_callbackThreadLaunched = false;
|
||||||
|
s_lastRetirementTimestamp = 0;
|
||||||
|
for(auto& it : s_eventCallback)
|
||||||
|
{
|
||||||
|
it.callbackFuncPtr = nullptr;
|
||||||
|
it.userData = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
namespace GX2
|
namespace GX2
|
||||||
{
|
{
|
||||||
void GX2EventInit();
|
|
||||||
void GX2Init_event();
|
void GX2Init_event();
|
||||||
|
void GX2EventResetToDefaultState();
|
||||||
|
|
||||||
|
void GX2EventInit();
|
||||||
void GX2WaitForVsync();
|
void GX2WaitForVsync();
|
||||||
void GX2WaitForFlip();
|
void GX2WaitForFlip();
|
||||||
void GX2DrawDone();
|
void GX2DrawDone();
|
||||||
|
@ -115,6 +115,9 @@ namespace GX2
|
|||||||
void _GX2DriverReset()
|
void _GX2DriverReset()
|
||||||
{
|
{
|
||||||
LatteGPUState.gx2InitCalled = 0;
|
LatteGPUState.gx2InitCalled = 0;
|
||||||
|
sGX2MainCoreIndex = 0;
|
||||||
|
GX2CommandResetToDefaultState();
|
||||||
|
GX2EventResetToDefaultState();
|
||||||
}
|
}
|
||||||
|
|
||||||
sint32 GX2GetMainCoreId(PPCInterpreter_t* hCPU)
|
sint32 GX2GetMainCoreId(PPCInterpreter_t* hCPU)
|
||||||
|
@ -43,8 +43,11 @@ namespace acp
|
|||||||
return ACPStatus::SUCCESS;
|
return ACPStatus::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sSaveDirMounted{false};
|
||||||
|
|
||||||
ACPStatus ACPMountSaveDir()
|
ACPStatus ACPMountSaveDir()
|
||||||
{
|
{
|
||||||
|
cemu_assert_debug(!sSaveDirMounted);
|
||||||
uint64 titleId = CafeSystem::GetForegroundTitleId();
|
uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||||
uint32 high = GetTitleIdHigh(titleId) & (~0xC);
|
uint32 high = GetTitleIdHigh(titleId) & (~0xC);
|
||||||
uint32 low = GetTitleIdLow(titleId);
|
uint32 low = GetTitleIdLow(titleId);
|
||||||
@ -56,6 +59,13 @@ namespace acp
|
|||||||
return _ACPConvertResultToACPStatus(&mountResult, "ACPMountSaveDir", 0x60);
|
return _ACPConvertResultToACPStatus(&mountResult, "ACPMountSaveDir", 0x60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACPStatus ACPUnmountSaveDir()
|
||||||
|
{
|
||||||
|
cemu_assert_debug(!sSaveDirMounted);
|
||||||
|
fsc_unmount("/vol/save/", FSC_PRIORITY_BASE);
|
||||||
|
return ACPStatus::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
uint64 _acpGetTimestamp()
|
uint64 _acpGetTimestamp()
|
||||||
{
|
{
|
||||||
return coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK;
|
return coreinit::coreinit_getOSTime() / ESPRESSO_TIMER_CLOCK;
|
||||||
@ -434,12 +444,12 @@ namespace acp
|
|||||||
ppcDefineParamU32(deviceId, 3);
|
ppcDefineParamU32(deviceId, 3);
|
||||||
|
|
||||||
if (deviceId != 3)
|
if (deviceId != 3)
|
||||||
assert_dbg();
|
cemuLog_logDebug(LogType::Force, "ACPGetTitleMetaXmlByDevice(): Unsupported deviceId");
|
||||||
|
|
||||||
acpPrepareRequest();
|
acpPrepareRequest();
|
||||||
acpRequest->requestCode = IOSU_ACP_GET_TITLE_META_XML;
|
acpRequest->requestCode = IOSU_ACP_GET_TITLE_META_XML;
|
||||||
acpRequest->ptr = acpMetaXml;
|
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);
|
__depr__IOS_Ioctlv(IOS_DEVICE_ACP_MAIN, IOSU_ACP_REQUEST_CEMU, 1, 1, acpBufferVector);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ namespace acp
|
|||||||
|
|
||||||
ACPStatus ACPGetApplicationBox(uint32be* applicationBox, uint64 titleId);
|
ACPStatus ACPGetApplicationBox(uint32be* applicationBox, uint64 titleId);
|
||||||
ACPStatus ACPMountSaveDir();
|
ACPStatus ACPMountSaveDir();
|
||||||
|
ACPStatus ACPUnmountSaveDir();
|
||||||
ACPStatus ACPCreateSaveDir(uint32 persistentId, ACPDeviceType type);
|
ACPStatus ACPCreateSaveDir(uint32 persistentId, ACPDeviceType type);
|
||||||
ACPStatus ACPUpdateSaveTimeStamp(uint32 persistentId, uint64 titleId, ACPDeviceType deviceType);;
|
ACPStatus ACPUpdateSaveTimeStamp(uint32 persistentId, uint64 titleId, ACPDeviceType deviceType);;
|
||||||
|
|
||||||
|
@ -1483,7 +1483,7 @@ std::string nnBossNsDataExport_GetPath(nsData_t* nsData)
|
|||||||
if (title_id == 0)
|
if (title_id == 0)
|
||||||
title_id = CafeSystem::GetForegroundTitleId();
|
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->storage.storageName;
|
||||||
path /= nsData->name;
|
path /= nsData->name;
|
||||||
return path.string();
|
return path.string();
|
||||||
@ -1578,6 +1578,13 @@ void nnBossNsDataExport_getSize(PPCInterpreter_t* hCPU)
|
|||||||
osLib_returnFromFunction64(hCPU, fileSize);
|
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)
|
uint32 nnBossNsData_read(nsData_t* nsData, uint64* sizeOutBE, void* buffer, sint32 length)
|
||||||
{
|
{
|
||||||
FSCVirtualFile* fscStorageFile = nullptr;
|
FSCVirtualFile* fscStorageFile = nullptr;
|
||||||
@ -1797,6 +1804,7 @@ void nnBoss_load()
|
|||||||
osLib_addFunction("nn_boss", "DeleteRealFileWithHistory__Q3_2nn4boss6NsDataFv", nnBossNsDataExport_DeleteRealFileWithHistory);
|
osLib_addFunction("nn_boss", "DeleteRealFileWithHistory__Q3_2nn4boss6NsDataFv", nnBossNsDataExport_DeleteRealFileWithHistory);
|
||||||
osLib_addFunction("nn_boss", "Exist__Q3_2nn4boss6NsDataCFv", nnBossNsDataExport_Exist);
|
osLib_addFunction("nn_boss", "Exist__Q3_2nn4boss6NsDataCFv", nnBossNsDataExport_Exist);
|
||||||
osLib_addFunction("nn_boss", "GetSize__Q3_2nn4boss6NsDataCFv", nnBossNsDataExport_getSize);
|
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_2nn4boss6NsDataFPvUi", nnBossNsDataExport_read);
|
||||||
osLib_addFunction("nn_boss", "Read__Q3_2nn4boss6NsDataFPLPvUi", nnBossNsDataExport_readWithSizeOut);
|
osLib_addFunction("nn_boss", "Read__Q3_2nn4boss6NsDataFPLPvUi", nnBossNsDataExport_readWithSizeOut);
|
||||||
osLib_addFunction("nn_boss", "Seek__Q3_2nn4boss6NsDataFLQ3_2nn4boss12PositionBase", nnBossNsDataExport_seek);
|
osLib_addFunction("nn_boss", "Seek__Q3_2nn4boss6NsDataFLQ3_2nn4boss12PositionBase", nnBossNsDataExport_seek);
|
||||||
|
@ -1,27 +1,91 @@
|
|||||||
#include "nn_ndm.h"
|
#include "nn_ndm.h"
|
||||||
#include "Cafe/OS/common/OSCommon.h"
|
#include "Cafe/OS/common/OSCommon.h"
|
||||||
|
#include "Cafe/OS/libs/nn_common.h"
|
||||||
|
|
||||||
namespace nn
|
namespace nn
|
||||||
{
|
{
|
||||||
namespace ndm
|
namespace ndm
|
||||||
{
|
{
|
||||||
void nnNdmExport_GetDaemonStatus(PPCInterpreter_t* hCPU)
|
|
||||||
|
enum class DAEMON_NAME : uint32
|
||||||
{
|
{
|
||||||
// parameters:
|
UKN_0, // Boss related?
|
||||||
// r3 pointer to status integer (out)
|
UKN_1, // Download Manager? scope.rpx (Download Manager app) expects this to have status 0 or 1. Otherwise it will display downloads as disabled
|
||||||
// r4 daemon name (integer)
|
UKN_2,
|
||||||
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
|
enum class DAEMON_STATUS : uint32
|
||||||
memory_writeU32(hCPU->gpr[3], 1);
|
{
|
||||||
// 2 - running?
|
STATUS_UKN_0 = 0, // probably: Ready or initializing?
|
||||||
// 3 - suspended?
|
RUNNING = 1, // most likely running, but not 100% sure
|
||||||
osLib_returnFromFunction(hCPU, 0);
|
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()
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
|
#include "Cafe/IOSU/legacy/iosu_ioctl.h"
|
||||||
#include "Cafe/IOSU/legacy/iosu_nim.h"
|
#include "Cafe/IOSU/legacy/iosu_nim.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_IOS.h"
|
||||||
|
#include "Cafe/OS/libs/nn_common.h"
|
||||||
|
|
||||||
#define nimPrepareRequest() \
|
#define nimPrepareRequest() \
|
||||||
StackAllocator<iosu::nim::iosuNimCemuRequest_t> _buf_nimRequest; \
|
StackAllocator<iosu::nim::iosuNimCemuRequest_t> _buf_nimRequest; \
|
||||||
@ -61,8 +62,6 @@ namespace nn
|
|||||||
|
|
||||||
void export_GetNumTitlePackages(PPCInterpreter_t* hCPU)
|
void export_GetNumTitlePackages(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "GetNumTitlePackages() - placeholder");
|
|
||||||
|
|
||||||
nimPrepareRequest();
|
nimPrepareRequest();
|
||||||
|
|
||||||
nimRequest->requestCode = IOSU_NIM_GET_PACKAGE_COUNT;
|
nimRequest->requestCode = IOSU_NIM_GET_PACKAGE_COUNT;
|
||||||
@ -152,8 +151,9 @@ namespace nn
|
|||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "QuerySchedulerStatus() - placeholder");
|
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)
|
// 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:
|
// downloads disabled:
|
||||||
//memory_writeU32(hCPU->gpr[3], (0x00010000));
|
//memory_writeU32(hCPU->gpr[3], (0x00010000));
|
||||||
@ -163,24 +163,44 @@ namespace nn
|
|||||||
osLib_returnFromFunction(hCPU, 0);
|
osLib_returnFromFunction(hCPU, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
struct nimResultError
|
||||||
{
|
{
|
||||||
uint32be iosError;
|
uint32be iosError;
|
||||||
uint32be ukn04;
|
uint32be ukn04;
|
||||||
}nimResultError_t; // size unknown, but probably is 0x8
|
};
|
||||||
|
|
||||||
|
void ConstructResultError(nimResultError* resultError, uint32be* nimErrorCodePtr, uint32 uknParam)
|
||||||
void export_ConstructResultError(PPCInterpreter_t* hCPU)
|
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "Construct__Q3_2nn3nim11ResultErrorFQ2_2nn6Resulti() - placeholder");
|
uint32 nnResultCode = *nimErrorCodePtr;
|
||||||
ppcDefineParamTypePtr(resultError, nimResultError_t, 0);
|
resultError->iosError = nnResultCode;
|
||||||
ppcDefineParamU32BEPtr(nimErrorCodePtr, 1);
|
|
||||||
ppcDefineParamU32(uknParam, 2);
|
|
||||||
|
|
||||||
resultError->iosError = 0;
|
|
||||||
resultError->ukn04 = uknParam;
|
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)
|
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", "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", "MakeTitlePackageTaskConfigAutoUsingBgInstallPolicy__Q3_2nn3nim4utilFULiQ3_2nn4Cafe9TitleType", export_MakeTitlePackageTaskConfigAutoUsingBgInstallPolicy);
|
||||||
osLib_addFunction("nn_nim", "CalculateTitleInstallSize__Q2_2nn3nimFPLRCQ3_2nn3nim22TitlePackageTaskConfigPCUsUi", export_CalculateTitleInstallSize);
|
osLib_addFunction("nn_nim", "CalculateTitleInstallSize__Q2_2nn3nimFPLRCQ3_2nn3nim22TitlePackageTaskConfigPCUsUi", export_CalculateTitleInstallSize);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "nn_olv_UploadCommunityTypes.h"
|
#include "nn_olv_UploadCommunityTypes.h"
|
||||||
#include "nn_olv_DownloadCommunityTypes.h"
|
#include "nn_olv_DownloadCommunityTypes.h"
|
||||||
#include "nn_olv_UploadFavoriteTypes.h"
|
#include "nn_olv_UploadFavoriteTypes.h"
|
||||||
|
#include "nn_olv_PostTypes.h"
|
||||||
|
|
||||||
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
|
#include "Cafe/OS/libs/proc_ui/proc_ui.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||||
@ -288,15 +289,16 @@ namespace nn
|
|||||||
loadOliveUploadCommunityTypes();
|
loadOliveUploadCommunityTypes();
|
||||||
loadOliveDownloadCommunityTypes();
|
loadOliveDownloadCommunityTypes();
|
||||||
loadOliveUploadFavoriteTypes();
|
loadOliveUploadFavoriteTypes();
|
||||||
|
loadOlivePostAndTopicTypes();
|
||||||
|
|
||||||
cafeExportRegisterFunc(GetErrorCode, "nn_olv", "GetErrorCode__Q2_2nn3olvFRCQ2_2nn6Result", LogType::None);
|
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", "DownloadPostDataList__Q2_2nn3olvFPQ3_2nn3olv19DownloadedTopicDataPQ3_2nn3olv18DownloadedPostDataPUiUiPCQ3_2nn3olv25DownloadPostDataListParam", export_DownloadPostDataList);
|
||||||
osLib_addFunction("nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", exportDownloadPostData_TestFlags);
|
// osLib_addFunction("nn_olv", "TestFlags__Q3_2nn3olv18DownloadedDataBaseCFUi", exportDownloadPostData_TestFlags);
|
||||||
osLib_addFunction("nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetPostId);
|
// osLib_addFunction("nn_olv", "GetPostId__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetPostId);
|
||||||
osLib_addFunction("nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetMiiNickname);
|
// osLib_addFunction("nn_olv", "GetMiiNickname__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetMiiNickname);
|
||||||
osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag);
|
// osLib_addFunction("nn_olv", "GetTopicTag__Q3_2nn3olv18DownloadedDataBaseCFv", exportDownloadPostData_GetTopicTag);
|
||||||
osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText);
|
// osLib_addFunction("nn_olv", "GetBodyText__Q3_2nn3olv18DownloadedDataBaseCFPwUi", exportDownloadPostData_GetBodyText);
|
||||||
|
|
||||||
osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken);
|
osLib_addFunction("nn_olv", "GetServiceToken__Q4_2nn3olv6hidden14PortalAppParamCFv", exportPortalAppParam_GetServiceToken);
|
||||||
|
|
||||||
|
311
src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp
Normal file
311
src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
430
src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h
Normal file
430
src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.h
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -203,6 +203,11 @@ namespace save
|
|||||||
return ConvertACPToSaveStatus(status);
|
return ConvertACPToSaveStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SAVEStatus SAVEUnmountSaveDir()
|
||||||
|
{
|
||||||
|
return ConvertACPToSaveStatus(acp::ACPUnmountSaveDir());
|
||||||
|
}
|
||||||
|
|
||||||
void _CheckAndMoveLegacySaves()
|
void _CheckAndMoveLegacySaves()
|
||||||
{
|
{
|
||||||
const uint64 titleId = CafeSystem::GetForegroundTitleId();
|
const uint64 titleId = CafeSystem::GetForegroundTitleId();
|
||||||
@ -1518,6 +1523,8 @@ namespace save
|
|||||||
|
|
||||||
void load()
|
void load()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
osLib_addFunction("nn_save", "SAVEInit", export_SAVEInit);
|
osLib_addFunction("nn_save", "SAVEInit", export_SAVEInit);
|
||||||
osLib_addFunction("nn_save", "SAVEInitSaveDir", export_SAVEInitSaveDir);
|
osLib_addFunction("nn_save", "SAVEInitSaveDir", export_SAVEInitSaveDir);
|
||||||
osLib_addFunction("nn_save", "SAVEGetSharedDataTitlePath", export_SAVEGetSharedDataTitlePath);
|
osLib_addFunction("nn_save", "SAVEGetSharedDataTitlePath", export_SAVEGetSharedDataTitlePath);
|
||||||
@ -1570,5 +1577,15 @@ namespace save
|
|||||||
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationAsync", export_SAVEOpenDirOtherNormalApplicationAsync);
|
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationAsync", export_SAVEOpenDirOtherNormalApplicationAsync);
|
||||||
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationVariationAsync", export_SAVEOpenDirOtherNormalApplicationVariationAsync);
|
osLib_addFunction("nn_save", "SAVEOpenDirOtherNormalApplicationVariationAsync", export_SAVEOpenDirOtherNormalApplicationVariationAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetToDefaultState()
|
||||||
|
{
|
||||||
|
if(g_nn_save->initialized)
|
||||||
|
{
|
||||||
|
SAVEUnmountSaveDir();
|
||||||
|
g_nn_save->initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ namespace nn
|
|||||||
namespace save
|
namespace save
|
||||||
{
|
{
|
||||||
void load();
|
void load();
|
||||||
|
void ResetToDefaultState();
|
||||||
|
|
||||||
bool GetPersistentIdEx(uint8 accountSlot, uint32* persistentId);
|
bool GetPersistentIdEx(uint8 accountSlot, uint32* persistentId);
|
||||||
}
|
}
|
||||||
|
150
src/Cafe/OS/libs/nn_spm/nn_spm.cpp
Normal file
150
src/Cafe/OS/libs/nn_spm/nn_spm.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/Cafe/OS/libs/nn_spm/nn_spm.h
Normal file
9
src/Cafe/OS/libs/nn_spm/nn_spm.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nn
|
||||||
|
{
|
||||||
|
namespace spm
|
||||||
|
{
|
||||||
|
void load();
|
||||||
|
}
|
||||||
|
}
|
@ -209,6 +209,8 @@ namespace snd_core
|
|||||||
};
|
};
|
||||||
|
|
||||||
void AXVPB_Init();
|
void AXVPB_Init();
|
||||||
|
void AXResetToDefaultState();
|
||||||
|
|
||||||
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex);
|
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex);
|
||||||
|
|
||||||
AXVPB* AXAcquireVoiceEx(uint32 priority, MPTR callbackEx, MPTR userParam);
|
AXVPB* AXAcquireVoiceEx(uint32 priority, MPTR callbackEx, MPTR userParam);
|
||||||
|
@ -8,10 +8,11 @@ namespace snd_core
|
|||||||
{
|
{
|
||||||
sndGeneric_t sndGeneric;
|
sndGeneric_t sndGeneric;
|
||||||
|
|
||||||
void resetToDefaultState()
|
void AXResetToDefaultState()
|
||||||
{
|
{
|
||||||
memset(&sndGeneric, 0x00, sizeof(sndGeneric));
|
memset(&sndGeneric, 0x00, sizeof(sndGeneric));
|
||||||
resetNumProcessedFrames();
|
resetNumProcessedFrames();
|
||||||
|
AXVBP_Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AXIsInit()
|
bool AXIsInit()
|
||||||
@ -77,14 +78,13 @@ namespace snd_core
|
|||||||
|
|
||||||
void AXQuit()
|
void AXQuit()
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "AXQuit called from 0x{:08x}", ppcInterpreterCurrentInstance->spr.LR);
|
|
||||||
// clean up
|
|
||||||
AXResetCallbacks();
|
AXResetCallbacks();
|
||||||
AXVoiceList_ResetFreeVoiceList();
|
|
||||||
// todo - should we wait to make sure any active callbacks are finished with execution before we exit AXQuit?
|
// todo - should we wait to make sure any active callbacks are finished with execution before we exit AXQuit?
|
||||||
sndGeneric.isInitialized = false;
|
|
||||||
// request worker thread stop and wait until complete
|
// request worker thread stop and wait until complete
|
||||||
AXIst_StopThread();
|
AXIst_StopThread();
|
||||||
|
// clean up subsystems
|
||||||
|
AXVBP_Reset();
|
||||||
|
sndGeneric.isInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sint32 AXGetMaxVoices()
|
sint32 AXGetMaxVoices()
|
||||||
@ -500,7 +500,7 @@ namespace snd_core
|
|||||||
|
|
||||||
void loadExports()
|
void loadExports()
|
||||||
{
|
{
|
||||||
resetToDefaultState();
|
AXResetToDefaultState();
|
||||||
|
|
||||||
loadExportsSndCore1();
|
loadExportsSndCore1();
|
||||||
loadExportsSndCore2();
|
loadExportsSndCore2();
|
||||||
@ -513,6 +513,8 @@ namespace snd_core
|
|||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
|
AXOut_reset();
|
||||||
|
AXResetToDefaultState();
|
||||||
sndGeneric.isInitialized = false;
|
sndGeneric.isInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,6 @@ namespace snd_core
|
|||||||
|
|
||||||
std::vector<AXVPB*>& AXVoiceList_GetListByPriority(uint32 priority);
|
std::vector<AXVPB*>& AXVoiceList_GetListByPriority(uint32 priority);
|
||||||
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices();
|
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices();
|
||||||
void AXVoiceList_ResetFreeVoiceList();
|
|
||||||
|
|
||||||
inline AXVPBInternal_t* GetInternalVoice(const AXVPB* vpb)
|
inline AXVPBInternal_t* GetInternalVoice(const AXVPB* vpb)
|
||||||
{
|
{
|
||||||
@ -206,6 +205,8 @@ namespace snd_core
|
|||||||
return (uint32)vpb->index;
|
return (uint32)vpb->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AXVBP_Reset();
|
||||||
|
|
||||||
// AXIst
|
// AXIst
|
||||||
void AXIst_InitThread();
|
void AXIst_InitThread();
|
||||||
OSThread_t* AXIst_GetThread();
|
OSThread_t* AXIst_GetThread();
|
||||||
|
@ -958,6 +958,7 @@ namespace snd_core
|
|||||||
|
|
||||||
void AXIst_InitThread()
|
void AXIst_InitThread()
|
||||||
{
|
{
|
||||||
|
__AXIstIsProcessingFrame = false;
|
||||||
// create ist message queue
|
// create ist message queue
|
||||||
OSInitMessageQueue(__AXIstThreadMsgQueue.GetPtr(), __AXIstThreadMsgArray.GetPtr(), 0x10);
|
OSInitMessageQueue(__AXIstThreadMsgQueue.GetPtr(), __AXIstThreadMsgArray.GetPtr(), 0x10);
|
||||||
// create thread
|
// create thread
|
||||||
|
@ -40,11 +40,6 @@ namespace snd_core
|
|||||||
return vpb;
|
return vpb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AXVoiceList_ResetFreeVoiceList()
|
|
||||||
{
|
|
||||||
__AXFreeVoices.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices()
|
std::vector<AXVPB*>& AXVoiceList_GetFreeVoices()
|
||||||
{
|
{
|
||||||
return __AXFreeVoices;
|
return __AXFreeVoices;
|
||||||
@ -80,6 +75,13 @@ namespace snd_core
|
|||||||
return __AXVoicesPerPriority[priority];
|
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;
|
SysAllocator<AXVPBInternal_t, AX_MAX_VOICES> _buffer__AXVPBInternalVoiceArray;
|
||||||
AXVPBInternal_t* __AXVPBInternalVoiceArray;
|
AXVPBInternal_t* __AXVPBInternalVoiceArray;
|
||||||
|
|
||||||
@ -445,14 +447,22 @@ namespace snd_core
|
|||||||
__AXVoiceListSpinlock.unlock();
|
__AXVoiceListSpinlock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AXVPBInit()
|
void __AXVPBResetVoices()
|
||||||
{
|
{
|
||||||
__AXVPBInternalVoiceArray = _buffer__AXVPBInternalVoiceArray.GetPtr();
|
__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(__AXVPBInternalVoiceShadowCopyArrayPtr, 0, sizeof(AXVPBInternal_t)*AX_MAX_VOICES);
|
||||||
memset(__AXVPBInternalVoiceArray, 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(__AXVPBItdArrayPtr, 0, sizeof(AXVPBItd)*AX_MAX_VOICES);
|
||||||
memset(__AXVPBArrayPtr, 0, sizeof(AXVPB)*AX_MAX_VOICES);
|
memset(__AXVPBArrayPtr, 0, sizeof(AXVPB)*AX_MAX_VOICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AXVPBInit()
|
||||||
|
{
|
||||||
|
__AXVPBResetVoices();
|
||||||
for (sint32 i = 0; i < AX_MAX_VOICES; i++)
|
for (sint32 i = 0; i < AX_MAX_VOICES; i++)
|
||||||
{
|
{
|
||||||
AXVPBItd* itd = __AXVPBItdArrayPtr + i;
|
AXVPBItd* itd = __AXVPBItdArrayPtr + i;
|
||||||
@ -494,12 +504,16 @@ namespace snd_core
|
|||||||
|
|
||||||
void AXVPB_Init()
|
void AXVPB_Init()
|
||||||
{
|
{
|
||||||
__AXVPBInternalVoiceShadowCopyArrayPtr = _buffer__AXVPBInternalVoiceShadowCopyArray.GetPtr();
|
__AXVPBResetVoices();
|
||||||
__AXVPBArrayPtr = _buffer__AXVPBArray.GetPtr();
|
|
||||||
__AXVPBItdArrayPtr = _buffer__AXVPBItdArray.GetPtr();
|
|
||||||
AXVPBInit();
|
AXVPBInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AXVBP_Reset()
|
||||||
|
{
|
||||||
|
AXVoiceList_Reset();
|
||||||
|
__AXVPBResetVoices();
|
||||||
|
}
|
||||||
|
|
||||||
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex)
|
sint32 AXIsValidDevice(sint32 device, sint32 deviceIndex)
|
||||||
{
|
{
|
||||||
if (device == AX_DEV_TV)
|
if (device == AX_DEV_TV)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "sysapp.h"
|
#include "sysapp.h"
|
||||||
#include "Cafe/CafeSystem.h"
|
#include "Cafe/CafeSystem.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
#include "Cafe/OS/libs/coreinit/coreinit_FG.h"
|
||||||
|
#include "Cafe/OS/libs/coreinit/coreinit_Misc.h"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -469,7 +470,6 @@ void sysappExport__SYSGetEShopArgs(PPCInterpreter_t* hCPU)
|
|||||||
void sysappExport_SYSGetUPIDFromTitleID(PPCInterpreter_t* hCPU)
|
void sysappExport_SYSGetUPIDFromTitleID(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
ppcDefineParamU64(titleId, 0);
|
ppcDefineParamU64(titleId, 0);
|
||||||
cemuLog_logDebug(LogType::Force, "SYSGetUPIDFromTitleID(0x{:08x}{:08x})", hCPU->gpr[3], hCPU->gpr[4]);
|
|
||||||
uint32 titleIdHigh = (titleId >> 32);
|
uint32 titleIdHigh = (titleId >> 32);
|
||||||
uint32 titleIdLow = (uint32)(titleId & 0xFFFFFFFF);
|
uint32 titleIdLow = (uint32)(titleId & 0xFFFFFFFF);
|
||||||
if ((titleIdHigh & 0xFFFF0000) != 0x50000)
|
if ((titleIdHigh & 0xFFFF0000) != 0x50000)
|
||||||
@ -541,32 +541,67 @@ void sysappExport_SYSGetVodArgs(PPCInterpreter_t* hCPU)
|
|||||||
osLib_returnFromFunction(hCPU, 1);
|
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
|
struct SysLauncherArgs28
|
||||||
{
|
{
|
||||||
uint32 ukn00;
|
uint32 ukn00;
|
||||||
uint32 ukn04;
|
uint32 ukn04;
|
||||||
uint32 ukn08;
|
uint32 ukn08;
|
||||||
uint32 ukn0C;
|
uint32 ukn0C;
|
||||||
uint32 ukn10; // caller title id? (8 byte)
|
// standard args above
|
||||||
uint32 ukn14;
|
uint64be caller_id; // titleId
|
||||||
uint32 ukn18; // launched title id? (8 byte)
|
uint64be launch_title; // titleId
|
||||||
uint32 ukn1C;
|
uint32be mode;
|
||||||
uint32 ukn20; // mode
|
uint32be slot_id;
|
||||||
uint32 ukn24; // slot
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(SysLauncherArgs28) == 0x28);
|
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?)
|
struct SysAccountArgs18
|
||||||
//ppcDefineParamStructPtr(launcherArgs, SysLauncherArgs, 0);
|
{
|
||||||
//memset(launcherArgs, 0, sizeof(SysLauncherArgs));
|
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)
|
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]);
|
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);
|
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);
|
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
|
// register sysapp functions
|
||||||
void sysapp_load()
|
void sysapp_load()
|
||||||
{
|
{
|
||||||
@ -592,6 +662,10 @@ void sysapp_load()
|
|||||||
|
|
||||||
osLib_addFunction("sysapp", "SYSGetVodArgs", sysappExport_SYSGetVodArgs);
|
osLib_addFunction("sysapp", "SYSGetVodArgs", sysappExport_SYSGetVodArgs);
|
||||||
|
|
||||||
osLib_addFunction("sysapp", "_SYSGetLauncherArgs", sysappExport__SYSGetLauncherArgs);
|
|
||||||
osLib_addFunction("sysapp", "SYSGetStandardResult", sysappExport_SYSGetStandardResult);
|
osLib_addFunction("sysapp", "SYSGetStandardResult", sysappExport_SYSGetStandardResult);
|
||||||
|
|
||||||
|
cafeExportRegisterFunc(_SYSGetLauncherArgs, "sysapp", "_SYSGetLauncherArgs", LogType::Placeholder);
|
||||||
|
cafeExportRegisterFunc(_SYSGetAccountArgs, "sysapp", "_SYSGetAccountArgs", LogType::Placeholder);
|
||||||
|
|
||||||
|
sysapp::load();
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
#include "BaseInfo.h"
|
|
||||||
|
|
||||||
#include "config/CemuConfig.h"
|
|
||||||
#include "Cafe/Filesystem/fsc.h"
|
|
||||||
#include "Cafe/Filesystem/FST/FST.h"
|
|
||||||
|
|
||||||
sint32 BaseInfo::GetLanguageIndex(std::string_view language)
|
|
||||||
{
|
|
||||||
if (language == "ja")
|
|
||||||
return (sint32)CafeConsoleLanguage::JA;
|
|
||||||
else if (language == "en")
|
|
||||||
return (sint32)CafeConsoleLanguage::EN;
|
|
||||||
else if (language == "fr")
|
|
||||||
return (sint32)CafeConsoleLanguage::FR;
|
|
||||||
else if (language == "de")
|
|
||||||
return (sint32)CafeConsoleLanguage::DE;
|
|
||||||
else if (language == "it")
|
|
||||||
return (sint32)CafeConsoleLanguage::IT;
|
|
||||||
else if (language == "es")
|
|
||||||
return (sint32)CafeConsoleLanguage::ES;
|
|
||||||
else if (language == "zhs")
|
|
||||||
return (sint32)CafeConsoleLanguage::ZH;
|
|
||||||
else if (language == "ko")
|
|
||||||
return (sint32)CafeConsoleLanguage::KO;
|
|
||||||
else if (language == "nl")
|
|
||||||
return (sint32)CafeConsoleLanguage::NL;
|
|
||||||
else if (language == "pt")
|
|
||||||
return (sint32)CafeConsoleLanguage::PT;
|
|
||||||
else if (language == "ru")
|
|
||||||
return (sint32)CafeConsoleLanguage::RU;
|
|
||||||
else if (language == "zht")
|
|
||||||
return (sint32)CafeConsoleLanguage::ZH;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<uint8[]> BaseInfo::ReadFSCFile(std::string_view filename, uint32& size) const
|
|
||||||
{
|
|
||||||
size = 0;
|
|
||||||
sint32 fscStatus = 0;
|
|
||||||
// load and parse meta.xml
|
|
||||||
FSCVirtualFile* file = fsc_open(const_cast<char*>(std::string(filename).c_str()), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus);
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
size = fsc_getFileSize(file);
|
|
||||||
auto buffer = std::make_unique<uint8[]>(size);
|
|
||||||
fsc_readFile(file, buffer.get(), size);
|
|
||||||
fsc_close(file);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<uint8[]> BaseInfo::ReadVirtualFile(FSTVolume* volume, std::string_view filename, uint32& size) const
|
|
||||||
{
|
|
||||||
size = 0;
|
|
||||||
FSTFileHandle fileHandle;
|
|
||||||
if (!volume->OpenFile(filename, fileHandle, true))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
size = volume->GetFileSize(fileHandle);
|
|
||||||
auto buffer = std::make_unique<uint8[]>(size);
|
|
||||||
volume->ReadFile(fileHandle, 0, size, buffer.get());
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace pugi
|
|
||||||
{
|
|
||||||
struct xml_parse_result;
|
|
||||||
class xml_document;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BaseInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class GameType
|
|
||||||
{
|
|
||||||
FSC, // using fsc API
|
|
||||||
Directory, // rpx/meta
|
|
||||||
Image, // wud/wux
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~BaseInfo() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] const fs::path& GetPath() const { return m_type_path; }
|
|
||||||
[[nodiscard]] GameType GetGameType() const { return m_type; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
GameType m_type;
|
|
||||||
fs::path m_type_path; // empty / base dir / wud path
|
|
||||||
|
|
||||||
virtual void ParseDirectory(const fs::path& filename) = 0;
|
|
||||||
virtual bool ParseFile(const fs::path& filename) = 0;
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<uint8[]> ReadFSCFile(std::string_view filename, uint32& size) const;
|
|
||||||
[[nodiscard]] std::unique_ptr<uint8[]> ReadVirtualFile(class FSTVolume* volume, std::string_view filename, uint32& size) const;
|
|
||||||
|
|
||||||
[[nodiscard]] static sint32 GetLanguageIndex(std::string_view language);
|
|
||||||
};
|
|
@ -20,6 +20,11 @@ public:
|
|||||||
return m_base.IsValid(); // at least the base must be valid for this to be a runnable title
|
return m_base.IsValid(); // at least the base must be valid for this to be a runnable title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSystemDataTitle() const
|
||||||
|
{
|
||||||
|
return m_base.IsSystemDataTitle();
|
||||||
|
}
|
||||||
|
|
||||||
void SetBase(const TitleInfo& titleInfo)
|
void SetBase(const TitleInfo& titleInfo)
|
||||||
{
|
{
|
||||||
m_base = titleInfo;
|
m_base = titleInfo;
|
||||||
@ -84,7 +89,7 @@ public:
|
|||||||
std::string GetTitleName()
|
std::string GetTitleName()
|
||||||
{
|
{
|
||||||
cemu_assert_debug(m_base.IsValid());
|
cemu_assert_debug(m_base.IsValid());
|
||||||
return m_base.GetTitleName(); // long name
|
return m_base.GetMetaTitleName(); // long name
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 GetVersion() const
|
uint16 GetVersion() const
|
||||||
@ -94,6 +99,13 @@ public:
|
|||||||
return m_base.GetAppTitleVersion();
|
return m_base.GetAppTitleVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetSDKVersion() const
|
||||||
|
{
|
||||||
|
if (m_update.IsValid())
|
||||||
|
return m_update.GetAppSDKVersion();
|
||||||
|
return m_base.GetAppSDKVersion();
|
||||||
|
}
|
||||||
|
|
||||||
CafeConsoleRegion GetRegion() const
|
CafeConsoleRegion GetRegion() const
|
||||||
{
|
{
|
||||||
if (m_update.IsValid())
|
if (m_update.IsValid())
|
||||||
|
@ -127,7 +127,8 @@ TitleInfo::CachedInfo TitleInfo::MakeCacheEntry()
|
|||||||
e.subPath = m_subPath;
|
e.subPath = m_subPath;
|
||||||
e.titleId = GetAppTitleId();
|
e.titleId = GetAppTitleId();
|
||||||
e.titleVersion = GetAppTitleVersion();
|
e.titleVersion = GetAppTitleVersion();
|
||||||
e.titleName = GetTitleName();
|
e.sdkVersion = GetAppSDKVersion();
|
||||||
|
e.titleName = GetMetaTitleName();
|
||||||
e.region = GetMetaRegion();
|
e.region = GetMetaRegion();
|
||||||
e.group_id = GetAppGroup();
|
e.group_id = GetAppGroup();
|
||||||
e.app_type = GetAppType();
|
e.app_type = GetAppType();
|
||||||
@ -427,7 +428,7 @@ void TitleInfo::Unmount(std::string_view virtualPath)
|
|||||||
continue;
|
continue;
|
||||||
fsc_unmount(itr.second.c_str(), itr.first);
|
fsc_unmount(itr.second.c_str(), itr.first);
|
||||||
std::erase(m_mountpoints, itr);
|
std::erase(m_mountpoints, itr);
|
||||||
// if the last mount point got unmounted, delete any open devices
|
// if the last mount point got unmounted, close any open devices
|
||||||
if (m_mountpoints.empty())
|
if (m_mountpoints.empty())
|
||||||
{
|
{
|
||||||
if (m_wudVolume)
|
if (m_wudVolume)
|
||||||
@ -436,13 +437,12 @@ void TitleInfo::Unmount(std::string_view virtualPath)
|
|||||||
delete m_wudVolume;
|
delete m_wudVolume;
|
||||||
m_wudVolume = nullptr;
|
m_wudVolume = nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// wua files use reference counting
|
|
||||||
if (m_zarchive)
|
if (m_zarchive)
|
||||||
{
|
{
|
||||||
_ZArchivePool_ReleaseInstance(m_fullPath, m_zarchive);
|
_ZArchivePool_ReleaseInstance(m_fullPath, m_zarchive);
|
||||||
if (m_mountpoints.empty())
|
if (m_mountpoints.empty())
|
||||||
m_zarchive = nullptr;
|
m_zarchive = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -467,7 +467,7 @@ bool TitleInfo::ParseXmlInfo()
|
|||||||
{
|
{
|
||||||
cemu_assert(m_isValid);
|
cemu_assert(m_isValid);
|
||||||
if (m_hasParsedXmlFiles)
|
if (m_hasParsedXmlFiles)
|
||||||
return m_parsedMetaXml && m_parsedAppXml && m_parsedCosXml;
|
return m_isValid;
|
||||||
m_hasParsedXmlFiles = true;
|
m_hasParsedXmlFiles = true;
|
||||||
|
|
||||||
std::string mountPath = GetUniqueTempMountingPath();
|
std::string mountPath = GetUniqueTempMountingPath();
|
||||||
@ -489,10 +489,16 @@ bool TitleInfo::ParseXmlInfo()
|
|||||||
|
|
||||||
Unmount(mountPath);
|
Unmount(mountPath);
|
||||||
|
|
||||||
bool hasAnyXml = m_parsedMetaXml || m_parsedAppXml || m_parsedCosXml;
|
// some system titles dont have a meta.xml file
|
||||||
|
bool allowMissingMetaXml = false;
|
||||||
if (!m_parsedMetaXml || !m_parsedAppXml || !m_parsedCosXml)
|
if(m_parsedAppXml && this->IsSystemDataTitle())
|
||||||
{
|
{
|
||||||
|
allowMissingMetaXml = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((allowMissingMetaXml == false && !m_parsedMetaXml) || !m_parsedAppXml || !m_parsedCosXml)
|
||||||
|
{
|
||||||
|
bool hasAnyXml = m_parsedMetaXml || m_parsedAppXml || m_parsedCosXml;
|
||||||
if (hasAnyXml)
|
if (hasAnyXml)
|
||||||
cemuLog_log(LogType::Force, "Title has missing meta .xml files. Title path: {}", _pathToUtf8(m_fullPath));
|
cemuLog_log(LogType::Force, "Title has missing meta .xml files. Title path: {}", _pathToUtf8(m_fullPath));
|
||||||
delete m_parsedMetaXml;
|
delete m_parsedMetaXml;
|
||||||
@ -531,6 +537,8 @@ bool TitleInfo::ParseAppXml(std::vector<uint8>& appXmlData)
|
|||||||
m_parsedAppXml->app_type = (uint32)std::stoull(child.text().as_string(), nullptr, 16);
|
m_parsedAppXml->app_type = (uint32)std::stoull(child.text().as_string(), nullptr, 16);
|
||||||
else if (name == "group_id")
|
else if (name == "group_id")
|
||||||
m_parsedAppXml->group_id = (uint32)std::stoull(child.text().as_string(), nullptr, 16);
|
m_parsedAppXml->group_id = (uint32)std::stoull(child.text().as_string(), nullptr, 16);
|
||||||
|
else if (name == "sdk_version")
|
||||||
|
m_parsedAppXml->sdk_version = (uint32)std::stoull(child.text().as_string(), nullptr, 16);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -557,6 +565,17 @@ uint16 TitleInfo::GetAppTitleVersion() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 TitleInfo::GetAppSDKVersion() const
|
||||||
|
{
|
||||||
|
cemu_assert_debug(m_isValid);
|
||||||
|
if (m_parsedAppXml)
|
||||||
|
return m_parsedAppXml->sdk_version;
|
||||||
|
if (m_cachedInfo)
|
||||||
|
return m_cachedInfo->sdkVersion;
|
||||||
|
cemu_assert_suspicious();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 TitleInfo::GetAppGroup() const
|
uint32 TitleInfo::GetAppGroup() const
|
||||||
{
|
{
|
||||||
cemu_assert_debug(m_isValid);
|
cemu_assert_debug(m_isValid);
|
||||||
@ -585,7 +604,7 @@ TitleIdParser::TITLE_TYPE TitleInfo::GetTitleType()
|
|||||||
return tip.GetType();
|
return tip.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TitleInfo::GetTitleName() const
|
std::string TitleInfo::GetMetaTitleName() const
|
||||||
{
|
{
|
||||||
cemu_assert_debug(m_isValid);
|
cemu_assert_debug(m_isValid);
|
||||||
if (m_parsedMetaXml)
|
if (m_parsedMetaXml)
|
||||||
@ -600,7 +619,6 @@ std::string TitleInfo::GetTitleName() const
|
|||||||
}
|
}
|
||||||
if (m_cachedInfo)
|
if (m_cachedInfo)
|
||||||
return m_cachedInfo->titleName;
|
return m_cachedInfo->titleName;
|
||||||
cemu_assert_suspicious();
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +629,6 @@ CafeConsoleRegion TitleInfo::GetMetaRegion() const
|
|||||||
return m_parsedMetaXml->GetRegion();
|
return m_parsedMetaXml->GetRegion();
|
||||||
if (m_cachedInfo)
|
if (m_cachedInfo)
|
||||||
return m_cachedInfo->region;
|
return m_cachedInfo->region;
|
||||||
cemu_assert_suspicious();
|
|
||||||
return CafeConsoleRegion::JPN;
|
return CafeConsoleRegion::JPN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ struct ParsedAppXml
|
|||||||
uint16 title_version;
|
uint16 title_version;
|
||||||
uint32 app_type;
|
uint32 app_type;
|
||||||
uint32 group_id;
|
uint32 group_id;
|
||||||
|
uint32 sdk_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParsedCosXml
|
struct ParsedCosXml
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
std::string subPath; // for WUA
|
std::string subPath; // for WUA
|
||||||
uint64 titleId;
|
uint64 titleId;
|
||||||
uint16 titleVersion;
|
uint16 titleVersion;
|
||||||
|
uint32 sdkVersion;
|
||||||
std::string titleName;
|
std::string titleName;
|
||||||
CafeConsoleRegion region;
|
CafeConsoleRegion region;
|
||||||
uint32 group_id;
|
uint32 group_id;
|
||||||
@ -115,12 +117,23 @@ public:
|
|||||||
return m_uid == rhs.m_uid;
|
return m_uid == rhs.m_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSystemDataTitle() const
|
||||||
|
{
|
||||||
|
if(!IsValid())
|
||||||
|
return false;
|
||||||
|
uint32 appType = GetAppType();
|
||||||
|
if(appType == 0)
|
||||||
|
return false; // not a valid app_type, but handle this in case some users use placeholder .xml data with fields zeroed-out
|
||||||
|
return ((appType>>24)&0x80) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// API which requires parsed meta data or cached info
|
// API which requires parsed meta data or cached info
|
||||||
TitleId GetAppTitleId() const; // from app.xml
|
TitleId GetAppTitleId() const; // from app.xml
|
||||||
uint16 GetAppTitleVersion() const; // from app.xml
|
uint16 GetAppTitleVersion() const; // from app.xml
|
||||||
|
uint32 GetAppSDKVersion() const; // from app.xml
|
||||||
uint32 GetAppGroup() const; // from app.xml
|
uint32 GetAppGroup() const; // from app.xml
|
||||||
uint32 GetAppType() const; // from app.xml
|
uint32 GetAppType() const; // from app.xml
|
||||||
std::string GetTitleName() const; // from meta.xml
|
std::string GetMetaTitleName() const; // from meta.xml
|
||||||
CafeConsoleRegion GetMetaRegion() const; // from meta.xml
|
CafeConsoleRegion GetMetaRegion() const; // from meta.xml
|
||||||
uint32 GetOlvAccesskey() const;
|
uint32 GetOlvAccesskey() const;
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ void CafeTitleList::LoadCacheFile()
|
|||||||
if( !TitleIdParser::ParseFromStr(titleInfoNode.attribute("titleId").as_string(), titleId))
|
if( !TitleIdParser::ParseFromStr(titleInfoNode.attribute("titleId").as_string(), titleId))
|
||||||
continue;
|
continue;
|
||||||
uint16 titleVersion = titleInfoNode.attribute("version").as_uint();
|
uint16 titleVersion = titleInfoNode.attribute("version").as_uint();
|
||||||
|
uint32 sdkVersion = titleInfoNode.attribute("sdk_version").as_uint();
|
||||||
TitleInfo::TitleDataFormat format = (TitleInfo::TitleDataFormat)ConvertString<uint32>(titleInfoNode.child_value("format"));
|
TitleInfo::TitleDataFormat format = (TitleInfo::TitleDataFormat)ConvertString<uint32>(titleInfoNode.child_value("format"));
|
||||||
CafeConsoleRegion region = (CafeConsoleRegion)ConvertString<uint32>(titleInfoNode.child_value("region"));
|
CafeConsoleRegion region = (CafeConsoleRegion)ConvertString<uint32>(titleInfoNode.child_value("region"));
|
||||||
std::string name = titleInfoNode.child_value("name");
|
std::string name = titleInfoNode.child_value("name");
|
||||||
@ -76,6 +77,7 @@ void CafeTitleList::LoadCacheFile()
|
|||||||
TitleInfo::CachedInfo cacheEntry;
|
TitleInfo::CachedInfo cacheEntry;
|
||||||
cacheEntry.titleId = titleId;
|
cacheEntry.titleId = titleId;
|
||||||
cacheEntry.titleVersion = titleVersion;
|
cacheEntry.titleVersion = titleVersion;
|
||||||
|
cacheEntry.sdkVersion = sdkVersion;
|
||||||
cacheEntry.titleDataFormat = format;
|
cacheEntry.titleDataFormat = format;
|
||||||
cacheEntry.region = region;
|
cacheEntry.region = region;
|
||||||
cacheEntry.titleName = std::move(name);
|
cacheEntry.titleName = std::move(name);
|
||||||
@ -115,6 +117,7 @@ void CafeTitleList::StoreCacheFile()
|
|||||||
auto titleInfoNode = title_list_node.append_child("title");
|
auto titleInfoNode = title_list_node.append_child("title");
|
||||||
titleInfoNode.append_attribute("titleId").set_value(fmt::format("{:016x}", info.titleId).c_str());
|
titleInfoNode.append_attribute("titleId").set_value(fmt::format("{:016x}", info.titleId).c_str());
|
||||||
titleInfoNode.append_attribute("version").set_value(fmt::format("{:}", info.titleVersion).c_str());
|
titleInfoNode.append_attribute("version").set_value(fmt::format("{:}", info.titleVersion).c_str());
|
||||||
|
titleInfoNode.append_attribute("sdk_version").set_value(fmt::format("{:}", info.sdkVersion).c_str());
|
||||||
titleInfoNode.append_attribute("group_id").set_value(fmt::format("{:08x}", info.group_id).c_str());
|
titleInfoNode.append_attribute("group_id").set_value(fmt::format("{:08x}", info.group_id).c_str());
|
||||||
titleInfoNode.append_attribute("app_type").set_value(fmt::format("{:08x}", info.app_type).c_str());
|
titleInfoNode.append_attribute("app_type").set_value(fmt::format("{:08x}", info.app_type).c_str());
|
||||||
titleInfoNode.append_child("region").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.region).c_str());
|
titleInfoNode.append_child("region").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.region).c_str());
|
||||||
@ -638,12 +641,15 @@ GameInfo2 CafeTitleList::GetGameInfo(TitleId titleId)
|
|||||||
{
|
{
|
||||||
TitleId appTitleId = it->GetAppTitleId();
|
TitleId appTitleId = it->GetAppTitleId();
|
||||||
if (appTitleId == baseTitleId)
|
if (appTitleId == baseTitleId)
|
||||||
|
{
|
||||||
gameInfo.SetBase(*it);
|
gameInfo.SetBase(*it);
|
||||||
|
}
|
||||||
if (hasSeparateUpdateTitleId && appTitleId == updateTitleId)
|
if (hasSeparateUpdateTitleId && appTitleId == updateTitleId)
|
||||||
{
|
{
|
||||||
gameInfo.SetUpdate(*it);
|
gameInfo.SetUpdate(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this title can have AOC content then do a second scan
|
// if this title can have AOC content then do a second scan
|
||||||
// todo - get a list of all AOC title ids from the base/update meta information
|
// todo - get a list of all AOC title ids from the base/update meta information
|
||||||
// for now we assume there is a direct match between the base titleId and the aoc titleId
|
// for now we assume there is a direct match between the base titleId and the aoc titleId
|
||||||
|
@ -113,13 +113,14 @@ bool CemuApp::OnInit()
|
|||||||
wxMessageBox(fmt::format("Cemu can't write to {} !", path.generic_string()), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr);
|
wxMessageBox(fmt::format("Cemu can't write to {} !", path.generic_string()), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr);
|
||||||
|
|
||||||
NetworkConfig::LoadOnce();
|
NetworkConfig::LoadOnce();
|
||||||
|
g_config.Load();
|
||||||
|
|
||||||
HandlePostUpdate();
|
HandlePostUpdate();
|
||||||
mainEmulatorHLE();
|
mainEmulatorHLE();
|
||||||
|
|
||||||
wxInitAllImageHandlers();
|
wxInitAllImageHandlers();
|
||||||
|
|
||||||
g_config.Load();
|
|
||||||
m_languages = GetAvailableLanguages();
|
m_languages = GetAvailableLanguages();
|
||||||
|
|
||||||
const sint32 language = GetConfig().language;
|
const sint32 language = GetConfig().language;
|
||||||
@ -156,10 +157,6 @@ bool CemuApp::OnInit()
|
|||||||
__fastfail(0);
|
__fastfail(0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// init input
|
|
||||||
InputManager::instance().load();
|
|
||||||
|
|
||||||
InitializeGlobalVulkan();
|
InitializeGlobalVulkan();
|
||||||
|
|
||||||
Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this);
|
Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this);
|
||||||
|
@ -26,7 +26,7 @@ public:
|
|||||||
//bool IsDLC() const { return m_game_info->IsDLC(); }
|
//bool IsDLC() const { return m_game_info->IsDLC(); }
|
||||||
//bool IsUpdate() const { return m_game_info->IsUpdate(); }
|
//bool IsUpdate() const { return m_game_info->IsUpdate(); }
|
||||||
const std::string& GetExceptionMessage() const { return m_thread_exception; }
|
const std::string& GetExceptionMessage() const { return m_thread_exception; }
|
||||||
const std::string GetGameName() const { return m_title_info.GetTitleName(); }
|
const std::string GetGameName() const { return m_title_info.GetMetaTitleName(); }
|
||||||
uint32 GetTargetVersion() const { return m_title_info.GetAppTitleVersion(); }
|
uint32 GetTargetVersion() const { return m_title_info.GetAppTitleVersion(); }
|
||||||
fs::path GetTargetPath() const { return fs::path(m_target_path); }
|
fs::path GetTargetPath() const { return fs::path(m_target_path); }
|
||||||
|
|
||||||
|
@ -73,8 +73,6 @@
|
|||||||
extern WindowInfo g_window_info;
|
extern WindowInfo g_window_info;
|
||||||
extern std::shared_mutex g_mutex;
|
extern std::shared_mutex g_mutex;
|
||||||
|
|
||||||
wxDEFINE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent);
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
// ui elements
|
// ui elements
|
||||||
@ -161,8 +159,10 @@ enum
|
|||||||
MAINFRAME_ID_TIMER1 = 21800,
|
MAINFRAME_ID_TIMER1 = 21800,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent);
|
||||||
wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
|
||||||
|
wxDEFINE_EVENT(wxEVT_REQUEST_RECREATE_CANVAS, wxCommandEvent);
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
|
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
|
||||||
EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer)
|
EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer)
|
||||||
@ -237,6 +237,8 @@ EVT_COMMAND(wxID_ANY, wxEVT_GAMELIST_END_UPDATE, MainWindow::OnGameListEndUpdate
|
|||||||
EVT_COMMAND(wxID_ANY, wxEVT_ACCOUNTLIST_REFRESH, MainWindow::OnAccountListRefresh)
|
EVT_COMMAND(wxID_ANY, wxEVT_ACCOUNTLIST_REFRESH, MainWindow::OnAccountListRefresh)
|
||||||
EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle)
|
EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle)
|
||||||
|
|
||||||
|
EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_RECREATE_CANVAS, MainWindow::OnRequestRecreateCanvas)
|
||||||
|
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
class wxGameDropTarget : public wxFileDropTarget
|
class wxGameDropTarget : public wxFileDropTarget
|
||||||
@ -290,6 +292,7 @@ MainWindow::MainWindow()
|
|||||||
{
|
{
|
||||||
gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
|
gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
|
||||||
g_mainFrame = this;
|
g_mainFrame = this;
|
||||||
|
CafeSystem::SetImplementation(this);
|
||||||
|
|
||||||
RecreateMenu();
|
RecreateMenu();
|
||||||
SetClientSize(1280, 720);
|
SetClientSize(1280, 720);
|
||||||
@ -330,26 +333,10 @@ MainWindow::MainWindow()
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SetSizer(main_sizer);
|
||||||
if (!quick_launch)
|
if (!quick_launch)
|
||||||
{
|
{
|
||||||
{
|
CreateGameListAndStatusBar();
|
||||||
m_main_panel = new wxPanel(this);
|
|
||||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
// game list
|
|
||||||
m_game_list = new wxGameList(m_main_panel, MAINFRAME_GAMELIST_ID);
|
|
||||||
m_game_list->Bind(wxEVT_OPEN_SETTINGS, [this](auto&) {OpenSettings(); });
|
|
||||||
m_game_list->SetDropTarget(new wxGameDropTarget(this));
|
|
||||||
sizer->Add(m_game_list, 1, wxEXPAND);
|
|
||||||
|
|
||||||
// info, warning bar
|
|
||||||
m_info_bar = new wxInfoBar(m_main_panel);
|
|
||||||
m_info_bar->SetShowHideEffects(wxSHOW_EFFECT_BLEND, wxSHOW_EFFECT_BLEND);
|
|
||||||
m_info_bar->SetEffectDuration(500);
|
|
||||||
sizer->Add(m_info_bar, 0, wxALL | wxEXPAND, 5);
|
|
||||||
|
|
||||||
m_main_panel->SetSizer(sizer);
|
|
||||||
main_sizer->Add(m_main_panel, 1, wxEXPAND, 0, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -367,10 +354,10 @@ MainWindow::MainWindow()
|
|||||||
LoadSettings();
|
LoadSettings();
|
||||||
|
|
||||||
auto& config = GetConfig();
|
auto& config = GetConfig();
|
||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
if (config.use_discord_presence)
|
if (config.use_discord_presence)
|
||||||
m_discord = std::make_unique<DiscordPresence>();
|
m_discord = std::make_unique<DiscordPresence>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Bind(wxEVT_OPEN_GRAPHIC_PACK, &MainWindow::OnGraphicWindowOpen, this);
|
Bind(wxEVT_OPEN_GRAPHIC_PACK, &MainWindow::OnGraphicWindowOpen, this);
|
||||||
Bind(wxEVT_LAUNCH_GAME, &MainWindow::OnLaunchFromFile, this);
|
Bind(wxEVT_LAUNCH_GAME, &MainWindow::OnLaunchFromFile, this);
|
||||||
@ -385,7 +372,6 @@ MainWindow::~MainWindow()
|
|||||||
{
|
{
|
||||||
if (m_padView)
|
if (m_padView)
|
||||||
{
|
{
|
||||||
//delete m_padView;
|
|
||||||
m_padView->Destroy();
|
m_padView->Destroy();
|
||||||
m_padView = nullptr;
|
m_padView = nullptr;
|
||||||
}
|
}
|
||||||
@ -396,6 +382,40 @@ MainWindow::~MainWindow()
|
|||||||
g_mainFrame = nullptr;
|
g_mainFrame = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::CreateGameListAndStatusBar()
|
||||||
|
{
|
||||||
|
if(m_main_panel)
|
||||||
|
return; // already displayed
|
||||||
|
m_main_panel = new wxPanel(this);
|
||||||
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
// game list
|
||||||
|
m_game_list = new wxGameList(m_main_panel, MAINFRAME_GAMELIST_ID);
|
||||||
|
m_game_list->Bind(wxEVT_OPEN_SETTINGS, [this](auto&) {OpenSettings(); });
|
||||||
|
m_game_list->SetDropTarget(new wxGameDropTarget(this));
|
||||||
|
sizer->Add(m_game_list, 1, wxEXPAND);
|
||||||
|
|
||||||
|
// info, warning bar
|
||||||
|
m_info_bar = new wxInfoBar(m_main_panel);
|
||||||
|
m_info_bar->SetShowHideEffects(wxSHOW_EFFECT_BLEND, wxSHOW_EFFECT_BLEND);
|
||||||
|
m_info_bar->SetEffectDuration(500);
|
||||||
|
sizer->Add(m_info_bar, 0, wxALL | wxEXPAND, 5);
|
||||||
|
|
||||||
|
m_main_panel->SetSizer(sizer);
|
||||||
|
|
||||||
|
auto* main_sizer = this->GetSizer();
|
||||||
|
main_sizer->Add(m_main_panel, 1, wxEXPAND, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::DestroyGameListAndStatusBar()
|
||||||
|
{
|
||||||
|
if(!m_main_panel)
|
||||||
|
return;
|
||||||
|
m_main_panel->Destroy();
|
||||||
|
m_main_panel = nullptr;
|
||||||
|
m_game_list = nullptr;
|
||||||
|
m_info_bar = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
wxString MainWindow::GetInitialWindowTitle()
|
wxString MainWindow::GetInitialWindowTitle()
|
||||||
{
|
{
|
||||||
return BUILD_VERSION_WITH_NAME_STRING;
|
return BUILD_VERSION_WITH_NAME_STRING;
|
||||||
@ -435,7 +455,7 @@ void MainWindow::OnClose(wxCloseEvent& event)
|
|||||||
|
|
||||||
event.Skip();
|
event.Skip();
|
||||||
|
|
||||||
CafeSystem::ShutdownTitle();
|
CafeSystem::Shutdown();
|
||||||
DestroyCanvas();
|
DestroyCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,36 +578,13 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY
|
|||||||
|
|
||||||
wxWindowUpdateLocker lock(this);
|
wxWindowUpdateLocker lock(this);
|
||||||
|
|
||||||
auto* main_sizer = GetSizer();
|
DestroyGameListAndStatusBar();
|
||||||
// remove old gamelist panel
|
|
||||||
if (m_main_panel)
|
|
||||||
{
|
|
||||||
m_main_panel->Hide();
|
|
||||||
main_sizer->Detach(m_main_panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create render canvas rendering
|
|
||||||
m_game_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS);
|
|
||||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
|
|
||||||
// shouldn't be needed, but who knows
|
|
||||||
m_game_panel->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this);
|
|
||||||
m_game_panel->Bind(wxEVT_CHAR, &MainWindow::OnChar, this);
|
|
||||||
|
|
||||||
m_game_panel->SetSizer(sizer);
|
|
||||||
main_sizer->Add(m_game_panel, 1, wxEXPAND, 0, nullptr);
|
|
||||||
|
|
||||||
m_game_launched = true;
|
m_game_launched = true;
|
||||||
m_loadMenuItem->Enable(false);
|
m_loadMenuItem->Enable(false);
|
||||||
m_installUpdateMenuItem->Enable(false);
|
m_installUpdateMenuItem->Enable(false);
|
||||||
m_memorySearcherMenuItem->Enable(true);
|
m_memorySearcherMenuItem->Enable(true);
|
||||||
|
|
||||||
if (m_game_list)
|
|
||||||
{
|
|
||||||
delete m_game_list;
|
|
||||||
m_game_list = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_launched_game_name = CafeSystem::GetForegroundTitleName();
|
m_launched_game_name = CafeSystem::GetForegroundTitleName();
|
||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
if (m_discord)
|
if (m_discord)
|
||||||
@ -679,6 +676,8 @@ void MainWindow::OnFileMenu(wxCommandEvent& event)
|
|||||||
DestroyCanvas();
|
DestroyCanvas();
|
||||||
m_game_launched = false;
|
m_game_launched = false;
|
||||||
RecreateMenu();
|
RecreateMenu();
|
||||||
|
CreateGameListAndStatusBar();
|
||||||
|
DoLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1547,6 +1546,18 @@ void MainWindow::AsyncSetTitle(std::string_view windowTitle)
|
|||||||
|
|
||||||
void MainWindow::CreateCanvas()
|
void MainWindow::CreateCanvas()
|
||||||
{
|
{
|
||||||
|
// create panel for canvas
|
||||||
|
m_game_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS);
|
||||||
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
// shouldn't be needed, but who knows
|
||||||
|
m_game_panel->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this);
|
||||||
|
m_game_panel->Bind(wxEVT_CHAR, &MainWindow::OnChar, this);
|
||||||
|
|
||||||
|
m_game_panel->SetSizer(sizer);
|
||||||
|
this->GetSizer()->Add(m_game_panel, 1, wxEXPAND, 0, nullptr);
|
||||||
|
|
||||||
|
// create canvas
|
||||||
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
||||||
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
|
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||||
else
|
else
|
||||||
@ -1580,14 +1591,18 @@ void MainWindow::DestroyCanvas()
|
|||||||
{
|
{
|
||||||
if (m_padView)
|
if (m_padView)
|
||||||
{
|
{
|
||||||
m_padView->Destroy();
|
m_padView->DestroyCanvas();
|
||||||
m_padView = nullptr;
|
|
||||||
}
|
}
|
||||||
if (m_render_canvas)
|
if (m_render_canvas)
|
||||||
{
|
{
|
||||||
m_render_canvas->Destroy();
|
m_render_canvas->Destroy();
|
||||||
m_render_canvas = nullptr;
|
m_render_canvas = nullptr;
|
||||||
}
|
}
|
||||||
|
if(m_game_panel)
|
||||||
|
{
|
||||||
|
m_game_panel->Destroy();
|
||||||
|
m_game_panel = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OnSizeEvent(wxSizeEvent& event)
|
void MainWindow::OnSizeEvent(wxSizeEvent& event)
|
||||||
@ -2048,9 +2063,9 @@ void MainWindow::RecreateMenu()
|
|||||||
|
|
||||||
auto& config = GetConfig();
|
auto& config = GetConfig();
|
||||||
|
|
||||||
m_menuBar = new wxMenuBar;
|
m_menuBar = new wxMenuBar();
|
||||||
// file submenu
|
// file submenu
|
||||||
m_fileMenu = new wxMenu;
|
m_fileMenu = new wxMenu();
|
||||||
|
|
||||||
if (!m_game_launched)
|
if (!m_game_launched)
|
||||||
{
|
{
|
||||||
@ -2084,7 +2099,7 @@ void MainWindow::RecreateMenu()
|
|||||||
{
|
{
|
||||||
// add 'Stop emulation' menu entry to file menu
|
// add 'Stop emulation' menu entry to file menu
|
||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_END_EMULATION, _("End emulation"));
|
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_END_EMULATION, _("Stop emulation"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2094,7 +2109,7 @@ void MainWindow::RecreateMenu()
|
|||||||
m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit"));
|
m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit"));
|
||||||
m_menuBar->Append(m_fileMenu, _("&File"));
|
m_menuBar->Append(m_fileMenu, _("&File"));
|
||||||
// options->account submenu
|
// options->account submenu
|
||||||
m_optionsAccountMenu = new wxMenu;
|
m_optionsAccountMenu = new wxMenu();
|
||||||
const auto account_id = ActiveSettings::GetPersistentId();
|
const auto account_id = ActiveSettings::GetPersistentId();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for(const auto& account : Account::GetAccounts())
|
for(const auto& account : Account::GetAccounts())
|
||||||
@ -2106,23 +2121,9 @@ void MainWindow::RecreateMenu()
|
|||||||
|
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
//optionsAccountMenu->AppendSeparator(); TODO
|
|
||||||
//optionsAccountMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 + index, _("Online enabled"))->Check(config.account.online_enabled);
|
|
||||||
|
|
||||||
// options->region submenu
|
|
||||||
//wxMenu* optionsRegionMenu = new wxMenu;
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_AUTO, _("&Auto"), wxEmptyString)->Check(config.console_region == ConsoleRegion::Auto);
|
|
||||||
////optionsRegionMenu->AppendSeparator();
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_USA, _("&USA"), wxEmptyString)->Check(config.console_region == ConsoleRegion::USA);
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_EUR, _("&Europe"), wxEmptyString)->Check(config.console_region == ConsoleRegion::EUR);
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_JPN, _("&Japan"), wxEmptyString)->Check(config.console_region == ConsoleRegion::JPN);
|
|
||||||
//// optionsRegionMenu->Append(MAINFRAME_MENU_ID_OPTIONS_REGION_AUS, wxT("&Australia"), wxEmptyString, wxITEM_RADIO)->Check(config_get()->region==3); -> Was merged into Europe?
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_CHN, _("&China"), wxEmptyString)->Check(config.console_region == ConsoleRegion::CHN);
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_KOR, _("&Korea"), wxEmptyString)->Check(config.console_region == ConsoleRegion::KOR);
|
|
||||||
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_TWN, _("&Taiwan"), wxEmptyString)->Check(config.console_region == ConsoleRegion::TWN);
|
|
||||||
|
|
||||||
// options->console language submenu
|
// options->console language submenu
|
||||||
wxMenu* optionsConsoleLanguageMenu = new wxMenu;
|
wxMenu* optionsConsoleLanguageMenu = new wxMenu();
|
||||||
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH, _("&English"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::EN);
|
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH, _("&English"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::EN);
|
||||||
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, _("&Japanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::JA);
|
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, _("&Japanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::JA);
|
||||||
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH, _("&French"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::FR);
|
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH, _("&French"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::FR);
|
||||||
@ -2137,12 +2138,11 @@ void MainWindow::RecreateMenu()
|
|||||||
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, _("&Taiwanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::TW);
|
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, _("&Taiwanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::TW);
|
||||||
|
|
||||||
// options submenu
|
// options submenu
|
||||||
wxMenu* optionsMenu = new wxMenu;
|
wxMenu* optionsMenu = new wxMenu();
|
||||||
m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen"), wxEmptyString);
|
m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen"), wxEmptyString);
|
||||||
m_fullscreenMenuItem->Check(ActiveSettings::FullscreenEnabled());
|
m_fullscreenMenuItem->Check(ActiveSettings::FullscreenEnabled());
|
||||||
|
|
||||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs"));
|
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs"));
|
||||||
//optionsMenu->AppendSubMenu(optionsVCAMenu, _("&GPU buffer cache accuracy"));
|
|
||||||
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
|
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
|
||||||
m_padViewMenuItem->Check(GetConfig().pad_open);
|
m_padViewMenuItem->Check(GetConfig().pad_open);
|
||||||
optionsMenu->AppendSeparator();
|
optionsMenu->AppendSeparator();
|
||||||
@ -2151,12 +2151,11 @@ void MainWindow::RecreateMenu()
|
|||||||
|
|
||||||
optionsMenu->AppendSeparator();
|
optionsMenu->AppendSeparator();
|
||||||
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));
|
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));
|
||||||
//optionsMenu->AppendSubMenu(optionsRegionMenu, _("&Console region"));
|
|
||||||
optionsMenu->AppendSubMenu(optionsConsoleLanguageMenu, _("&Console language"));
|
optionsMenu->AppendSubMenu(optionsConsoleLanguageMenu, _("&Console language"));
|
||||||
m_menuBar->Append(optionsMenu, _("&Options"));
|
m_menuBar->Append(optionsMenu, _("&Options"));
|
||||||
|
|
||||||
// tools submenu
|
// tools submenu
|
||||||
wxMenu* toolsMenu = new wxMenu;
|
wxMenu* toolsMenu = new wxMenu();
|
||||||
m_memorySearcherMenuItem = toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, _("&Memory searcher"));
|
m_memorySearcherMenuItem = toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, _("&Memory searcher"));
|
||||||
m_memorySearcherMenuItem->Enable(false);
|
m_memorySearcherMenuItem->Enable(false);
|
||||||
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
|
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
|
||||||
@ -2164,7 +2163,7 @@ void MainWindow::RecreateMenu()
|
|||||||
m_menuBar->Append(toolsMenu, _("&Tools"));
|
m_menuBar->Append(toolsMenu, _("&Tools"));
|
||||||
|
|
||||||
// cpu timer speed menu
|
// cpu timer speed menu
|
||||||
wxMenu* timerSpeedMenu = new wxMenu;
|
wxMenu* timerSpeedMenu = new wxMenu();
|
||||||
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_1X, _("&1x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 3);
|
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_1X, _("&1x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 3);
|
||||||
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_2X, _("&2x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 2);
|
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_2X, _("&2x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 2);
|
||||||
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_4X, _("&4x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 1);
|
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_4X, _("&4x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 1);
|
||||||
@ -2174,12 +2173,12 @@ void MainWindow::RecreateMenu()
|
|||||||
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, _("&0.125x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 6);
|
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, _("&0.125x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 6);
|
||||||
|
|
||||||
// cpu submenu
|
// cpu submenu
|
||||||
wxMenu* cpuMenu = new wxMenu;
|
wxMenu* cpuMenu = new wxMenu();
|
||||||
cpuMenu->AppendSubMenu(timerSpeedMenu, _("&Timer speed"));
|
cpuMenu->AppendSubMenu(timerSpeedMenu, _("&Timer speed"));
|
||||||
m_menuBar->Append(cpuMenu, _("&CPU"));
|
m_menuBar->Append(cpuMenu, _("&CPU"));
|
||||||
|
|
||||||
// nfc submenu
|
// nfc submenu
|
||||||
wxMenu* nfcMenu = new wxMenu;
|
wxMenu* nfcMenu = new wxMenu();
|
||||||
m_nfcMenu = nfcMenu;
|
m_nfcMenu = nfcMenu;
|
||||||
nfcMenu->Append(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, _("&Scan NFC tag from file"))->Enable(false);
|
nfcMenu->Append(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, _("&Scan NFC tag from file"))->Enable(false);
|
||||||
m_menuBar->Append(nfcMenu, _("&NFC"));
|
m_menuBar->Append(nfcMenu, _("&NFC"));
|
||||||
@ -2216,7 +2215,7 @@ void MainWindow::RecreateMenu()
|
|||||||
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, _("&Shaders"), wxEmptyString)->Check(ActiveSettings::DumpShadersEnabled());
|
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, _("&Shaders"), wxEmptyString)->Check(ActiveSettings::DumpShadersEnabled());
|
||||||
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, _("&nlibcurl HTTP/HTTPS requests"), wxEmptyString);
|
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, _("&nlibcurl HTTP/HTTPS requests"), wxEmptyString);
|
||||||
// debug submenu
|
// debug submenu
|
||||||
wxMenu* debugMenu = new wxMenu;
|
wxMenu* debugMenu = new wxMenu();
|
||||||
m_debugMenu = debugMenu;
|
m_debugMenu = debugMenu;
|
||||||
debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging"));
|
debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging"));
|
||||||
debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump"));
|
debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump"));
|
||||||
@ -2251,7 +2250,7 @@ void MainWindow::RecreateMenu()
|
|||||||
|
|
||||||
m_menuBar->Append(debugMenu, _("&Debug"));
|
m_menuBar->Append(debugMenu, _("&Debug"));
|
||||||
// help menu
|
// help menu
|
||||||
wxMenu* helpMenu = new wxMenu;
|
wxMenu* helpMenu = new wxMenu();
|
||||||
//helpMenu->Append(MAINFRAME_MENU_ID_HELP_WEB, wxT("&Visit website"));
|
//helpMenu->Append(MAINFRAME_MENU_ID_HELP_WEB, wxT("&Visit website"));
|
||||||
//helpMenu->AppendSeparator();
|
//helpMenu->AppendSeparator();
|
||||||
m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates"));
|
m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates"));
|
||||||
@ -2272,7 +2271,7 @@ void MainWindow::RecreateMenu()
|
|||||||
m_memorySearcherMenuItem->Enable(true);
|
m_memorySearcherMenuItem->Enable(true);
|
||||||
m_nfcMenu->Enable(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, true);
|
m_nfcMenu->Enable(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, true);
|
||||||
|
|
||||||
// disable OpenGL logging (currently cant be toggled after OpenGL backend is initialized)
|
// these options cant be toggled after the renderer backend is initialized:
|
||||||
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), false);
|
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), false);
|
||||||
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), false);
|
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), false);
|
||||||
|
|
||||||
@ -2343,31 +2342,9 @@ void MainWindow::RestoreSettingsAfterGameExited()
|
|||||||
void MainWindow::UpdateSettingsAfterGameLaunch()
|
void MainWindow::UpdateSettingsAfterGameLaunch()
|
||||||
{
|
{
|
||||||
m_update_available = {};
|
m_update_available = {};
|
||||||
//m_game_launched = true;
|
|
||||||
RecreateMenu();
|
RecreateMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MainWindow::GetRegionString(uint32 region) const
|
|
||||||
{
|
|
||||||
switch (region)
|
|
||||||
{
|
|
||||||
case 0x1:
|
|
||||||
return "JPN";
|
|
||||||
case 0x2:
|
|
||||||
return "USA";
|
|
||||||
case 0x4:
|
|
||||||
return "EUR";
|
|
||||||
case 0x10:
|
|
||||||
return "CHN";
|
|
||||||
case 0x20:
|
|
||||||
return "KOR";
|
|
||||||
case 0x40:
|
|
||||||
return "TWN";
|
|
||||||
default:
|
|
||||||
return std::to_string(region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::OnGraphicWindowClose(wxCloseEvent& event)
|
void MainWindow::OnGraphicWindowClose(wxCloseEvent& event)
|
||||||
{
|
{
|
||||||
m_graphic_pack_window->Destroy();
|
m_graphic_pack_window->Destroy();
|
||||||
@ -2378,7 +2355,6 @@ void MainWindow::OnGraphicWindowOpen(wxTitleIdEvent& event)
|
|||||||
{
|
{
|
||||||
if (m_graphic_pack_window)
|
if (m_graphic_pack_window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_graphic_pack_window = new GraphicPacksWindow2(this, event.GetTitleId());
|
m_graphic_pack_window = new GraphicPacksWindow2(this, event.GetTitleId());
|
||||||
m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this);
|
m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this);
|
||||||
m_graphic_pack_window->Show(true);
|
m_graphic_pack_window->Show(true);
|
||||||
@ -2395,3 +2371,20 @@ void MainWindow::RequestLaunchGame(fs::path filePath, wxLaunchGameEvent::INITIAT
|
|||||||
wxLaunchGameEvent evt(filePath, initiatedBy);
|
wxLaunchGameEvent evt(filePath, initiatedBy);
|
||||||
wxPostEvent(g_mainFrame, evt);
|
wxPostEvent(g_mainFrame, evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnRequestRecreateCanvas(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
CounterSemaphore* sem = (CounterSemaphore*)event.GetClientData();
|
||||||
|
DestroyCanvas();
|
||||||
|
CreateCanvas();
|
||||||
|
sem->increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::CafeRecreateCanvas()
|
||||||
|
{
|
||||||
|
CounterSemaphore sem;
|
||||||
|
auto* evt = new wxCommandEvent(wxEVT_REQUEST_RECREATE_CANVAS);
|
||||||
|
evt->SetClientData((void*)&sem);
|
||||||
|
wxQueueEvent(g_mainFrame, evt);
|
||||||
|
sem.decrementWithWait();
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
|
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
|
||||||
|
#include "Cafe/CafeSystem.h"
|
||||||
|
|
||||||
class DebuggerWindow2;
|
class DebuggerWindow2;
|
||||||
struct GameEntry;
|
struct GameEntry;
|
||||||
@ -50,7 +51,7 @@ private:
|
|||||||
INITIATED_BY m_initiatedBy;
|
INITIATED_BY m_initiatedBy;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MainWindow : public wxFrame
|
class MainWindow : public wxFrame, public CafeSystem::SystemImplementation
|
||||||
{
|
{
|
||||||
friend class CemuApp;
|
friend class CemuApp;
|
||||||
|
|
||||||
@ -58,6 +59,9 @@ public:
|
|||||||
MainWindow();
|
MainWindow();
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
void CreateGameListAndStatusBar();
|
||||||
|
void DestroyGameListAndStatusBar();
|
||||||
|
|
||||||
void UpdateSettingsAfterGameLaunch();
|
void UpdateSettingsAfterGameLaunch();
|
||||||
void RestoreSettingsAfterGameExited();
|
void RestoreSettingsAfterGameExited();
|
||||||
|
|
||||||
@ -151,6 +155,11 @@ private:
|
|||||||
|
|
||||||
void OnTimer(wxTimerEvent& event);
|
void OnTimer(wxTimerEvent& event);
|
||||||
|
|
||||||
|
// CafeSystem implementation
|
||||||
|
void CafeRecreateCanvas() override;
|
||||||
|
|
||||||
|
void OnRequestRecreateCanvas(wxCommandEvent& event);
|
||||||
|
|
||||||
wxRect GetDesktopRect();
|
wxRect GetDesktopRect();
|
||||||
|
|
||||||
MemorySearcherTool* m_toolWindow = nullptr;
|
MemorySearcherTool* m_toolWindow = nullptr;
|
||||||
@ -183,8 +192,6 @@ private:
|
|||||||
void LoadSettings();
|
void LoadSettings();
|
||||||
void SaveSettings();
|
void SaveSettings();
|
||||||
|
|
||||||
std::string GetRegionString(uint32 region) const;
|
|
||||||
|
|
||||||
void OnGraphicWindowClose(wxCloseEvent& event);
|
void OnGraphicWindowClose(wxCloseEvent& event);
|
||||||
void OnGraphicWindowOpen(wxTitleIdEvent& event);
|
void OnGraphicWindowOpen(wxTitleIdEvent& event);
|
||||||
|
|
||||||
@ -195,42 +202,40 @@ private:
|
|||||||
wxWindow* m_render_canvas{};
|
wxWindow* m_render_canvas{};
|
||||||
|
|
||||||
// gamelist
|
// gamelist
|
||||||
wxGameList* m_game_list;
|
wxGameList* m_game_list{};
|
||||||
wxInfoBar* m_info_bar;
|
wxInfoBar* m_info_bar{};
|
||||||
|
|
||||||
// menu
|
// menu
|
||||||
wxMenuBar* m_menuBar = nullptr;
|
wxMenuBar* m_menuBar{};
|
||||||
|
|
||||||
// file
|
// file
|
||||||
wxMenu* m_fileMenu;
|
wxMenu* m_fileMenu{};
|
||||||
wxMenuItem* m_fileMenuSeparator0;
|
wxMenuItem* m_fileMenuSeparator0{};
|
||||||
wxMenuItem* m_fileMenuSeparator1;
|
wxMenuItem* m_fileMenuSeparator1{};
|
||||||
wxMenuItem* m_loadMenuItem;
|
wxMenuItem* m_loadMenuItem{};
|
||||||
wxMenuItem* m_installUpdateMenuItem;
|
wxMenuItem* m_installUpdateMenuItem{};
|
||||||
wxMenuItem* m_exitMenuItem;
|
wxMenuItem* m_exitMenuItem{};
|
||||||
|
|
||||||
// options
|
// options
|
||||||
//wxMenu* m_gpuBufferCacheAccuracySubmenu;
|
wxMenu* m_optionsAccountMenu{};
|
||||||
wxMenu* m_optionsAccountMenu;
|
|
||||||
|
|
||||||
wxMenuItem* m_fullscreenMenuItem;
|
wxMenuItem* m_fullscreenMenuItem{};
|
||||||
wxMenuItem* m_padViewMenuItem;
|
wxMenuItem* m_padViewMenuItem{};
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
wxMenuItem* m_memorySearcherMenuItem;
|
wxMenuItem* m_memorySearcherMenuItem{};
|
||||||
|
|
||||||
// cpu
|
// cpu
|
||||||
//wxMenu* m_cpuModeSubmenu;
|
wxMenu* m_cpuTimerSubmenu{};
|
||||||
wxMenu* m_cpuTimerSubmenu;
|
|
||||||
|
|
||||||
// nfc
|
// nfc
|
||||||
wxMenu* m_nfcMenu;
|
wxMenu* m_nfcMenu{};
|
||||||
wxMenuItem* m_nfcMenuSeparator0;
|
wxMenuItem* m_nfcMenuSeparator0{};
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
wxMenu* m_debugMenu;
|
wxMenu* m_debugMenu{};
|
||||||
wxMenu* m_loggingSubmenu;
|
wxMenu* m_loggingSubmenu{};
|
||||||
wxMenuItem* m_asyncCompile;
|
wxMenuItem* m_asyncCompile{};
|
||||||
|
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -90,6 +90,14 @@ void PadViewFrame::InitializeRenderCanvas()
|
|||||||
SendSizeEvent();
|
SendSizeEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PadViewFrame::DestroyCanvas()
|
||||||
|
{
|
||||||
|
if(!m_render_canvas)
|
||||||
|
return;
|
||||||
|
m_render_canvas->Destroy();
|
||||||
|
m_render_canvas = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void PadViewFrame::OnSizeEvent(wxSizeEvent& event)
|
void PadViewFrame::OnSizeEvent(wxSizeEvent& event)
|
||||||
{
|
{
|
||||||
if (!IsMaximized() && !IsFullScreen())
|
if (!IsMaximized() && !IsFullScreen())
|
||||||
|
@ -14,6 +14,7 @@ public:
|
|||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
void InitializeRenderCanvas();
|
void InitializeRenderCanvas();
|
||||||
|
void DestroyCanvas();
|
||||||
|
|
||||||
void OnKeyUp(wxKeyEvent& event);
|
void OnKeyUp(wxKeyEvent& event);
|
||||||
void OnChar(wxKeyEvent& event);
|
void OnChar(wxKeyEvent& event);
|
||||||
|
@ -36,8 +36,6 @@ public:
|
|||||||
OpenGLCanvas(wxWindow* parent, const wxSize& size, bool is_main_window)
|
OpenGLCanvas(wxWindow* parent, const wxSize& size, bool is_main_window)
|
||||||
: IRenderCanvas(is_main_window), wxGLCanvas(parent, wxID_ANY, g_gl_attribute_list, wxDefaultPosition, size, wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
: IRenderCanvas(is_main_window), wxGLCanvas(parent, wxID_ANY, g_gl_attribute_list, wxDefaultPosition, size, wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "Creating OpenGL canvas");
|
|
||||||
|
|
||||||
if (m_is_main_window)
|
if (m_is_main_window)
|
||||||
{
|
{
|
||||||
sGLTVView = this;
|
sGLTVView = this;
|
||||||
|
@ -53,8 +53,9 @@ VulkanCanvas::~VulkanCanvas()
|
|||||||
|
|
||||||
if(!m_is_main_window)
|
if(!m_is_main_window)
|
||||||
{
|
{
|
||||||
if(auto vulkan_renderer = VulkanRenderer::GetInstance())
|
VulkanRenderer* vkr = (VulkanRenderer*)g_renderer.get();
|
||||||
vulkan_renderer->StopUsingPadAndWait();
|
if(vkr)
|
||||||
|
vkr->StopUsingPadAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ std::string wxGameList::GetNameByTitleId(uint64 titleId)
|
|||||||
return "Unknown title";
|
return "Unknown title";
|
||||||
std::string name;
|
std::string name;
|
||||||
if (!GetConfig().GetGameListCustomName(titleId, name))
|
if (!GetConfig().GetGameListCustomName(titleId, name))
|
||||||
name = titleInfo.GetTitleName();
|
name = titleInfo.GetMetaTitleName();
|
||||||
m_name_cache.emplace(titleId, name);
|
m_name_cache.emplace(titleId, name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -967,13 +967,11 @@ int wxGameList::FindInsertPosition(TitleId titleId)
|
|||||||
void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event)
|
void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event)
|
||||||
{
|
{
|
||||||
const auto titleId = event.GetTitleId();
|
const auto titleId = event.GetTitleId();
|
||||||
// get GameInfo from title list
|
|
||||||
GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId);
|
GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId);
|
||||||
|
if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle())
|
||||||
if (!gameInfo.IsValid())
|
|
||||||
{
|
{
|
||||||
// entry no longer exists
|
// entry no longer exists or is not a valid game
|
||||||
// we dont need to do anything here because all delete operations should trigger a full list refresh
|
// we dont need to remove list entries here because all delete operations should trigger a full list refresh
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TitleId baseTitleId = gameInfo.GetBaseTitleId();
|
TitleId baseTitleId = gameInfo.GetBaseTitleId();
|
||||||
|
@ -104,6 +104,11 @@ void ImGui_PrecacheFonts()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImGui_ClearFonts()
|
||||||
|
{
|
||||||
|
g_imgui_fonts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
ImFont* ImGui_GetFont(float size)
|
ImFont* ImGui_GetFont(float size)
|
||||||
{
|
{
|
||||||
const auto it = g_imgui_fonts.find((int)size);
|
const auto it = g_imgui_fonts.find((int)size);
|
||||||
|
@ -12,5 +12,6 @@ inline bool operator>=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x >= r
|
|||||||
bool ImGui_BeginPadDistinct(const char* name, bool* p_open, ImGuiWindowFlags flags, bool pad);
|
bool ImGui_BeginPadDistinct(const char* name, bool* p_open, ImGuiWindowFlags flags, bool pad);
|
||||||
|
|
||||||
void ImGui_PrecacheFonts();
|
void ImGui_PrecacheFonts();
|
||||||
|
void ImGui_ClearFonts();
|
||||||
ImFont* ImGui_GetFont(float size);
|
ImFont* ImGui_GetFont(float size);
|
||||||
void ImGui_UpdateWindowInformation(bool mainWindow);
|
void ImGui_UpdateWindowInformation(bool mainWindow);
|
97
src/main.cpp
97
src/main.cpp
@ -5,14 +5,15 @@
|
|||||||
#include "Cafe/OS/RPL/rpl.h"
|
#include "Cafe/OS/RPL/rpl.h"
|
||||||
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
||||||
#include "Cafe/OS/libs/gx2/GX2.h"
|
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||||
|
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
||||||
|
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
||||||
#include "Cafe/GameProfile/GameProfile.h"
|
#include "Cafe/GameProfile/GameProfile.h"
|
||||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||||
#include "config/CemuConfig.h"
|
#include "config/CemuConfig.h"
|
||||||
#include "config/NetworkSettings.h"
|
#include "config/NetworkSettings.h"
|
||||||
#include "gui/CemuApp.h"
|
|
||||||
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
|
||||||
#include "config/LaunchSettings.h"
|
#include "config/LaunchSettings.h"
|
||||||
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
#include "input/InputManager.h"
|
||||||
|
#include "gui/CemuApp.h"
|
||||||
|
|
||||||
#include "Cafe/CafeSystem.h"
|
#include "Cafe/CafeSystem.h"
|
||||||
#include "Cafe/TitleList/TitleList.h"
|
#include "Cafe/TitleList/TitleList.h"
|
||||||
@ -113,7 +114,6 @@ void infoLog_cemuStartup()
|
|||||||
cemuLog_log(LogType::Force, "------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING);
|
cemuLog_log(LogType::Force, "------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING);
|
||||||
cemuLog_log(LogType::Force, "Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base);
|
cemuLog_log(LogType::Force, "Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base);
|
||||||
cemuLog_log(LogType::Force, "mlc01 path: {}", _pathToUtf8(ActiveSettings::GetMlcPath()));
|
cemuLog_log(LogType::Force, "mlc01 path: {}", _pathToUtf8(ActiveSettings::GetMlcPath()));
|
||||||
// check for wine version
|
|
||||||
checkForWine();
|
checkForWine();
|
||||||
// CPU and RAM info
|
// CPU and RAM info
|
||||||
logCPUAndMemoryInfo();
|
logCPUAndMemoryInfo();
|
||||||
@ -158,16 +158,9 @@ void reconfigureVkDrivers()
|
|||||||
_putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1");
|
_putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1");
|
||||||
}
|
}
|
||||||
|
|
||||||
void mainEmulatorCommonInit()
|
void WindowsInitCwd()
|
||||||
{
|
{
|
||||||
reconfigureGLDrivers();
|
#if BOOST_OS_WINDOWS
|
||||||
reconfigureVkDrivers();
|
|
||||||
// crypto init
|
|
||||||
AES128_init();
|
|
||||||
// init PPC timer (call this as early as possible because it measures frequency of RDTSC using an asynchronous thread over 3 seconds)
|
|
||||||
PPCTimer_init();
|
|
||||||
|
|
||||||
#if BOOST_OS_WINDOWS
|
|
||||||
executablePath.resize(4096);
|
executablePath.resize(4096);
|
||||||
int i = GetModuleFileName(NULL, executablePath.data(), executablePath.size());
|
int i = GetModuleFileName(NULL, executablePath.data(), executablePath.size());
|
||||||
if(i >= 0)
|
if(i >= 0)
|
||||||
@ -175,10 +168,22 @@ void mainEmulatorCommonInit()
|
|||||||
else
|
else
|
||||||
executablePath.clear();
|
executablePath.clear();
|
||||||
SetCurrentDirectory(executablePath.c_str());
|
SetCurrentDirectory(executablePath.c_str());
|
||||||
|
|
||||||
// set high priority
|
// set high priority
|
||||||
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
|
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CemuCommonInit()
|
||||||
|
{
|
||||||
|
reconfigureGLDrivers();
|
||||||
|
reconfigureVkDrivers();
|
||||||
|
// crypto init
|
||||||
|
AES128_init();
|
||||||
|
// init PPC timer
|
||||||
|
// call this as early as possible because it measures frequency of RDTSC using an asynchronous thread over 3 seconds
|
||||||
|
PPCTimer_init();
|
||||||
|
|
||||||
|
WindowsInitCwd();
|
||||||
ExceptionHandler_Init();
|
ExceptionHandler_Init();
|
||||||
// read config
|
// read config
|
||||||
g_config.Load();
|
g_config.Load();
|
||||||
@ -186,13 +191,31 @@ void mainEmulatorCommonInit()
|
|||||||
n_config.Load();
|
n_config.Load();
|
||||||
// symbol storage
|
// symbol storage
|
||||||
rplSymbolStorage_init();
|
rplSymbolStorage_init();
|
||||||
// static initialization
|
// parallelize expensive init code
|
||||||
IAudioAPI::InitializeStatic();
|
std::future<int> futureInitAudioAPI = std::async(std::launch::async, []{ IAudioAPI::InitializeStatic(); IAudioInputAPI::InitializeStatic(); return 0; });
|
||||||
IAudioInputAPI::InitializeStatic();
|
std::future<int> futureInitGraphicPacks = std::async(std::launch::async, []{ GraphicPack2::LoadAll(); return 0; });
|
||||||
// load graphic packs (must happen before config is loaded)
|
InputManager::instance().load();
|
||||||
GraphicPack2::LoadAll();
|
futureInitAudioAPI.wait();
|
||||||
// initialize file system
|
futureInitGraphicPacks.wait();
|
||||||
fsc_init();
|
// log Cemu startup info
|
||||||
|
infoLog_cemuStartup();
|
||||||
|
// init Cafe system
|
||||||
|
CafeSystem::Initialize();
|
||||||
|
// init title list
|
||||||
|
CafeTitleList::Initialize(ActiveSettings::GetUserDataPath("title_list_cache.xml"));
|
||||||
|
for (auto& it : GetConfig().game_paths)
|
||||||
|
CafeTitleList::AddScanPath(it);
|
||||||
|
fs::path mlcPath = ActiveSettings::GetMlcPath();
|
||||||
|
if (!mlcPath.empty())
|
||||||
|
CafeTitleList::SetMLCPath(mlcPath);
|
||||||
|
CafeTitleList::Refresh();
|
||||||
|
// init save list
|
||||||
|
CafeSaveList::Initialize();
|
||||||
|
if (!mlcPath.empty())
|
||||||
|
{
|
||||||
|
CafeSaveList::SetMLCPath(mlcPath);
|
||||||
|
CafeSaveList::Refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mainEmulatorLLE();
|
void mainEmulatorLLE();
|
||||||
@ -218,35 +241,7 @@ int mainEmulatorHLE()
|
|||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
unitTests();
|
unitTests();
|
||||||
#endif
|
#endif
|
||||||
// init common
|
CemuCommonInit();
|
||||||
mainEmulatorCommonInit();
|
|
||||||
// reserve memory (no allocations yet)
|
|
||||||
memory_init();
|
|
||||||
// init ppc core
|
|
||||||
PPCCore_init();
|
|
||||||
// log Cemu startup info
|
|
||||||
infoLog_cemuStartup();
|
|
||||||
// init RPL loader
|
|
||||||
RPLLoader_InitState();
|
|
||||||
// init IOSU components
|
|
||||||
iosuCrypto_init();
|
|
||||||
// init Cafe system (todo - the stuff above should be part of this too)
|
|
||||||
CafeSystem::Initialize();
|
|
||||||
// init title list
|
|
||||||
CafeTitleList::Initialize(ActiveSettings::GetUserDataPath("title_list_cache.xml"));
|
|
||||||
for (auto& it : GetConfig().game_paths)
|
|
||||||
CafeTitleList::AddScanPath(it);
|
|
||||||
fs::path mlcPath = ActiveSettings::GetMlcPath();
|
|
||||||
if (!mlcPath.empty())
|
|
||||||
CafeTitleList::SetMLCPath(mlcPath);
|
|
||||||
CafeTitleList::Refresh();
|
|
||||||
// init save list
|
|
||||||
CafeSaveList::Initialize();
|
|
||||||
if (!mlcPath.empty())
|
|
||||||
{
|
|
||||||
CafeSaveList::SetMLCPath(mlcPath);
|
|
||||||
CafeSaveList::Refresh();
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "gui/guiWrapper.h"
|
#include "gui/guiWrapper.h"
|
||||||
#include "Common/FileStream.h"
|
#include "Common/FileStream.h"
|
||||||
|
|
||||||
void mainEmulatorCommonInit();
|
void CemuCommonInit();
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -33,7 +33,7 @@ void loadPPCBootrom()
|
|||||||
|
|
||||||
void mainEmulatorLLE()
|
void mainEmulatorLLE()
|
||||||
{
|
{
|
||||||
mainEmulatorCommonInit();
|
CemuCommonInit();
|
||||||
// memory init
|
// memory init
|
||||||
memory_initPhysicalLayout();
|
memory_initPhysicalLayout();
|
||||||
|
|
||||||
|
@ -122,6 +122,15 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
for(auto& bucket : rangeBuckets)
|
||||||
|
{
|
||||||
|
while(!bucket.list_ranges.empty())
|
||||||
|
deleteRange(bucket.list_ranges[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -108,5 +108,94 @@ namespace StringHelpers
|
|||||||
}
|
}
|
||||||
return parsedLen;
|
return parsedLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StringLineIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using value_type = std::string_view;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = const std::string_view*;
|
||||||
|
using reference = const std::string_view&;
|
||||||
|
|
||||||
|
Iterator(std::string_view str, sint32 pos) : m_str(str), m_pos(pos)
|
||||||
|
{
|
||||||
|
update_line();
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return m_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return &m_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator& operator++()
|
||||||
|
{
|
||||||
|
m_pos = m_nextPos;
|
||||||
|
update_line();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const Iterator& lhs, const Iterator& rhs)
|
||||||
|
{
|
||||||
|
return lhs.m_str.data() == rhs.m_str.data() && lhs.m_pos == rhs.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const Iterator& lhs, const Iterator& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_line()
|
||||||
|
{
|
||||||
|
if (m_pos >= m_str.size())
|
||||||
|
{
|
||||||
|
m_pos = -1;
|
||||||
|
m_line = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto pos = m_str.find('\n', m_pos);
|
||||||
|
m_nextPos = pos != std::string_view::npos ? pos : -1;
|
||||||
|
if(m_nextPos < 0)
|
||||||
|
m_line = m_str.substr(m_pos, std::string::npos);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_line = m_str.substr(m_pos, m_nextPos - m_pos);
|
||||||
|
++m_nextPos; // skip \n
|
||||||
|
}
|
||||||
|
while (!m_line.empty() && m_line.back() == '\r')
|
||||||
|
m_line.remove_suffix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view m_str;
|
||||||
|
sint32 m_pos;
|
||||||
|
sint32 m_nextPos;
|
||||||
|
std::string_view m_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringLineIterator(std::string_view str) : m_str(str) {}
|
||||||
|
StringLineIterator(std::span<uint8> str) : m_str((const char*)str.data(), str.size()) {}
|
||||||
|
|
||||||
|
Iterator begin() const
|
||||||
|
{
|
||||||
|
return Iterator{m_str, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator end() const
|
||||||
|
{
|
||||||
|
return Iterator{m_str, -1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string_view m_str;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user