/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** * * @modified Tom Clay, 2026 - Adapted for ReXGlue runtime */ #include #include #include #include #include #include #include #include #include #include REXCVAR_DEFINE_BOOL(win32_high_resolution_timer, true, "Win32", "Requests high-resolution timer from the NT kernel"); REXCVAR_DEFINE_BOOL(win32_mmcss, true, "Win32", "Opt in the Multimedia Class Scheduler Service (MMCSS) scheduling for " "prioritized access to CPU resources"); namespace { void RequestWin32HighResolutionTimer() { HMODULE ntdll_module = GetModuleHandleW(L"ntdll.dll"); if (!ntdll_module) { return; } // Xenia requests the maximum NT timer resolution directly rather than using // timeBeginPeriod(1), which is coarser and may override this setting in SDL. using NtQueryTimerResolutionFn = LONG(NTAPI*)(PULONG minimum_resolution, PULONG maximum_resolution, PULONG current_resolution); using NtSetTimerResolutionFn = LONG(NTAPI*)(ULONG desired_resolution, BOOLEAN set_resolution, PULONG current_resolution); auto nt_query_timer_resolution = reinterpret_cast( GetProcAddress(ntdll_module, "NtQueryTimerResolution")); auto nt_set_timer_resolution = reinterpret_cast(GetProcAddress(ntdll_module, "NtSetTimerResolution")); if (!nt_query_timer_resolution || !nt_set_timer_resolution) { return; } ULONG minimum_resolution = 0; ULONG maximum_resolution = 0; ULONG current_resolution = 0; nt_query_timer_resolution(&minimum_resolution, &maximum_resolution, ¤t_resolution); nt_set_timer_resolution(maximum_resolution, TRUE, ¤t_resolution); } void RequestWin32MMCSS() { HMODULE dwmapi_module = LoadLibraryW(L"dwmapi.dll"); if (!dwmapi_module) { return; } using DwmEnableMMCSSFn = HRESULT(STDAPICALLTYPE*)(BOOL enable_mmcss); auto dwm_enable_mmcss = reinterpret_cast(GetProcAddress(dwmapi_module, "DwmEnableMMCSS")); if (dwm_enable_mmcss) { dwm_enable_mmcss(TRUE); } FreeLibrary(dwmapi_module); } // Convert wide argv from CommandLineToArgvW to UTF-8 argc/argv for cvar::Init std::vector WideArgsToUtf8(int argc, wchar_t** wargv) { std::vector args; args.reserve(static_cast(argc)); for (int i = 0; i < argc; ++i) { std::wstring wide(wargv[i]); if (wide.empty()) { args.emplace_back(); continue; } int size = WideCharToMultiByte(CP_UTF8, 0, wide.data(), static_cast(wide.size()), nullptr, 0, nullptr, nullptr); std::string utf8(static_cast(size), '\0'); WideCharToMultiByte(CP_UTF8, 0, wide.data(), static_cast(wide.size()), utf8.data(), size, nullptr, nullptr); args.push_back(std::move(utf8)); } return args; } } // namespace int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hinstance_prev, LPWSTR command_line, int show_cmd) { (void)hinstance_prev; (void)command_line; // Convert wide command line to UTF-8 argc/argv and parse CVARs int wargc = 0; wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); auto utf8_args = WideArgsToUtf8(wargc, wargv); LocalFree(wargv); // Build char* argv for cvar::Init std::vector argv_ptrs; argv_ptrs.reserve(utf8_args.size()); for (auto& s : utf8_args) { argv_ptrs.push_back(s.data()); } auto remaining = rex::cvar::Init(static_cast(argv_ptrs.size()), argv_ptrs.data()); rex::cvar::ApplyEnvironment(); rex::InitLoggingEarly(); // Mirrors Xenia's InitializeWin32App timing setup: parse cvars first, then // request high-performance timing and compositor scheduling before windows // and render/audio/input subsystems start. if (REXCVAR_GET(win32_high_resolution_timer)) { RequestWin32HighResolutionTimer(); } if (REXCVAR_GET(win32_mmcss)) { RequestWin32MMCSS(); } int result; { rex::ui::Win32WindowedAppContext app_context(hinstance, show_cmd); // TODO(Triang3l): Initialize creates a window. Set DPI awareness via the // manifest. if (!app_context.Initialize()) { return EXIT_FAILURE; } std::unique_ptr app = rex::ui::GetWindowedAppCreator()(app_context); // Match remaining positional args to app's expected options const auto& option_names = app->GetPositionalOptions(); std::map parsed; size_t count = std::min(remaining.size(), option_names.size()); for (size_t i = 0; i < count; ++i) { parsed[option_names[i]] = remaining[i]; } app->SetParsedArguments(std::move(parsed)); // Initialize COM on the UI thread with the apartment-threaded concurrency // model, so dialogs can be used. if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) { return EXIT_FAILURE; } result = app->OnInitialize() ? app_context.RunMainMessageLoop() : EXIT_FAILURE; app->InvokeOnDestroy(); } // TODO: Port ShutdownWin32App from Xenia // Logging may still be needed in the destructors. // rex::ShutdownWin32App(); CoUninitialize(); return result; }