2022-08-22 22:21:23 +02:00
|
|
|
|
#include "gui/guiWrapper.h"
|
|
|
|
|
#include "gui/wxgui.h"
|
|
|
|
|
#include "util/crypto/aes128.h"
|
|
|
|
|
#include "gui/MainWindow.h"
|
|
|
|
|
#include "Cafe/OS/RPL/rpl.h"
|
|
|
|
|
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
|
|
|
|
#include "Cafe/OS/libs/gx2/GX2.h"
|
|
|
|
|
#include "Cafe/GameProfile/GameProfile.h"
|
2022-09-04 01:27:44 +02:00
|
|
|
|
#include "Cafe/GraphicPack/GraphicPack2.h"
|
2022-08-22 22:21:23 +02:00
|
|
|
|
#include "config/CemuConfig.h"
|
2022-10-11 04:04:47 +02:00
|
|
|
|
#include "config/NetworkSettings.h"
|
2022-08-22 22:21:23 +02:00
|
|
|
|
#include "gui/CemuApp.h"
|
|
|
|
|
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
|
|
|
|
#include "config/LaunchSettings.h"
|
|
|
|
|
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
|
|
|
|
|
|
|
|
|
|
#include "Cafe/CafeSystem.h"
|
|
|
|
|
#include "Cafe/TitleList/TitleList.h"
|
|
|
|
|
#include "Cafe/TitleList/SaveList.h"
|
|
|
|
|
|
|
|
|
|
#include "Common/ExceptionHandler/ExceptionHandler.h"
|
|
|
|
|
|
|
|
|
|
#include <wx/setup.h>
|
|
|
|
|
#include "util/helpers/helpers.h"
|
|
|
|
|
#include "config/ActiveSettings.h"
|
|
|
|
|
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
|
|
|
|
|
|
|
|
|
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
|
|
|
|
#include "Cafe/OS/libs/vpad/vpad.h"
|
|
|
|
|
|
|
|
|
|
#include "audio/IAudioAPI.h"
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
#pragma comment(lib,"Dbghelp.lib")
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define SDL_MAIN_HANDLED
|
|
|
|
|
#include <SDL.h>
|
|
|
|
|
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_LINUX || BOOST_OS_MACOS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
#define _putenv(__s) putenv((char*)(__s))
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-08-28 15:29:15 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
extern "C"
|
|
|
|
|
{
|
|
|
|
|
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|
|
|
|
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
|
|
|
|
}
|
2022-08-28 15:29:15 +02:00
|
|
|
|
#endif
|
2022-08-22 22:21:23 +02:00
|
|
|
|
|
|
|
|
|
bool _cpuExtension_SSSE3 = false;
|
|
|
|
|
bool _cpuExtension_SSE4_1 = false;
|
|
|
|
|
bool _cpuExtension_AVX2 = false;
|
|
|
|
|
|
|
|
|
|
std::atomic_bool g_isGPUInitFinished = false;
|
|
|
|
|
|
|
|
|
|
std::wstring executablePath;
|
|
|
|
|
|
|
|
|
|
void logCPUAndMemoryInfo()
|
|
|
|
|
{
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
int CPUInfo[4] = { -1 };
|
|
|
|
|
unsigned nExIds, i = 0;
|
|
|
|
|
char CPUBrandString[0x40];
|
|
|
|
|
// Get the information associated with each extended ID.
|
2022-09-01 20:46:20 +02:00
|
|
|
|
cpuid(CPUInfo, 0x80000000);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
nExIds = CPUInfo[0];
|
|
|
|
|
for (i = 0x80000000; i <= nExIds; ++i)
|
|
|
|
|
{
|
2022-09-01 20:46:20 +02:00
|
|
|
|
cpuid(CPUInfo, i);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
// Interpret CPU brand string
|
|
|
|
|
if (i == 0x80000002)
|
|
|
|
|
memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
|
|
|
|
|
else if (i == 0x80000003)
|
|
|
|
|
memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
|
|
|
|
|
else if (i == 0x80000004)
|
|
|
|
|
memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
|
|
|
|
|
}
|
|
|
|
|
forceLog_printf("CPU: %s", CPUBrandString);
|
|
|
|
|
|
|
|
|
|
MEMORYSTATUSEX statex;
|
|
|
|
|
statex.dwLength = sizeof(statex);
|
|
|
|
|
GlobalMemoryStatusEx(&statex);
|
|
|
|
|
uint32 memoryInMB = (uint32)(statex.ullTotalPhys / 1024LL / 1024LL);
|
|
|
|
|
forceLog_printf("RAM: %uMB", memoryInMB);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool g_running_in_wine = false;
|
|
|
|
|
bool IsRunningInWine()
|
|
|
|
|
{
|
|
|
|
|
return g_running_in_wine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkForWine()
|
|
|
|
|
{
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
const HMODULE hmodule = GetModuleHandleA("ntdll.dll");
|
|
|
|
|
if (!hmodule)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const auto pwine_get_version = (const char*(__cdecl*)())GetProcAddress(hmodule, "wine_get_version");
|
|
|
|
|
if (pwine_get_version)
|
|
|
|
|
{
|
|
|
|
|
g_running_in_wine = true;
|
|
|
|
|
forceLog_printf("Wine version: %s", pwine_get_version());
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
g_running_in_wine = false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void infoLog_cemuStartup()
|
|
|
|
|
{
|
2022-08-31 12:04:09 +02:00
|
|
|
|
cemuLog_force("------- Init {} -------", BUILD_VERSION_WITH_NAME_STRING);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
cemuLog_force("Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base);
|
|
|
|
|
cemuLog_force(u8"mlc01 path: {}", ActiveSettings::GetMlcPath().generic_u8string());
|
|
|
|
|
// check for wine version
|
|
|
|
|
checkForWine();
|
|
|
|
|
// CPU and RAM info
|
|
|
|
|
logCPUAndMemoryInfo();
|
|
|
|
|
// extensions that Cemu uses
|
|
|
|
|
char cpuExtensionStr[256];
|
|
|
|
|
strcpy(cpuExtensionStr, "");
|
|
|
|
|
if (_cpuExtension_SSSE3)
|
|
|
|
|
{
|
|
|
|
|
strcat(cpuExtensionStr, "SSSE3");
|
|
|
|
|
}
|
|
|
|
|
if (_cpuExtension_SSE4_1)
|
|
|
|
|
{
|
|
|
|
|
if (cpuExtensionStr[0] != '\0')
|
|
|
|
|
strcat(cpuExtensionStr, ", ");
|
|
|
|
|
strcat(cpuExtensionStr, "SSE4.1");
|
|
|
|
|
}
|
|
|
|
|
if (_cpuExtension_AVX2)
|
|
|
|
|
{
|
|
|
|
|
if (cpuExtensionStr[0] != '\0')
|
|
|
|
|
strcat(cpuExtensionStr, ", ");
|
|
|
|
|
strcat(cpuExtensionStr, "AVX2");
|
|
|
|
|
}
|
|
|
|
|
if (AES128_useAESNI())
|
|
|
|
|
{
|
|
|
|
|
if (cpuExtensionStr[0] != '\0')
|
|
|
|
|
strcat(cpuExtensionStr, ", ");
|
|
|
|
|
strcat(cpuExtensionStr, "AES-NI");
|
|
|
|
|
}
|
|
|
|
|
cemuLog_force("Used CPU extensions: {}", cpuExtensionStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// some implementations of _putenv dont copy the string and instead only store a pointer
|
|
|
|
|
// thus we use a helper to keep a permanent copy
|
|
|
|
|
std::vector<std::string*> sPutEnvMap;
|
|
|
|
|
|
|
|
|
|
void _putenvSafe(const char* c)
|
|
|
|
|
{
|
|
|
|
|
auto s = new std::string(c);
|
|
|
|
|
sPutEnvMap.emplace_back(s);
|
|
|
|
|
_putenv(s->c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void reconfigureGLDrivers()
|
|
|
|
|
{
|
|
|
|
|
// reconfigure GL drivers to store
|
2022-10-12 08:03:26 +02:00
|
|
|
|
const fs::path nvCacheDir = ActiveSettings::GetCachePath("shaderCache/driver/nvidia/");
|
2022-08-22 22:21:23 +02:00
|
|
|
|
|
|
|
|
|
std::error_code err;
|
|
|
|
|
fs::create_directories(nvCacheDir, err);
|
|
|
|
|
|
|
|
|
|
std::string nvCacheDirEnvOption("__GL_SHADER_DISK_CACHE_PATH=");
|
2022-09-09 23:48:52 +02:00
|
|
|
|
nvCacheDirEnvOption.append(_pathToUtf8(nvCacheDir));
|
2022-08-22 22:21:23 +02:00
|
|
|
|
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
std::wstring tmpW = boost::nowide::widen(nvCacheDirEnvOption);
|
|
|
|
|
_wputenv(tmpW.c_str());
|
|
|
|
|
#else
|
|
|
|
|
_putenvSafe(nvCacheDirEnvOption.c_str());
|
|
|
|
|
#endif
|
|
|
|
|
_putenvSafe("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void reconfigureVkDrivers()
|
|
|
|
|
{
|
|
|
|
|
_putenvSafe("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1=1");
|
|
|
|
|
_putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mainEmulatorCommonInit()
|
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
// check available CPU extensions
|
|
|
|
|
int cpuInfo[4];
|
2022-09-01 20:46:20 +02:00
|
|
|
|
cpuid(cpuInfo, 0x1);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
_cpuExtension_SSSE3 = ((cpuInfo[2] >> 9) & 1) != 0;
|
|
|
|
|
_cpuExtension_SSE4_1 = ((cpuInfo[2] >> 19) & 1) != 0;
|
|
|
|
|
|
2022-09-01 20:46:20 +02:00
|
|
|
|
cpuidex(cpuInfo, 0x7, 0);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
_cpuExtension_AVX2 = ((cpuInfo[1] >> 5) & 1) != 0;
|
|
|
|
|
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
executablePath.resize(4096);
|
|
|
|
|
int i = GetModuleFileName(NULL, executablePath.data(), executablePath.size());
|
|
|
|
|
if(i >= 0)
|
|
|
|
|
executablePath.resize(i);
|
|
|
|
|
else
|
|
|
|
|
executablePath.clear();
|
|
|
|
|
SetCurrentDirectory(executablePath.c_str());
|
|
|
|
|
|
|
|
|
|
// set high priority
|
|
|
|
|
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
|
|
|
|
|
#endif
|
|
|
|
|
ExceptionHandler_init();
|
|
|
|
|
// read config
|
|
|
|
|
g_config.Load();
|
2022-10-11 04:04:47 +02:00
|
|
|
|
if (NetworkConfig::XMLExists())
|
|
|
|
|
n_config.Load();
|
2022-08-22 22:21:23 +02:00
|
|
|
|
// symbol storage
|
|
|
|
|
rplSymbolStorage_init();
|
|
|
|
|
// static initialization
|
|
|
|
|
IAudioAPI::InitializeStatic();
|
|
|
|
|
// load graphic packs (must happen before config is loaded)
|
2022-09-04 01:27:44 +02:00
|
|
|
|
GraphicPack2::LoadAll();
|
2022-08-22 22:21:23 +02:00
|
|
|
|
// initialize file system
|
|
|
|
|
fsc_init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mainEmulatorLLE();
|
|
|
|
|
void ppcAsmTest();
|
|
|
|
|
void gx2CopySurfaceTest();
|
|
|
|
|
void ExpressionParser_test();
|
|
|
|
|
void FSTVolumeTest();
|
2022-10-17 13:25:49 +02:00
|
|
|
|
void CRCTest();
|
2022-08-22 22:21:23 +02:00
|
|
|
|
|
|
|
|
|
void unitTests()
|
|
|
|
|
{
|
|
|
|
|
ExpressionParser_test();
|
|
|
|
|
gx2CopySurfaceTest();
|
|
|
|
|
ppcAsmTest();
|
|
|
|
|
FSTVolumeTest();
|
2022-10-17 13:25:49 +02:00
|
|
|
|
CRCTest();
|
2022-08-22 22:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int mainEmulatorHLE()
|
|
|
|
|
{
|
|
|
|
|
LatteOverlay_init();
|
|
|
|
|
// run a couple of tests if in non-release mode
|
2022-09-24 08:43:27 +02:00
|
|
|
|
#ifdef CEMU_DEBUG_ASSERT
|
2022-08-22 22:21:23 +02:00
|
|
|
|
unitTests();
|
|
|
|
|
#endif
|
|
|
|
|
// init common
|
|
|
|
|
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
|
2022-10-12 08:03:26 +02:00
|
|
|
|
CafeTitleList::Initialize(ActiveSettings::GetUserDataPath("title_list_cache.xml"));
|
2022-08-22 22:21:23 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isConsoleConnected = false;
|
|
|
|
|
void requireConsole()
|
|
|
|
|
{
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
if (isConsoleConnected)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (AttachConsole(ATTACH_PARENT_PROCESS) != FALSE)
|
|
|
|
|
{
|
|
|
|
|
freopen("CONIN$", "r", stdin);
|
|
|
|
|
freopen("CONOUT$", "w", stdout);
|
|
|
|
|
freopen("CONOUT$", "w", stderr);
|
|
|
|
|
isConsoleConnected = true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandlePostUpdate()
|
|
|
|
|
{
|
|
|
|
|
// finalize update process
|
|
|
|
|
// delete update cemu.exe.backup if available
|
|
|
|
|
const auto filename = ActiveSettings::GetFullPath().replace_extension("exe.backup");
|
|
|
|
|
if (fs::exists(filename))
|
|
|
|
|
{
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
HANDLE lock;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
lock = CreateMutex(nullptr, TRUE, L"Global\\cemu_update_lock");
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
} while (lock == nullptr);
|
|
|
|
|
const DWORD wait_result = WaitForSingleObject(lock, 2000);
|
|
|
|
|
CloseHandle(lock);
|
|
|
|
|
|
|
|
|
|
if (wait_result == WAIT_OBJECT_0)
|
|
|
|
|
{
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::remove(filename, ec);
|
|
|
|
|
}
|
|
|
|
|
#else
|
2022-10-14 12:49:41 +02:00
|
|
|
|
while (fs::exists(filename))
|
2022-08-22 22:21:23 +02:00
|
|
|
|
{
|
|
|
|
|
std::error_code ec;
|
2022-10-14 12:49:41 +02:00
|
|
|
|
fs::remove(filename, ec);
|
2022-08-22 22:21:23 +02:00
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ToolShaderCacheMerger();
|
|
|
|
|
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_WINDOWS
|
2022-08-22 22:21:23 +02:00
|
|
|
|
|
2022-09-24 08:43:27 +02:00
|
|
|
|
// entrypoint for release builds
|
2022-10-14 12:49:41 +02:00
|
|
|
|
int wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd)
|
2022-08-22 22:21:23 +02:00
|
|
|
|
{
|
2022-10-14 12:49:41 +02:00
|
|
|
|
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
|
|
|
|
|
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
|
2022-08-22 22:21:23 +02:00
|
|
|
|
SDL_SetMainReady();
|
2022-09-24 08:43:27 +02:00
|
|
|
|
if (!LaunchSettings::HandleCommandline(lpCmdLine))
|
|
|
|
|
return 0;
|
2022-10-12 08:03:26 +02:00
|
|
|
|
gui_create();
|
|
|
|
|
return 0;
|
2022-08-22 22:21:23 +02:00
|
|
|
|
}
|
2022-09-24 08:43:27 +02:00
|
|
|
|
|
|
|
|
|
// entrypoint for debug builds with console
|
|
|
|
|
int main(int argc, char* argv[])
|
2022-08-22 22:21:23 +02:00
|
|
|
|
{
|
2022-10-14 12:49:41 +02:00
|
|
|
|
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
|
|
|
|
|
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
|
2022-08-22 22:21:23 +02:00
|
|
|
|
SDL_SetMainReady();
|
2022-09-24 08:43:27 +02:00
|
|
|
|
if (!LaunchSettings::HandleCommandline(argc, argv))
|
2022-08-22 22:21:23 +02:00
|
|
|
|
return 0;
|
2022-10-12 08:03:26 +02:00
|
|
|
|
gui_create();
|
|
|
|
|
return 0;
|
2022-08-22 22:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
2022-09-24 08:43:27 +02:00
|
|
|
|
|
2022-08-22 22:21:23 +02:00
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#if BOOST_OS_LINUX
|
2022-08-22 22:21:23 +02:00
|
|
|
|
XInitThreads();
|
2022-08-26 04:03:26 +02:00
|
|
|
|
#endif
|
2022-08-22 22:21:23 +02:00
|
|
|
|
if (!LaunchSettings::HandleCommandline(argc, argv))
|
|
|
|
|
return 0;
|
2022-10-12 08:03:26 +02:00
|
|
|
|
gui_create();
|
|
|
|
|
return 0;
|
2022-08-22 22:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-08-26 19:41:42 +02:00
|
|
|
|
extern "C" DLLEXPORT uint64 gameMeta_getTitleId()
|
2022-08-22 22:21:23 +02:00
|
|
|
|
{
|
|
|
|
|
return CafeSystem::GetForegroundTitleId();
|
2022-08-26 19:41:42 +02:00
|
|
|
|
}
|