diff --git a/assets/launcher-ui/src/modules/gameClass.ts b/assets/launcher-ui/src/modules/gameClass.ts new file mode 100644 index 0000000..7f7ea95 --- /dev/null +++ b/assets/launcher-ui/src/modules/gameClass.ts @@ -0,0 +1,73 @@ +import { ref, type Ref } from "vue"; +import { VersionState } from "./launcher"; + +export class GameClass { + public meta: Ref = ref(undefined); + public versionState: Ref = ref(VersionState.Unknown); + + get installed() { + return this.meta.value?.installed ?? false; + } + + async updateMeta() { + this.meta.value = await window.laochan.detectGameInstall(0); + this.versionState.value = this.checkVersion(); + } + + get installPath() { + if (!this.installed) { + return; + } + return this.meta.value!.install_path; + } + + get configPath() { + const installPath = this.installPath; + if (!installPath) { + return; + } + + return installPath + 'laochan-config.json'; + } + + checkVersion(): VersionState { + if (!this.installed) { + return VersionState.Unknown; + } + + const targetVersion = this.meta.value!.game_module_target_version; + + if (targetVersion == 'ANY') + return VersionState.Normal; + + const installVersion = this.meta.value!.game_module_version; + const installVersionNum = Number.parseInt(installVersion.split(":")[4]); + const targetVersionNum = Number.parseInt(targetVersion.split(":")[4]); + + if (installVersionNum > targetVersionNum) { + return VersionState.Need2UpdateLauncher; + } + + if (installVersionNum < targetVersionNum) { + return VersionState.Need2UpdateGame; + } + + return VersionState.Normal + } + + async settings() { + if (!this.installed) { + return; + } + + window.laochan.shellExecute(this.meta.value!.settings_module_path); + } + + async updater() { + if (!this.installed) { + return; + } + + window.laochan.shellExecute(this.meta.value!.updater_module_path); + } +} diff --git a/assets/launcher-ui/src/modules/gitadora.ts b/assets/launcher-ui/src/modules/gitadora.ts index a4dd16b..871c0de 100644 --- a/assets/launcher-ui/src/modules/gitadora.ts +++ b/assets/launcher-ui/src/modules/gitadora.ts @@ -2,64 +2,19 @@ import { ref, type Ref } from "vue"; import { launcher, VersionState } from "./launcher"; import type { RefSymbol } from "@vue/reactivity"; import { faL } from "@fortawesome/free-solid-svg-icons"; +import { GameClass } from "./gameClass"; export interface GITADORAConfig { } -export class GITADORA { +export class GITADORA extends GameClass { private _config: Ref = ref(undefined); private _dirty: boolean = false; - public GameMeta: Ref = ref(undefined); - public GameVersionState: Ref = ref(VersionState.Unknown); - + get config() { return this._config; } - installed() { - return this.GameMeta.value?.installed ?? false; - } - - async UpdateMeta() { - this.GameMeta.value = await window.laochan.detectGameInstall(2); - this.GameVersionState.value = this.checkVersion(); - } - - get installPath() { - if (!this.installed()) { - return; - } - return this.GameMeta.value!.install_path; - } - - get configPath() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - return installPath + 'laochan-config.json'; - } - - checkVersion(): VersionState { - if (!this.installed()) { - return VersionState.Unknown; - } - const installVersion = this.GameMeta.value!.game_module_version; - const targetVersion = this.GameMeta.value!.game_module_target_version; - const installVersionNum = Number.parseInt(installVersion.split(":")[4]); - const targetVersionNum = Number.parseInt(targetVersion.split(":")[4]); - if (installVersionNum > targetVersionNum) { - return VersionState.Need2UpdateLauncher; - } - else if (installVersionNum < targetVersionNum) { - return VersionState.Need2UpdateGame; - } - else { - return VersionState.Normal - } - } - async resetConfig() { this._config.value = {} this._dirty = true; @@ -113,24 +68,6 @@ export class GITADORA { window.laochan.close(); } - - async settings() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\settings.exe'); - } - - async updater() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\updater.exe', '-t DUMMY'); - } }; export const gitadora = new GITADORA(); diff --git a/assets/launcher-ui/src/modules/iidx.ts b/assets/launcher-ui/src/modules/iidx.ts index e30bbff..ccdca5f 100644 --- a/assets/launcher-ui/src/modules/iidx.ts +++ b/assets/launcher-ui/src/modules/iidx.ts @@ -1,7 +1,7 @@ import { ref, type Ref } from "vue"; -import { launcher, VersionState } from "./launcher"; import dedent from "dedent"; -import type { TupleType } from "typescript"; +import { GameClass } from "./gameClass"; +import { launcher } from "./launcher"; export enum GraphicsAPI { Native = 0, @@ -39,60 +39,14 @@ export interface IIDXConfig { graphicsAPI: GraphicsAPI; } -export class IIDX { +export class IIDX extends GameClass { private _config: Ref = ref(undefined); private _dirty: boolean = false; - public GameMeta: Ref = ref(undefined); - public GameVersionState: Ref = ref(VersionState.Unknown); get config() { return this._config; } - installed() { - return this.GameMeta.value?.installed ?? false; - } - - async UpdateMeta() { - this.GameMeta.value = await window.laochan.detectGameInstall(0); - this.GameVersionState.value = this.checkVersion(); - } - - get installPath() { - if (!this.installed()) { - return; - } - return this.GameMeta.value!.install_path; - } - - get configPath() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - return installPath + 'laochan-config.json'; - } - - checkVersion(): VersionState { - if (!this.installed()) { - return VersionState.Unknown; - } - const installVersion = this.GameMeta.value!.game_module_version; - const targetVersion = this.GameMeta.value!.game_module_target_version; - const installVersionNum = Number.parseInt(installVersion.split(":")[4]); - const targetVersionNum = Number.parseInt(targetVersion.split(":")[4]); - if (installVersionNum > targetVersionNum) { - return VersionState.Need2UpdateLauncher; - } - else if (installVersionNum < targetVersionNum) { - return VersionState.Need2UpdateGame; - } - else { - return VersionState.Normal - } - } - async resetConfig() { const { devices } = await window.laochan.getAsioDeviceList(); @@ -165,7 +119,7 @@ export class IIDX { window.laochan.setParam('IIDX_RESOLTION_W', JSON.stringify(config.resolution.w)), window.laochan.setParam('IIDX_RESOLTION_H', JSON.stringify(config.resolution.h)), - window.laochan.setParam('IIDX_GRAPHICS_API',JSON.stringify(config.graphicsAPI)), + window.laochan.setParam('IIDX_GRAPHICS_API', JSON.stringify(config.graphicsAPI)), ]); } @@ -183,24 +137,6 @@ export class IIDX { window.laochan.close(); } - async settings() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\bm2dx_settings.exe'); - } - - async updater() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\bm2dx_updater.exe'); - } - async generateBat() { const config = this._config.value; if (!config) { diff --git a/assets/launcher-ui/src/modules/sdvx.ts b/assets/launcher-ui/src/modules/sdvx.ts index 6f89e31..b2a9d38 100644 --- a/assets/launcher-ui/src/modules/sdvx.ts +++ b/assets/launcher-ui/src/modules/sdvx.ts @@ -1,63 +1,18 @@ import { ref, type Ref } from "vue"; import { launcher, VersionState } from "./launcher"; +import { GameClass } from "./gameClass"; export interface SDVXConfig { } -export class SDVX { +export class SDVX extends GameClass { private _config: Ref = ref(undefined); private _dirty: boolean = false; - public GameMeta: Ref = ref(undefined); - public GameVersionState: Ref = ref(VersionState.Unknown); - + get config() { return this._config; } - installed() { - return this.GameMeta.value?.installed??false; - } - - async UpdateMeta() { - this.GameMeta.value = await window.laochan.detectGameInstall(1); - this.GameVersionState.value = this.checkVersion(); - } - - get installPath() { - if (!this.installed()) { - return; - } - return this.GameMeta.value!.install_path; - } - - get configPath() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - return installPath + 'laochan-config.json'; - } - - checkVersion(): VersionState { - if (!this.installed()) { - return VersionState.Unknown; - } - const installVersion = this.GameMeta.value!.game_module_version; - const targetVersion = this.GameMeta.value!.game_module_target_version; - const installVersionNum = Number.parseInt(installVersion.split(":")[4]); - const targetVersionNum = Number.parseInt(targetVersion.split(":")[4]); - if (installVersionNum > targetVersionNum) { - return VersionState.Need2UpdateLauncher; - } - else if (installVersionNum < targetVersionNum) { - return VersionState.Need2UpdateGame; - } - else { - return VersionState.Normal - } - } - async resetConfig() { this._config.value = {} this._dirty = true; @@ -111,24 +66,6 @@ export class SDVX { window.laochan.close(); } - - async settings() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\settings.exe'); - } - - async updater() { - const installPath = this.installPath; - if (!installPath) { - return; - } - - window.laochan.shellExecute(installPath + '\\launcher\\modules\\updater.exe', '-t DUMMY'); - } }; export const sdvx = new SDVX(); diff --git a/src/client/component/exception.cpp b/src/client/component/exception.cpp index 0920b67..44c5b31 100644 --- a/src/client/component/exception.cpp +++ b/src/client/component/exception.cpp @@ -55,7 +55,7 @@ namespace exception utils::thread::suspend_other_threads(); show_mouse_cursor(); - MessageBoxA(nullptr, error_str.data(), "Laochan Eacnet Infinitas ERROR", MB_ICONERROR); + MessageBoxA(nullptr, error_str.data(), "Laochen Eacnet ERROR", MB_ICONERROR); TerminateProcess(GetCurrentProcess(), exception_data.code); } @@ -83,37 +83,11 @@ namespace exception return timestamp; } - std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo) - { - std::string info{}; - const auto line = [&info](const std::string& text) - { - info.append(text); - info.append("\r\n"); - }; - - line("Laochen Eacnet Infinitas Crash Dump"); - line(""); - line("Timestamp: "s + get_timestamp()); - line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode)); - line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress)); - -#pragma warning(push) -#pragma warning(disable: 4996) - OSVERSIONINFOEXA version_info; - ZeroMemory(&version_info, sizeof(version_info)); - version_info.dwOSVersionInfoSize = sizeof(version_info); - GetVersionExA(reinterpret_cast(&version_info)); -#pragma warning(pop) - - line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion)); - - return info; - } - void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo) { - const std::string crash_name = utils::string::va("bm2dx-crash-%s.dmp", get_timestamp().data()); + auto game_name = game::environment::get_string(); + + const std::string crash_name = utils::string::va("%s-crash-%s.dmp", game_name.data(), get_timestamp().data()); utils::io::write_file(crash_name, create_minidump(exceptioninfo)); } diff --git a/src/client/component/iidx/custom_resolution.cpp b/src/client/component/iidx/custom_resolution.cpp index 9283ceb..0709882 100644 --- a/src/client/component/iidx/custom_resolution.cpp +++ b/src/client/component/iidx/custom_resolution.cpp @@ -5,10 +5,24 @@ namespace iidx::custom_resolution { - int mode() + enum IIDX_DISPLAY_MODE_ + { + IIDX_DISPLAY_MODE_FULLSCREEN, + IIDX_DISPLAY_MODE_FULLSCREEN_WINDOWED, + IIDX_DISPLAY_MODE_WINDOWED, + }; + + enum IIDX_GRAPHICS_API_ + { + IIDX_GRAPHICS_API_DX9, + IIDX_GRAPHICS_API_DX9_ON_12, + IIDX_GRAPHICS_API_DXVK, + }; + + IIDX_DISPLAY_MODE_ mode() { static auto mode = std::stoi(game::environment::get_param("IIDX_DISPLAY_MODE")); - return mode; + return static_cast(mode); } int width() @@ -28,9 +42,10 @@ namespace iidx::custom_resolution return h; } - int graphicsAPI() { + IIDX_GRAPHICS_API_ graphics_api() + { static auto api = std::stoi(game::environment::get_param("IIDX_GRAPHICS_API")); - return api; + return static_cast(api); } namespace @@ -38,52 +53,60 @@ namespace iidx::custom_resolution HRESULT WINAPI create_d3d9ex(UINT SDKVersion, IDirect3D9Ex** ppD3D9Ex) { IDirect3D9Ex* d3d9ex = nullptr; - HRESULT hr = 0; - switch (graphicsAPI()) - { - case 0: - { - hr = Direct3DCreate9Ex(SDKVersion, &d3d9ex); - break; - } - case 1: + auto hr = E_FAIL; + const auto api = graphics_api(); + + switch (api) { - ID3D12Device* device = nullptr; - auto d3d12hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_1, __uuidof(ID3D12Device), (void**)&device); - if (!SUCCEEDED(d3d12hr)) + case IIDX_GRAPHICS_API_DX9: { - return d3d12hr; + hr = Direct3DCreate9Ex(SDKVersion, &d3d9ex); + break; } - _D3D9ON12_ARGS arg; - arg.Enable9On12 = TRUE; - arg.pD3D12Device = device; - arg.NumQueues = 0; - hr = Direct3DCreate9On12Ex(SDKVersion, &arg, 1, &d3d9ex); - break; - } - case 2: - { - auto dxvk = LoadLibraryW(L"dxvk.dll"); - if (!dxvk) + case IIDX_GRAPHICS_API_DX9_ON_12: + { + void* device = nullptr; + hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_9_3, __uuidof(ID3D12Device), &device); + if (!SUCCEEDED(hr)) + break; + + D3D9ON12_ARGS arg + { + .Enable9On12 = true, + .pD3D12Device = reinterpret_cast(device), + .NumQueues = 0, + }; + + hr = Direct3DCreate9On12Ex(SDKVersion, &arg, 1, &d3d9ex); + break; + } + case IIDX_GRAPHICS_API_DXVK: { - return -1; + auto dxvk = utils::nt::library{ "dxvk.dll" }; + if (!dxvk) + break; + + hr = dxvk.invoke_pascal("Direct3DCreate9Ex", SDKVersion, &d3d9ex); + break; } - auto dxvkDirect3DCreate9Ex = GetProcAddress(dxvk, "Direct3DCreate9Ex"); - hr = ((HRESULT(*)(UINT SDKVersion, IDirect3D9Ex * *ppD3D9Ex))dxvkDirect3DCreate9Ex)(SDKVersion, &d3d9ex); - break; } - default: + + if (FAILED(hr)) { + printf("W: Failed to initialize graphics api with mode %d, falling back to d3d9, hr = 0x%x.\n", api, hr); hr = Direct3DCreate9Ex(SDKVersion, &d3d9ex); - break; - } } + if (SUCCEEDED(hr)) { + if (mode() == IIDX_DISPLAY_MODE_FULLSCREEN) + DwmEnableMMCSS(TRUE); + *ppD3D9Ex = new d3d9ex_proxy(d3d9ex); } else { + printf("E: Failed to initialize graphics api with hr = 0x%x.\n", hr); *ppD3D9Ex = nullptr; } @@ -102,7 +125,7 @@ namespace iidx::custom_resolution HWND WINAPI create_window_ex_a(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) { - if (mode() == 2) + if (mode() == IIDX_DISPLAY_MODE_WINDOWED) { dwStyle = WS_SYSMENU | WS_CAPTION | WS_VISIBLE; RECT rc; @@ -123,7 +146,7 @@ namespace iidx::custom_resolution BOOL WINAPI set_window_pos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) { - if (mode() == 2) + if (mode() == IIDX_DISPLAY_MODE_WINDOWED) { RECT rc; SetRect(&rc, 0, 0, width(), height()); diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index 59cf754..15bb4bf 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -81,17 +81,17 @@ namespace game { switch (game_type) { - case launcher::game::iidx: - return "beatmania IIDX INFINITAS"; + case launcher::game::iidx: + return "beatmania IIDX INFINITAS"; - case launcher::game::sdvx: - return "SOUND VOLTEX EXCEED GEAR"; + case launcher::game::sdvx: + return "SOUND VOLTEX EXCEED GEAR"; - case launcher::game::gitadora: - return "GITADORA"; + case launcher::game::gitadora: + return "GITADORA"; - default: - return "Unknown (" + std::to_string(static_cast(game_type)) + ")"; + default: + return "Unknown (" + std::to_string(static_cast(game_type)) + ")"; } } @@ -99,17 +99,17 @@ namespace game { switch (game_type) { - case launcher::game::iidx: - return L"beatmania IIDX INFINITAS"; + case launcher::game::iidx: + return L"beatmania IIDX INFINITAS"; - case launcher::game::sdvx: - return L"SOUND VOLTEX EXCEED GEAR"; + case launcher::game::sdvx: + return L"SOUND VOLTEX EXCEED GEAR"; - case launcher::game::gitadora: - return L"GITADORA"; + case launcher::game::gitadora: + return L"GITADORA"; - default: - return L"Unknown (" + std::to_wstring(static_cast(game_type)) + L")"; + default: + return L"Unknown (" + std::to_wstring(static_cast(game_type)) + L")"; } } @@ -121,46 +121,45 @@ namespace game return version; } - //https://learn.microsoft.com/zh-cn/archive/msdn-magazine/2017/may/c-use-modern-c-to-access-the-windows-registry - std::wstring RegGetString(HKEY hKey, const std::wstring& subKey, - const std::wstring& value) { - // Retrieve the size of the string value data - DWORD dataSize{}; - auto m_errorCode = ::RegGetValueW(hKey, subKey.c_str(), value.c_str(), - RRF_RT_REG_SZ, nullptr, nullptr, &dataSize); - - // Check if the value information retrieval was successful - if (m_errorCode != ERROR_SUCCESS) { - throw std::exception("Cannot read string from registry", m_errorCode); + static std::wstring get_reg_value(launcher::game game_type, std::wstring const& query) + { + HKEY key; + auto path = L"SOFTWARE\\KONAMI\\" + get_stringw(); + + if (RegOpenKeyExW(HKEY_CURRENT_USER, path.data(), 0, KEY_READ | KEY_WOW64_64KEY, &key) + && RegOpenKeyExW(HKEY_LOCAL_MACHINE, path.data(), 0, KEY_READ | KEY_WOW64_64KEY, &key)) + { + throw std::exception("Game is not installed properly (key is not found)"); } - // Use smart pointers for automatic memory management - std::vector data(dataSize / sizeof(wchar_t)); + DWORD type, size; - // Retrieve the actual string value data - m_errorCode = ::RegGetValueW(hKey, subKey.c_str(), value.c_str(), - RRF_RT_REG_SZ, nullptr, data.data(), &dataSize); + if (RegQueryValueExW(key, query.data(), 0, &type, 0, &size)) + { + throw std::exception("Game is not installed properly (failed to get key value size)"); + } - // Check if the value data retrieval was successful - if (m_errorCode != ERROR_SUCCESS) { - throw std::exception("Cannot read string from registry", m_errorCode); + if (type != 1) + { + throw std::exception("Game is not installed properly (illegal key value type)"); } - // Resize the vector based on the actual size of the string - data.resize(dataSize / sizeof(wchar_t) - 1); + std::wstring value; + value.resize(size - 1); - // Convert the wchar_t vector to a wstring and return - return std::wstring(data.begin(), data.end()); - } + if (RegQueryValueExW(key, query.data(), 0, &type, reinterpret_cast(value.data()), &size)) + { + RegCloseKey(key); + throw std::exception("failed to read key value"); + } + + RegCloseKey(key); - static std::wstring get_reg_value(launcher::game game_type, std::wstring const& query) - { - auto path = L"SOFTWARE\\KONAMI\\" + get_stringw(game_type); - auto value = RegGetString(HKEY_LOCAL_MACHINE, path, query); return value; } - std::wstring get_reg_value(std::wstring const& query) { + std::wstring get_reg_value(std::wstring const& query) + { return get_reg_value(get_game(), query); } @@ -184,51 +183,60 @@ namespace game return get_reg_value(game_type, L"ResourceDir"); } - void gamemeta::ensure_created(launcher::game gametype) { if (inited) - { return; - } + this->game_type = gametype; + try { install_path = game::environment::get_install_path(gametype); resource_path = game::environment::get_resource_path(gametype); switch (gametype) { - case launcher::game::iidx: - { - game_module_path = install_path / L"game" / L"app" / L"bm2dx.exe"; - settings_module_path = install_path / L"launcher" / L"modules" / L"bm2dx_settings.exe"; - updater_module_path = install_path / L"launcher" / L"modules" / L"bm2dx_updater.exe"; - game_module_target_version = IIDX_TARGET_VERSIONW; - break; - } - case launcher::game::sdvx: - { - game_module_path = install_path / L"game" / L"modules" / L"sv6c.exe"; - settings_module_path = install_path / L"launcher" / L"modules" / L"settings.exe"; - updater_module_path = install_path / L"launcher" / L"modules" / L"updater.exe"; - game_module_target_version = SDVX_TARGET_VERSIONW; - break; - } - case launcher::game::gitadora: - { - game_module_path = install_path / L"game" / L"modules" / L"gitadora.exe"; - settings_module_path = install_path / L"launcher" / L"modules" / L"settings.exe"; - updater_module_path = install_path / L"launcher" / L"modules" / L"updater.exe"; - game_module_target_version = GITADORA_TARGET_VERSIONW; - break; - } - default: - { - throw std::exception("unexcepted gametype"); - break; - } + case launcher::game::iidx: + { + if (std::filesystem::exists("bm2dx.exe")) + game_module_path = std::filesystem::absolute("bm2dx.exe"); + else + game_module_path = install_path / "game" / "app" / "bm2dx.exe"; + + updater_module_path = install_path / "launcher" / "modules" / "bm2dx_updater.exe"; + game_module_target_version = IIDX_TARGET_VERSION; + break; + } + case launcher::game::sdvx: + { + if (std::filesystem::exists("sv6c.exe")) + game_module_path = std::filesystem::absolute("sv6c.exe"); + else + game_module_path = install_path / "game" / "modules" / "sv6c.exe"; + + updater_module_path = install_path / "launcher" / "modules" / "updater.exe"; + game_module_target_version = SDVX_TARGET_VERSION; + break; + } + case launcher::game::gitadora: + { + if (std::filesystem::exists("gitadora.exe")) + game_module_path = std::filesystem::absolute("gitadora.exe"); + else + game_module_path = install_path / "game" / "modules" / "gitadora.exe"; + + updater_module_path = install_path / "launcher" / "modules" / "updater.exe"; + game_module_target_version = GITADORA_TARGET_VERSION; + break; + } + default: + { + throw std::exception("unexcepted gametype"); + break; + } } - game_module_version = utils::nt::library::get_version(game_module_path.wstring()); + + game_module_version = utils::nt::library::get_version(game_module_path); installed = true; } catch (const std::exception) @@ -238,45 +246,53 @@ namespace game inited = true; } - static gamemeta gamemetas[(unsigned long long)launcher::game::count]; + static gamemeta gamemetas[static_cast(launcher::game::count)]; gamemeta gamemeta::get_gamemeta(launcher::game game_type) { - gamemeta& gamemeta = gamemetas[(unsigned long long)game_type]; + gamemeta& gamemeta = gamemetas[static_cast(game_type)]; gamemeta.ensure_created(game_type); return gamemeta; } + bool gamemeta::get_install_state() { return installed; } + std::wstring gamemeta::get_install_path() { - return install_path.wstring(); + return install_path.generic_wstring(); } + std::wstring gamemeta::get_resource_path() { - return resource_path.wstring(); + return resource_path.generic_wstring(); } + std::wstring gamemeta::get_game_module_path() { - return game_module_path.wstring(); + return game_module_path.generic_wstring(); } + std::wstring gamemeta::get_settings_module_path() { - return settings_module_path.wstring(); + return settings_module_path.generic_wstring(); } + std::wstring gamemeta::get_updater_module_path() { - return updater_module_path.wstring(); + return updater_module_path.generic_wstring(); } - std::wstring gamemeta::get_game_module_version() + + std::string gamemeta::get_game_module_version() { return game_module_version; } - std::wstring gamemeta::get_game_module_target_version() + + std::string gamemeta::get_game_module_target_version() { return game_module_target_version; } -} + } } diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp index 6b1831d..900f2ac 100644 --- a/src/client/game/game.hpp +++ b/src/client/game/game.hpp @@ -6,12 +6,8 @@ #include #define IIDX_TARGET_VERSION "P2D:J:B:A:2024080500" -#define SDVX_TARGET_VERSION "QCV:J:B:A:2024080700" +#define SDVX_TARGET_VERSION "ANY" #define GITADORA_TARGET_VERSION "U32:J:A:A:2024021300" -#define IIDX_TARGET_VERSIONW L"P2D:J:B:A:2024080500" -#define SDVX_TARGET_VERSIONW L"QCV:J:B:A:2024080700" -#define GITADORA_TARGET_VERSIONW L"U32:J:A:A:2024021300" - namespace game { @@ -46,13 +42,16 @@ namespace game { public: bool get_install_state(); + std::wstring get_install_path(); std::wstring get_resource_path(); std::wstring get_game_module_path(); std::wstring get_settings_module_path(); std::wstring get_updater_module_path(); - std::wstring get_game_module_version(); - std::wstring get_game_module_target_version(); + + std::string get_game_module_version(); + std::string get_game_module_target_version(); + static gamemeta get_gamemeta(launcher::game); private: launcher::game game_type = launcher::game::invalid; @@ -63,12 +62,10 @@ namespace game std::filesystem::path game_module_path; std::filesystem::path settings_module_path; std::filesystem::path updater_module_path; - std::wstring game_module_version; - std::wstring game_module_target_version; + std::string game_module_version; + std::string game_module_target_version; void ensure_created(launcher::game); }; - - } template diff --git a/src/client/launcher/launcher.cpp b/src/client/launcher/launcher.cpp index 5c6cdee..316d803 100644 --- a/src/client/launcher/launcher.cpp +++ b/src/client/launcher/launcher.cpp @@ -41,11 +41,9 @@ launcher::launcher() { } } - ), [](borderless_smartview* smartview) - { - delete smartview; - } - ); + ), [](borderless_smartview* smartview) { + delete smartview; + }); this->create_main_menu(); } @@ -55,272 +53,256 @@ void launcher::create_main_menu() smartview_->set_title("Laochan-Eacnet Launcher"); smartview_->set_dpi_aware_size(1280, 720); - smartview_->expose("version", []() -> std::string - { - return VERSION; - } - ); + smartview_->expose("version", []() -> std::string { + return VERSION; + }); - smartview_->expose("close", [this]() - { - smartview_->send_close(); - } - ); + smartview_->expose("close", [this]() { + smartview_->send_close(); + }); - smartview_->expose("minimize", [this]() - { - smartview_->set_minimized(true); - } - ); + smartview_->expose("minimize", [this]() { + smartview_->set_minimized(true); + }); - smartview_->expose("mounted", [this]() - { - smartview_->show(); - } - ); + smartview_->expose("mounted", [this]() { + smartview_->show(); + }); - smartview_->expose("shellExecute", [](std::string command, std::string args) - { - ShellExecuteA(nullptr, "open", command.data(), args.data(), nullptr, 1); - }, false - ); + smartview_->expose("shellExecute", [](std::string command, std::string args) { + ShellExecuteA(nullptr, "open", command.data(), args.data(), nullptr, 1); + }, false); - smartview_->expose("detectGameInstall", [](int game_index) -> std::string + smartview_->expose("detectGameInstall", [](int game_index) -> std::string { + try { - try - { - if (game_index >= static_cast(launcher::game::count) || game_index <= static_cast(launcher::game::invalid)) - { - MessageBoxA(nullptr, "game index out of range", "ERROR", MB_ICONERROR); - return {}; - } - auto gamemeta = ::game::environment::gamemeta::get_gamemeta(static_cast(game_index)); - nlohmann::json j1; - j1["game_type"] = game_index; - j1["game_name"] = ::game::environment::get_string(static_cast(game_index)); - j1["installed"] = gamemeta.get_install_state(); - if (gamemeta.get_install_state()) - { - j1["install_path"] = utils::string::wide_to_utf8(gamemeta.get_install_path()); - j1["resource_path"] = utils::string::wide_to_utf8(gamemeta.get_resource_path()); - j1["game_module_path"] = utils::string::wide_to_utf8(gamemeta.get_game_module_path()); - j1["settings_module_path"] = utils::string::wide_to_utf8(gamemeta.get_settings_module_path()); - j1["updater_module_path"] = utils::string::wide_to_utf8(gamemeta.get_updater_module_path()); - j1["game_module_version"] = utils::string::wide_to_utf8(gamemeta.get_game_module_version()); - j1["game_module_target_version"] = utils::string::wide_to_utf8(gamemeta.get_game_module_target_version()); - } - auto json = j1.dump(); + auto g = static_cast(game_index); - return json; - } - catch (std::exception&) + if (g >= launcher::game::count || g <= launcher::game::invalid) { + MessageBoxA(nullptr, "game index out of range", "ERROR", MB_ICONERROR); + return {}; } - return {}; - }, true - ); + auto meta = ::game::environment::gamemeta::get_gamemeta(g); - smartview_->expose("readFile", [](std::string path) -> std::string - { - if (!std::filesystem::exists(path)) - return ""; - auto pathw = utils::string::utf8_to_wide(path); - std::ifstream file_stream; - file_stream.open(pathw, std::ifstream::in); - file_stream.seekg(0, std::ios::end); - auto size = file_stream.tellg(); - file_stream.seekg(0, std::ios::beg); - std::string buffer; - buffer.resize(static_cast(size) + 1); - file_stream.read(buffer.data(), size); - buffer.resize(size); - file_stream.close(); - return buffer; - }, true - ); - - smartview_->expose("writeFile", [](std::string path, std::string content) -> void + nlohmann::json result + { + { "game_type", game_index }, + { "game_name", ::game::environment::get_string(static_cast(game_index)) }, + { "installed", meta.get_install_state() }, + }; + + if (meta.get_install_state()) + { + result["install_path"] = utils::string::wide_to_utf8(meta.get_install_path()); + result["resource_path"] = utils::string::wide_to_utf8(meta.get_resource_path()); + result["game_module_path"] = utils::string::wide_to_utf8(meta.get_game_module_path()); + result["settings_module_path"] = utils::string::wide_to_utf8(meta.get_settings_module_path()); + result["updater_module_path"] = utils::string::wide_to_utf8(meta.get_updater_module_path()); + result["game_module_version"] = meta.get_game_module_version(); + result["game_module_target_version"] = meta.get_game_module_target_version(); + } + + return result.dump(); + } + catch (std::exception&) { - if (std::filesystem::exists(path)) - std::filesystem::remove(path); - auto pathw = utils::string::utf8_to_wide(path); - std::ofstream file_stream; - file_stream.open(pathw, std::ifstream::out); - file_stream.write(content.data(), content.size()); - file_stream.close(); } - ); - smartview_->expose("uuid", []() -> std::string - { - const auto steamid = steam_proxy::get_steam_id(); - if (steamid != 0xFFFFFFFFDEADBEEFul) - return std::to_string(steamid); + return {}; + }, true); - const auto uuid = utils::string::dump_hex(utils::smbios::get_uuid(), ""); - return uuid; - } - ); + smartview_->expose("readFile", [](std::string path) -> std::string { + if (!std::filesystem::exists(path)) + return ""; + auto pathw = utils::string::utf8_to_wide(path); + std::ifstream file_stream{ pathw, std::ios::binary | std::ios::ate }; - smartview_->expose("setGame", [](int target) -> void - { - auto result = static_cast(target); - if (result >= launcher::game::count || result <= launcher::game::invalid) - return; + if (!file_stream.is_open()) + return ""; - target_game = result; - } - ); + auto size = file_stream.tellg(); + file_stream.seekg(0, std::ios::beg); + + std::string buffer; + buffer.resize(size); + file_stream.read(buffer.data(), size); - smartview_->expose("setParam", [](std::string key, std::string value) -> void + return buffer; + }, true); + + smartview_->expose("writeFile", [](std::string path, std::string content) -> void { + if (std::filesystem::exists(path)) + std::filesystem::remove(path); + + auto pathw = utils::string::utf8_to_wide(path); + std::ofstream file_stream{ pathw, std::ios::binary }; + + if (!file_stream.is_open()) { - ::game::environment::set_param(key, value); + printf("W:Failed to write %llu bytes to %s!\n", content.size(), path.data()); + return; } - ); - smartview_->expose("getAsioDeviceList", []() -> std::vector - { - std::vector result; + file_stream.write(content.data(), content.size()); + }); - HKEY key; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO", 0, KEY_READ, &key)) - return result; + smartview_->expose("uuid", []() -> std::string { + const auto steamid = steam_proxy::get_steam_id(); + if (steamid != 0xFFFFFFFFDEADBEEFul) + return std::to_string(steamid); - DWORD subkey_counts, subkey_name_maxlen; - RegQueryInfoKeyA(key, nullptr, nullptr, nullptr, &subkey_counts, &subkey_name_maxlen, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + const auto uuid = utils::string::dump_hex(utils::smbios::get_uuid(), ""); + return uuid; + }); - char* buffer = utils::memory::allocate(subkey_name_maxlen * 2); + smartview_->expose("setGame", [](int target) -> void { + auto result = static_cast(target); + if (result >= launcher::game::count || result <= launcher::game::invalid) + return; - for (DWORD i = 0; i < subkey_counts; i++) - { - DWORD size = subkey_name_maxlen * 2; - RegEnumKeyExA(key, i, buffer, &size, nullptr, nullptr, nullptr, nullptr); + target_game = result; + }); - result.push_back(std::string{ buffer }); - } + smartview_->expose("setParam", [](std::string key, std::string value) -> void { + ::game::environment::set_param(key, value); + }); - utils::memory::free(buffer); - return result; - } - ); + smartview_->expose("getAsioDeviceList", []() -> std::vector { + std::vector result; - smartview_->expose("checkWasapiDeviceStatus", []() -> int - { - IMMDeviceEnumerator* device_enumerator = nullptr; + HKEY key; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\ASIO", 0, KEY_READ, &key)) + return result; - auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast(&device_enumerator)); - if (FAILED(hr)) - { - return -1; - } + DWORD subkey_counts, subkey_name_maxlen; + RegQueryInfoKeyA(key, nullptr, nullptr, nullptr, &subkey_counts, &subkey_name_maxlen, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); - IMMDevice* device = nullptr; - hr = device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + char* buffer = utils::memory::allocate(subkey_name_maxlen * 2); - if (FAILED(hr)) - { - device_enumerator->Release(); - return -2; - } + for (DWORD i = 0; i < subkey_counts; i++) + { + DWORD size = subkey_name_maxlen * 2; + RegEnumKeyExA(key, i, buffer, &size, nullptr, nullptr, nullptr, nullptr); - IAudioClient* audio_client = nullptr; - hr = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast(&audio_client)); + result.push_back(std::string{ buffer }); + } - if (FAILED(hr)) - { - device->Release(); - device_enumerator->Release(); - return -3; - } + utils::memory::free(buffer); + return result; + }); - WAVEFORMATEXTENSIBLE wf = { 0 }; - wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = 2; - wf.Format.nSamplesPerSec = 44100; - wf.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;; - wf.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + smartview_->expose("checkWasapiDeviceStatus", []() -> int { + IMMDeviceEnumerator* device_enumerator = nullptr; - const WORD BIT_SETS[][2] = - { - { 32, 32 }, - { 24, 24 }, - { 16, 24 }, - { 16, 16 }, - }; + auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast(&device_enumerator)); + if (FAILED(hr)) + { + return -1; + } - for (auto& bit_set : BIT_SETS) - { - wf.Format.wBitsPerSample = bit_set[0]; - wf.Samples.wValidBitsPerSample = bit_set[0]; - wf.Format.nBlockAlign = wf.Format.nChannels * wf.Format.wBitsPerSample / 8; - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + IMMDevice* device = nullptr; + hr = device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); - hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, reinterpret_cast(&wf), nullptr); - if (SUCCEEDED(hr)) - { - audio_client->Release(); - device->Release(); - device_enumerator->Release(); + if (FAILED(hr)) + { + device_enumerator->Release(); + return -2; + } - return 0; - } - } + IAudioClient* audio_client = nullptr; + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast(&audio_client)); - audio_client->Release(); + if (FAILED(hr)) + { device->Release(); device_enumerator->Release(); - return 1; + return -3; } - ); - smartview_->expose("queryDisplayModes", []() -> std::vector + WAVEFORMATEXTENSIBLE wf = { 0 }; + wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.Format.nChannels = 2; + wf.Format.nSamplesPerSec = 44100; + wf.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;; + wf.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + const WORD BIT_SETS[][2] = { - static std::vector result; - if (result.size()) - return result; + { 32, 32 }, + { 24, 24 }, + { 16, 24 }, + { 16, 16 }, + }; - IDXGIFactory* factory = nullptr; - CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory); + for (auto& bit_set : BIT_SETS) + { + wf.Format.wBitsPerSample = bit_set[0]; + wf.Samples.wValidBitsPerSample = bit_set[0]; + wf.Format.nBlockAlign = wf.Format.nChannels * wf.Format.wBitsPerSample / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; - IDXGIAdapter* adapter = nullptr; - factory->EnumAdapters(0, &adapter); + hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, reinterpret_cast(&wf), nullptr); + if (SUCCEEDED(hr)) + { + audio_client->Release(); + device->Release(); + device_enumerator->Release(); - IDXGIOutput* output = nullptr; - adapter->EnumOutputs(0, &output); + return 0; + } + } - UINT mode_count = 0; - output->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &mode_count, nullptr); + audio_client->Release(); + device->Release(); + device_enumerator->Release(); + return 1; + }); - auto modes = utils::memory::allocate_array(mode_count); - output->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &mode_count, modes); + smartview_->expose("queryDisplayModes", []() -> std::vector { + static std::vector result; + if (result.size()) + return result; - for (UINT i = 0; i < mode_count; i++) - { - auto mode = modes[i]; - auto mode_formatted = utils::string::va("[%d,%d,%.2f]", mode.Width, mode.Height, mode.RefreshRate.Numerator / static_cast(mode.RefreshRate.Denominator)); + IDXGIFactory* factory = nullptr; + CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory); - if (std::find(result.begin(), result.end(), mode_formatted) == result.end()) - result.push_back(mode_formatted); - } + IDXGIAdapter* adapter = nullptr; + factory->EnumAdapters(0, &adapter); - utils::memory::free(modes); + IDXGIOutput* output = nullptr; + adapter->EnumOutputs(0, &output); - output->Release(); - adapter->Release(); - factory->Release(); + UINT mode_count = 0; + output->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &mode_count, nullptr); - return result; - } - ); + auto modes = utils::memory::allocate_array(mode_count); + output->GetDisplayModeList(DXGI_FORMAT_B8G8R8A8_UNORM, 0, &mode_count, modes); - smartview_->expose("selfPath", []() -> std::string + for (UINT i = 0; i < mode_count; i++) { - utils::nt::library self{}; - return self.get_path(); + auto mode = modes[i]; + auto mode_formatted = utils::string::va("[%d,%d,%.2f]", mode.Width, mode.Height, mode.RefreshRate.Numerator / static_cast(mode.RefreshRate.Denominator)); + + if (std::find(result.begin(), result.end(), mode_formatted) == result.end()) + result.push_back(mode_formatted); } - ); + + utils::memory::free(modes); + + output->Release(); + adapter->Release(); + factory->Release(); + + return result; + }); + + smartview_->expose("selfPath", []() -> std::string { + utils::nt::library self{}; + return utils::string::wide_to_utf8(self.get_path().generic_wstring()); + }); #if _DEBUG smartview_->set_dev_tools(true); diff --git a/src/client/main.cpp b/src/client/main.cpp index 1bd5a5a..0fd8c0f 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -66,47 +66,46 @@ FARPROC load_binary(const launcher::game game) loader loader; utils::nt::library self; - loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void* + loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void* { + // dump mount point + if (library == "avs2-core.dll" && function == "#105") { - // dump mount point - if (library == "avs2-core.dll" && function == "#105") - { - return dump_mount_point; - } - else if (function == "ExitProcess") - { - return exit_hook; - } - else if (function == "GetModuleHandleA") - { - return get_module_handle_a; - } - else if (function == "GetModuleHandleW") - { - return get_module_handle_w; - } - else if (function == "GetModuleFileNameA") - { - return get_module_file_name_a; - } + return dump_mount_point; + } + else if (function == "ExitProcess") + { + return exit_hook; + } + else if (function == "GetModuleHandleA") + { + return get_module_handle_a; + } + else if (function == "GetModuleHandleW") + { + return get_module_handle_w; + } + else if (function == "GetModuleFileNameA") + { + return get_module_file_name_a; + } - return component_loader::load_import(library, function); - }); + return component_loader::load_import(library, function); + }); std::string binary; switch (game) { - case launcher::game::iidx: - binary = "bm2dx.exe"; - break; - case launcher::game::sdvx: - binary = "sv6c.exe"; - break; - case launcher::game::gitadora: - binary = "gitadora.exe"; - break; - default: - throw std::runtime_error("Unsupported game!"); + case launcher::game::iidx: + binary = "bm2dx.exe"; + break; + case launcher::game::sdvx: + binary = "sv6c.exe"; + break; + case launcher::game::gitadora: + binary = "gitadora.exe"; + break; + default: + throw std::runtime_error("Unsupported game!"); } auto mod = loader.load_library(binary); @@ -127,7 +126,7 @@ bool try_set_game_environment(launcher::game game) auto modules_path = game_path / (game == launcher::game::iidx ? "app" : "modules"); SetCurrentDirectoryW(game_path.wstring().data()); - SetDllDirectoryW(modules_path.wstring().data()); + AddDllDirectory(modules_path.wstring().data()); return true; } @@ -160,53 +159,43 @@ launcher::game detect_game_from_arguments() } -void enable_eco_qos() { - auto sharedUserData = (BYTE*)0x7FFE0000; - auto major = *(ULONG*)(sharedUserData + 0x26c); - //auto minor = *(ULONG*)(sharedUserData + 0x270); - auto build = *(ULONG*)(sharedUserData + 0x260); - auto CurrentProcessHandle = GetCurrentProcess(); - MEMORY_PRIORITY_INFORMATION _MEMORY_PRIORITY_INFORMATION{}; - _MEMORY_PRIORITY_INFORMATION.MemoryPriority = 3; - PROCESS_POWER_THROTTLING_STATE _PROCESS_POWER_THROTTLING_STATE{}; - _PROCESS_POWER_THROTTLING_STATE.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; - _PROCESS_POWER_THROTTLING_STATE.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; - _PROCESS_POWER_THROTTLING_STATE.StateMask = 1; - //if (major >= 10 && build >= 22000)//Windows 11 EcoQoS - if (major >= 10 && build >= 16299)//Windows 1709 LowQoS - { - SetProcessInformation(CurrentProcessHandle, ProcessPowerThrottling, &_PROCESS_POWER_THROTTLING_STATE, sizeof(PROCESS_POWER_THROTTLING_STATE)); - SetProcessInformation(CurrentProcessHandle, ProcessMemoryPriority, &_MEMORY_PRIORITY_INFORMATION, sizeof(MEMORY_PRIORITY_INFORMATION)); - SetPriorityClass(CurrentProcessHandle, IDLE_PRIORITY_CLASS); - } -} +static void set_qos_mode(bool high) +{ + static const bool can_set_qos_mode = [] { + utils::nt::library ntdll{ "ntdll.dll" }; -void enable_high_qos() { - auto sharedUserData = (BYTE*)0x7FFE0000; - auto major = *(ULONG*)(sharedUserData + 0x26c); - //auto minor = *(ULONG*)(sharedUserData + 0x270); - auto build = *(ULONG*)(sharedUserData + 0x260); - auto CurrentProcessHandle = GetCurrentProcess(); - MEMORY_PRIORITY_INFORMATION _MEMORY_PRIORITY_INFORMATION{}; - _MEMORY_PRIORITY_INFORMATION.MemoryPriority = 5; - PROCESS_POWER_THROTTLING_STATE _PROCESS_POWER_THROTTLING_STATE{}; - _PROCESS_POWER_THROTTLING_STATE.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; - _PROCESS_POWER_THROTTLING_STATE.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; - _PROCESS_POWER_THROTTLING_STATE.StateMask = 0; - //if (major >= 10 && build >= 22000)//Windows 11 EcoQoS - if (major >= 10 && build >= 16299)//Windows 1709 LowQoS - { - SetProcessInformation(CurrentProcessHandle, ProcessPowerThrottling, &_PROCESS_POWER_THROTTLING_STATE, sizeof(PROCESS_POWER_THROTTLING_STATE)); - SetProcessInformation(CurrentProcessHandle, ProcessMemoryPriority, &_MEMORY_PRIORITY_INFORMATION, sizeof(MEMORY_PRIORITY_INFORMATION)); - SetPriorityClass(CurrentProcessHandle, HIGH_PRIORITY_CLASS); - } + OSVERSIONINFOEXW info; + ntdll.invoke_pascal("RtlGetVersion", &info); + + return info.dwMajorVersion >= 10 && info.dwBuildNumber >= 16299; + }(); + + if (!can_set_qos_mode) + return; + + auto self = GetCurrentProcess(); + + MEMORY_PRIORITY_INFORMATION priority_info{ + .MemoryPriority = high ? 5U : 3U, + }; + + PROCESS_POWER_THROTTLING_STATE power_state{ + .Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION, + .ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED, + .StateMask = high, + }; + + SetProcessInformation(self, ProcessPowerThrottling, &priority_info, sizeof(PROCESS_POWER_THROTTLING_STATE)); + SetProcessInformation(self, ProcessMemoryPriority, &power_state, sizeof(MEMORY_PRIORITY_INFORMATION)); + SetPriorityClass(self, IDLE_PRIORITY_CLASS); } int main() { FARPROC entry_point; enable_dpi_awareness(); - enable_eco_qos(); + set_qos_mode(false); + // pin system dinput8 here to prevent old client from loading LoadLibraryA("dinput8.dll"); @@ -214,10 +203,9 @@ int main() { auto premature_shutdown = true; - const auto _ = gsl::finally([&premature_shutdown] - { - component_loader::pre_destroy(); - }); + const auto _ = gsl::finally([&premature_shutdown] { + component_loader::pre_destroy(); + }); try { @@ -234,8 +222,7 @@ int main() return 0; } - enable_high_qos(); - DwmEnableMMCSS(TRUE); + set_qos_mode(true); try_set_game_environment(game); component_loader::create_components(game::environment::get_game()); @@ -255,10 +242,11 @@ int main() } catch (std::exception& e) { - MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); + MessageBoxA(nullptr, e.what(), "Laochan Bootstrap ERROR", MB_ICONERROR); return 1; } } + return static_cast(entry_point()); } diff --git a/src/common/utils/nt.cpp b/src/common/utils/nt.cpp index dadb198..e2e8c40 100644 --- a/src/common/utils/nt.cpp +++ b/src/common/utils/nt.cpp @@ -1,23 +1,25 @@ #include "nt.hpp" #include "memory.hpp" +#include + namespace utils::nt { library library::load(const std::string& name) { - return library(LoadLibraryA(name.data())); + return library{ name }; } library library::load(const std::filesystem::path& path) { - return library::load(path.generic_string()); + return library{ path.generic_string() }; } library library::get_by_address(void* address) { HMODULE handle = nullptr; GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - static_cast(address), &handle); + static_cast(address), &handle); return library(handle); } @@ -29,6 +31,7 @@ namespace utils::nt library::library(const std::string& name) { this->module_ = GetModuleHandleA(name.data()); + if (!this->module_) this->module_ = LoadLibraryA(name.data()); } library::library(const HMODULE handle) @@ -95,7 +98,7 @@ namespace utils::nt DWORD protection; VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, - &protection); + &protection); } size_t library::get_relative_entry_point() const @@ -119,19 +122,19 @@ namespace utils::nt { if (!this->is_valid()) return ""; - auto path = this->get_path(); + auto path = this->get_path().generic_string(); const auto pos = path.find_last_of("/\\"); if (pos == std::string::npos) return path; return path.substr(pos + 1); } - std::string library::get_path() const + std::filesystem::path library::get_path() const { if (!this->is_valid()) return ""; - char name[MAX_PATH] = {0}; - GetModuleFileNameA(this->module_, name, sizeof name); + wchar_t name[MAX_PATH] = { 0 }; + GetModuleFileNameW(this->module_, name, sizeof name); return name; } @@ -144,20 +147,22 @@ namespace utils::nt return path.parent_path().generic_string(); } - std::string library::get_version() const + std::string library::get_version(const std::filesystem::path& path) { DWORD handle; - auto path = this->get_path(); - auto size = GetFileVersionInfoSizeA(path.data(), &handle); + auto p = path.generic_wstring(); + auto size = GetFileVersionInfoSizeW(p.data(), &handle); - if (!size) return {}; + if (!size || !handle) return {}; auto buffer = memory::allocate(size); - - if (!GetFileVersionInfoA(path.data(), handle, size, buffer)) - { + auto _ = gsl::finally([=] { memory::free(buffer); + }); + + if (!GetFileVersionInfoW(p.data(), handle, size, buffer)) + { return {}; } @@ -166,45 +171,15 @@ namespace utils::nt if (!VerQueryValueA(buffer, "\\StringFileInfo\\041104b0\\ProductVersion", &out_buffer, &out_size)) { - memory::free(buffer); return {}; } - - auto result = std::string{reinterpret_cast(out_buffer)}; - memory::free(buffer); - return result; + return std::string{ reinterpret_cast(out_buffer) }; } - std::wstring library::get_version(const std::wstring& path) + std::string library::get_version() const { - DWORD handle; - - auto size = GetFileVersionInfoSizeW(path.data(), &handle); - - if (!size) return {}; - - auto buffer = memory::allocate(size); - - if (!GetFileVersionInfoW(path.data(), handle, size, buffer)) - { - memory::free(buffer); - return {}; - } - - LPVOID out_buffer; - UINT out_size; - - if (!VerQueryValueW(buffer, L"\\StringFileInfo\\041104b0\\ProductVersion", &out_buffer, &out_size)) - { - memory::free(buffer); - return {}; - } - - auto result = std::wstring{ reinterpret_cast(out_buffer) }; - - memory::free(buffer); - return result; + return library::get_version(get_path()); } void library::free() @@ -240,16 +215,16 @@ namespace utils::nt if (!header) return nullptr; auto* import_descriptor = reinterpret_cast(this->get_ptr() + header->DataDirectory - [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while (import_descriptor->Name) { if (!_stricmp(reinterpret_cast(this->get_ptr() + import_descriptor->Name), module_name.data())) { auto* original_thunk_data = reinterpret_cast(import_descriptor-> - OriginalFirstThunk + this->get_ptr()); + OriginalFirstThunk + this->get_ptr()); auto* thunk_data = reinterpret_cast(import_descriptor->FirstThunk + this-> - get_ptr()); + get_ptr()); while (original_thunk_data->u1.AddressOfData) { @@ -316,8 +291,8 @@ namespace utils::nt GetCurrentDirectoryA(sizeof(current_dir), current_dir); auto* const command_line = GetCommandLineA(); - CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, - &startup_info, &process_info); + CreateProcessA(self.get_path().generic_string().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, + &startup_info, &process_info); if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess); diff --git a/src/common/utils/nt.hpp b/src/common/utils/nt.hpp index fa99870..9d761d4 100644 --- a/src/common/utils/nt.hpp +++ b/src/common/utils/nt.hpp @@ -24,7 +24,7 @@ namespace utils::nt static library load(const std::string& name); static library load(const std::filesystem::path& path); static library get_by_address(void* address); - static std::wstring get_version(const std::wstring& name); + static std::string get_version(const std::filesystem::path& path); library(); explicit library(const std::string& name); @@ -45,8 +45,8 @@ namespace utils::nt size_t get_relative_entry_point() const; bool is_valid() const; + std::filesystem::path get_path() const; std::string get_name() const; - std::string get_path() const; std::string get_folder() const; std::string get_version() const; std::uint8_t* get_ptr() const;