Cemu/src/main.cpp
2022-08-26 04:03:26 +02:00

472 lines
11 KiB
C++

#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"
#include "Cafe/GraphicPack/GraphicPack.h"
#include "config/CemuConfig.h"
#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"
#if BOOST_OS_WINDOWS
#pragma comment(lib,"Dbghelp.lib")
#endif
#define SDL_MAIN_HANDLED
#include <SDL.h>
#if BOOST_OS_LINUX || BOOST_OS_MACOS
#define _putenv(__s) putenv((char*)(__s))
#endif
extern "C"
{
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}
bool _cpuExtension_SSSE3 = false;
bool _cpuExtension_SSE4_1 = false;
bool _cpuExtension_AVX2 = false;
std::atomic_bool g_isGPUInitFinished = false;
std::wstring executablePath;
bool g_cemuhook_loaded = false;
bool IsCemuhookLoaded()
{
return g_cemuhook_loaded;
}
void checkForCemuhook()
{
#if BOOST_OS_WINDOWS
// check if there is a dbghelp.dll in the current working directory
if (!fs::exists(ActiveSettings::GetPath("cemuhook.dll")))
return;
// check if Cemuhook can be detected
DWORD verHandle;
DWORD verLen = GetFileVersionInfoSizeW(L"cemuhook.dll", &verHandle);
if (verLen == 0)
return;
uint8* verData = (uint8*)malloc(verLen);
GetFileVersionInfoW(L"cemuhook.dll", 0, verLen, verData);
// get version
LPVOID lpBuffer;
UINT size;
if (VerQueryValueW(verData, L"\\", (LPVOID*)&lpBuffer, (PUINT)&size))
{
if (size)
{
VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer;
if (verInfo->dwSignature == 0xfeef04bd)
{
forceLog_printf("Cemuhook version: %d.%d.%d.%d", (verInfo->dwFileVersionMS >> 16) & 0xFFFF, (verInfo->dwFileVersionMS >> 0) & 0xFFFF, (verInfo->dwFileVersionLS >> 16) & 0xFFFF, (verInfo->dwFileVersionLS >> 0) & 0xFFFF);
g_cemuhook_loaded = true;
}
}
}
free(verData);
#endif
}
void logCPUAndMemoryInfo()
{
#if BOOST_OS_WINDOWS
int CPUInfo[4] = { -1 };
unsigned nExIds, i = 0;
char CPUBrandString[0x40];
// Get the information associated with each extended ID.
__cpuid(CPUInfo, 0x80000000);
nExIds = CPUInfo[0];
for (i = 0x80000000; i <= nExIds; ++i)
{
__cpuid(CPUInfo, i);
// 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()
{
#if BOOST_OS_WINDOWS
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()
{
cemuLog_force("------- Init {} {}.{}{} -------", EMULATOR_NAME, EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX);
cemuLog_force("Init Wii U memory space (base: 0x{:016x})", (size_t)memory_base);
cemuLog_force(u8"mlc01 path: {}", ActiveSettings::GetMlcPath().generic_u8string());
// check if Cemuhook is installed
checkForCemuhook();
// 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
const fs::path nvCacheDir = ActiveSettings::GetPath("shaderCache/driver/nvidia/");
std::error_code err;
fs::create_directories(nvCacheDir, err);
std::string nvCacheDirEnvOption("__GL_SHADER_DISK_CACHE_PATH=");
nvCacheDirEnvOption.append(_utf8Wrapper(nvCacheDir));
#if BOOST_OS_WINDOWS
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];
__cpuid(cpuInfo, 0x1);
_cpuExtension_SSSE3 = ((cpuInfo[2] >> 9) & 1) != 0;
_cpuExtension_SSE4_1 = ((cpuInfo[2] >> 19) & 1) != 0;
__cpuidex(cpuInfo, 0x7, 0);
_cpuExtension_AVX2 = ((cpuInfo[1] >> 5) & 1) != 0;
#if BOOST_OS_WINDOWS
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();
// symbol storage
rplSymbolStorage_init();
// static initialization
IAudioAPI::InitializeStatic();
// load graphic packs (must happen before config is loaded)
graphicPack_loadAll();
// initialize file system
fsc_init();
}
void mainEmulatorLLE();
void ppcAsmTest();
void gx2CopySurfaceTest();
void ExpressionParser_test();
void FSTVolumeTest();
void unitTests()
{
ExpressionParser_test();
gx2CopySurfaceTest();
ppcAsmTest();
FSTVolumeTest();
}
int mainEmulatorHLE()
{
if (!TestWriteAccess(ActiveSettings::GetPath()))
wxMessageBox("Cemu doesn't have write access to it's own directory.\nPlease move it to a different location or run Cemu as administrator!", "Warning", wxOK|wxICON_ERROR); // todo - different error messages per OS
LatteOverlay_init();
// run a couple of tests if in non-release mode
#ifndef PUBLIC_RELEASE
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
CafeTitleList::Initialize(ActiveSettings::GetPath("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();
}
// Create UI
gui_create();
return 0;
}
bool isConsoleConnected = false;
void requireConsole()
{
#if BOOST_OS_WINDOWS
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))
{
#if BOOST_OS_WINDOWS
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
while( fs::exists(filename) )
{
std::error_code ec;
fs::remove(filename, ec);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
#endif
}
}
void ToolShaderCacheMerger();
#if BOOST_OS_WINDOWS
#ifndef PUBLIC_RELEASE
#include <crtdbg.h>
int wmain(int argc, wchar_t* argv[])
{
SDL_SetMainReady();
_CrtSetDbgFlag(_CRTDBG_CHECK_DEFAULT_DF);
//ToolShaderCacheMerger();
if (!LaunchSettings::HandleCommandline(argc, argv))
return 0;
ActiveSettings::LoadOnce();
HandlePostUpdate();
return mainEmulatorHLE();
}
#else
int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd)
{
SDL_SetMainReady();
if (!LaunchSettings::HandleCommandline(lpCmdLine))
return 0;
ActiveSettings::LoadOnce();
HandlePostUpdate();
return mainEmulatorHLE();
}
#endif
#else
int main(int argc, char *argv[])
{
#if BOOST_OS_LINUX
XInitThreads();
#endif
if (!LaunchSettings::HandleCommandline(argc, argv))
return 0;
ActiveSettings::LoadOnce();
HandlePostUpdate();
return mainEmulatorHLE();
}
#endif
/* Cemuhook legacy API */
#pragma optimize("",off)
__declspec(dllexport) void gameMeta_loadForCurrent()
{
int placeholderA = 0x11223344;
int placeholderB = 0x55667788;
}
#pragma optimize("",on)
__declspec(dllexport) uint64 gameMeta_getTitleId()
{
return CafeSystem::GetForegroundTitleId();
}
/* Cemuhook loading */
#if BOOST_OS_WINDOWS
#pragma init_seg(".CRT$XCT")
HANDLE dbgLib;
int dbghelp_init(void)
{
// load dbghelp.dll from the system folder instead of loading outdated cemuhook via dbghelp.dll
WCHAR dllPath[MAX_PATH];
GetSystemDirectoryW(dllPath, MAX_PATH);
wcscat_s(dllPath, sizeof(dllPath) / sizeof(WCHAR), TEXT("\\dbghelp.dll"));
dbgLib = LoadLibraryW(dllPath);
if (dbgLib == NULL)
return -1;
return 0;
}
HMODULE _earlyInitFunction()
{
dbghelp_init();
return LoadLibraryA("cemuhook2.dll");
}
HMODULE _cemuHookDllHandle = _earlyInitFunction();
#endif