From fbfdc30e253f851534bd33886b7df8c38fa88adc Mon Sep 17 00:00:00 2001 From: momo5502 Date: Tue, 24 Sep 2019 10:30:08 +0200 Subject: [PATCH] Fix stuff --- generate.bat | 6 +- premake5.lua | 262 +++--- src/game/demonware/services/bdDediAuth.cpp | 122 +-- src/game/demonware/services/bdDediRSAAuth.cpp | 142 ++-- src/game/demonware/services/bdSteamAuth.cpp | 122 +-- src/game/game.cpp | 748 +++++++++--------- src/game/game.hpp | 190 ++--- src/launcher/html/doc_host_ui_handler.cpp | 230 +++--- src/launcher/html/doc_host_ui_handler.hpp | 94 +-- src/launcher/html/html_argument.cpp | 96 +-- src/launcher/html/html_argument.hpp | 40 +- src/launcher/html/html_dispatch.cpp | 122 +-- src/launcher/html/html_dispatch.hpp | 48 +- src/launcher/html/html_frame.cpp | 524 ++++++------ src/launcher/html/html_frame.hpp | 134 ++-- src/launcher/html/html_window.cpp | 58 +- src/launcher/html/html_window.hpp | 30 +- src/launcher/html/ole_client_site.cpp | 154 ++-- src/launcher/html/ole_client_site.hpp | 48 +- src/launcher/html/ole_in_place_frame.cpp | 164 ++-- src/launcher/html/ole_in_place_frame.hpp | 60 +- src/launcher/html/ole_in_place_site.cpp | 210 ++--- src/launcher/html/ole_in_place_site.hpp | 64 +- src/launcher/launcher.cpp | 176 ++--- src/launcher/launcher.hpp | 62 +- src/launcher/window.cpp | 360 ++++----- src/launcher/window.hpp | 84 +- src/loader/binary_loader.cpp | 242 +++--- src/loader/binary_loader.hpp | 16 +- src/loader/loader.cpp | 330 ++++---- src/loader/loader.hpp | 44 +- src/loader/module_loader.cpp | 200 ++--- src/main.cpp | 264 +++---- src/module/ceg.cpp | 180 ++--- src/module/console.cpp | 230 +++--- src/module/discord.cpp | 100 +-- src/module/fastfiles.cpp | 120 +-- src/module/fov.cpp | 74 +- src/module/patches.cpp | 122 +-- src/module/scripting.cpp | 324 ++++---- src/module/security.cpp | 50 +- src/module/steam_proxy.cpp | 356 ++++----- src/resource.hpp | 32 +- src/resource.rc | 234 +++--- src/resources/main.html | 260 +++--- src/resources/settings.html | 86 +- src/steam/interfaces/user.cpp | 286 +++---- tools/premake5.exe | Bin 1715712 -> 1362432 bytes 48 files changed, 3950 insertions(+), 3950 deletions(-) diff --git a/generate.bat b/generate.bat index d745a2e..33a2a8a 100644 --- a/generate.bat +++ b/generate.bat @@ -1,3 +1,3 @@ -@echo off -git submodule update --init --recursive -tools\premake5 %* vs2017 \ No newline at end of file +@echo off +git submodule update --init --recursive +tools\premake5 %* vs2019 \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index b097298..9ff4879 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,131 +1,131 @@ -dependencies = { - basePath = "./deps" -} - -function dependencies.load() - dir = path.join(dependencies.basePath, "premake/*.lua") - deps = os.matchfiles(dir) - - for i, dep in pairs(deps) do - dep = dep:gsub(".lua", "") - require(dep) - end -end - -function dependencies.imports() - for i, proj in pairs(dependencies) do - if type(i) == 'number' then - proj.import() - end - end -end - -function dependencies.projects() - for i, proj in pairs(dependencies) do - if type(i) == 'number' then - proj.project() - end - end -end - -newoption { - trigger = "copy-to", - description = "Optional, copy the EXE to a custom folder after build, define the path here if wanted.", - value = "PATH" -} - -dependencies.load() - -workspace "open-iw5" - startproject "open-iw5" - location "./build" - objdir "%{wks.location}/obj" - targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}" - - configurations { - "Debug", - "Release", - } - - architecture "x32" - platforms "x32" - - buildoptions "/std:c++latest" - systemversion "latest" - symbols "On" - staticruntime "On" - editandcontinue "Off" - warnings "Extra" - characterset "ASCII" - toolset "v142" - - flags { - "NoIncrementalLink", - "NoMinimalRebuild", - "MultiProcessorCompile", - "No64BitChecks" - } - - configuration "windows" - defines { - "_WINDOWS", - "WIN32", - } - - configuration "Release" - optimize "Full" - - defines { - "NDEBUG", - } - - flags { - "FatalCompileWarnings", - } - - configuration "Debug" - optimize "Debug" - - defines { - "DEBUG", - "_DEBUG", - } - - configuration {} - - project "open-iw5" - kind "ConsoleApp" - language "C++" - - pchheader "std_include.hpp" - pchsource "src/std_include.cpp" - - linkoptions "/IGNORE:4254 /DYNAMICBASE:NO /SAFESEH:NO /LARGEADDRESSAWARE" - linkoptions "/LAST:.main" - - - files { - "./src/**.rc", - "./src/**.hpp", - "./src/**.cpp", - "./src/resources/**.*" - } - - includedirs { - "./src" - } - - resincludedirs { - "$(ProjectDir)src" - } - - if _OPTIONS["copy-to"] then - postbuildcommands { - "copy /y \"$(TargetDir)*.exe\" \"" .. _OPTIONS["copy-to"] .. "\"" - } - end - - dependencies.imports() - - group "Dependencies" - dependencies.projects() +dependencies = { + basePath = "./deps" +} + +function dependencies.load() + dir = path.join(dependencies.basePath, "premake/*.lua") + deps = os.matchfiles(dir) + + for i, dep in pairs(deps) do + dep = dep:gsub(".lua", "") + require(dep) + end +end + +function dependencies.imports() + for i, proj in pairs(dependencies) do + if type(i) == 'number' then + proj.import() + end + end +end + +function dependencies.projects() + for i, proj in pairs(dependencies) do + if type(i) == 'number' then + proj.project() + end + end +end + +newoption { + trigger = "copy-to", + description = "Optional, copy the EXE to a custom folder after build, define the path here if wanted.", + value = "PATH" +} + +dependencies.load() + +workspace "open-iw5" + startproject "open-iw5" + location "./build" + objdir "%{wks.location}/obj" + targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}" + + configurations { + "Debug", + "Release", + } + + architecture "x32" + platforms "x32" + + buildoptions "/std:c++latest" + systemversion "latest" + symbols "On" + staticruntime "On" + editandcontinue "Off" + warnings "Extra" + characterset "ASCII" + toolset "v142" + + flags { + "NoIncrementalLink", + "NoMinimalRebuild", + "MultiProcessorCompile", + "No64BitChecks" + } + + configuration "windows" + defines { + "_WINDOWS", + "WIN32", + } + + configuration "Release" + optimize "Full" + + defines { + "NDEBUG", + } + + flags { + "FatalCompileWarnings", + } + + configuration "Debug" + optimize "Debug" + + defines { + "DEBUG", + "_DEBUG", + } + + configuration {} + + project "open-iw5" + kind "ConsoleApp" + language "C++" + + pchheader "std_include.hpp" + pchsource "src/std_include.cpp" + + linkoptions "/IGNORE:4254 /DYNAMICBASE:NO /SAFESEH:NO /LARGEADDRESSAWARE" + linkoptions "/LAST:.main" + + + files { + "./src/**.rc", + "./src/**.hpp", + "./src/**.cpp", + "./src/resources/**.*" + } + + includedirs { + "./src" + } + + resincludedirs { + "$(ProjectDir)src" + } + + if _OPTIONS["copy-to"] then + postbuildcommands { + "copy /y \"$(TargetDir)*.exe\" \"" .. _OPTIONS["copy-to"] .. "\"" + } + end + + dependencies.imports() + + group "Dependencies" + dependencies.projects() diff --git a/src/game/demonware/services/bdDediAuth.cpp b/src/game/demonware/services/bdDediAuth.cpp index 12f4ddb..19c5bd6 100644 --- a/src/game/demonware/services/bdDediAuth.cpp +++ b/src/game/demonware/services/bdDediAuth.cpp @@ -1,61 +1,61 @@ -#include -#include "bdDediAuth.hpp" -#include "game/game.hpp" -#include "steam/steam.hpp" -#include "utils/cryptography.hpp" - -namespace demonware -{ - void bdDediAuth::call_service(i_server* server, const std::string& data) - { - bit_buffer buffer(data); - - bool more_data; - buffer.set_use_data_types(false); - buffer.read_bool(&more_data); - buffer.set_use_data_types(true); - - uint32_t seed, title_id, ticket_size; - buffer.read_uint32(&seed); - buffer.read_uint32(&title_id); - - uint8_t ticket[1024]; - buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); - - game::native::bdAuthTicket auth_ticket{}; - std::memset(&auth_ticket, 0xA, sizeof auth_ticket); - - auth_ticket.m_magicNumber = 0x0EFBDADDE; - auth_ticket.m_type = 0; - auth_ticket.m_titleID = title_id; - auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; - auth_ticket.m_licenseID = 4; - - auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY); - - strcpy_s(auth_ticket.m_username, "Open-IW5 Server"); - std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); - auth_ticket.m_timeIssued = static_cast(time(nullptr)); - - uint8_t lsg_ticket[128]; - ZeroMemory(&lsg_ticket, sizeof lsg_ticket); - std::memcpy(lsg_ticket, key.data(), 24); - - const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); - - const std::string enc_key(reinterpret_cast(&ticket[32]), 24); - auto enc_ticket = utils::cryptography::des3::encrypt( - std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); - - bit_buffer response; - response.set_use_data_types(false); - response.write_bool(false); - response.write_uint32(700); - response.write_uint32(seed); - response.write_bytes(enc_ticket.size(), enc_ticket.data()); - response.write_bytes(sizeof(lsg_ticket), lsg_ticket); - - auto reply = server->create_message(29); - reply->send(&response, false); - } -} +#include +#include "bdDediAuth.hpp" +#include "game/game.hpp" +#include "steam/steam.hpp" +#include "utils/cryptography.hpp" + +namespace demonware +{ + void bdDediAuth::call_service(i_server* server, const std::string& data) + { + bit_buffer buffer(data); + + bool more_data; + buffer.set_use_data_types(false); + buffer.read_bool(&more_data); + buffer.set_use_data_types(true); + + uint32_t seed, title_id, ticket_size; + buffer.read_uint32(&seed); + buffer.read_uint32(&title_id); + + uint8_t ticket[1024]; + buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); + + game::native::bdAuthTicket auth_ticket{}; + std::memset(&auth_ticket, 0xA, sizeof auth_ticket); + + auth_ticket.m_magicNumber = 0x0EFBDADDE; + auth_ticket.m_type = 0; + auth_ticket.m_titleID = title_id; + auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; + auth_ticket.m_licenseID = 4; + + auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY); + + strcpy_s(auth_ticket.m_username, "Open-IW5 Server"); + std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); + auth_ticket.m_timeIssued = static_cast(time(nullptr)); + + uint8_t lsg_ticket[128]; + ZeroMemory(&lsg_ticket, sizeof lsg_ticket); + std::memcpy(lsg_ticket, key.data(), 24); + + const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); + + const std::string enc_key(reinterpret_cast(&ticket[32]), 24); + auto enc_ticket = utils::cryptography::des3::encrypt( + std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); + + bit_buffer response; + response.set_use_data_types(false); + response.write_bool(false); + response.write_uint32(700); + response.write_uint32(seed); + response.write_bytes(enc_ticket.size(), enc_ticket.data()); + response.write_bytes(sizeof(lsg_ticket), lsg_ticket); + + auto reply = server->create_message(29); + reply->send(&response, false); + } +} diff --git a/src/game/demonware/services/bdDediRSAAuth.cpp b/src/game/demonware/services/bdDediRSAAuth.cpp index 37684bf..9511c34 100644 --- a/src/game/demonware/services/bdDediRSAAuth.cpp +++ b/src/game/demonware/services/bdDediRSAAuth.cpp @@ -1,71 +1,71 @@ -#include -#include "bdDediRSAAuth.hpp" -#include "game/game.hpp" -#include "steam/steam.hpp" -#include "utils/cryptography.hpp" - -namespace demonware -{ - void bdDediRSAAuth::call_service(i_server* server, const std::string& data) - { - bit_buffer buffer(data); - - bool more_data; - buffer.set_use_data_types(false); - buffer.read_bool(&more_data); - buffer.set_use_data_types(true); - - uint32_t seed, title_id, ticket_size; - buffer.read_uint32(&seed); - buffer.read_uint32(&title_id); - - unsigned char rsakey[140]; - buffer.read_bytes(sizeof(rsakey), rsakey); - - uint8_t ticket[1024]; - buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); - - game::native::bdAuthTicket auth_ticket{}; - std::memset(&auth_ticket, 0xA, sizeof auth_ticket); - - auth_ticket.m_magicNumber = 0x0EFBDADDE; - auth_ticket.m_type = 0; - auth_ticket.m_titleID = title_id; - auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; - - auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY); - - strcpy_s(auth_ticket.m_username, "Open-IW5 Server"); - std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); - auth_ticket.m_timeIssued = static_cast(time(nullptr)); - - uint8_t lsg_ticket[128]; - ZeroMemory(&lsg_ticket, sizeof lsg_ticket); - std::memcpy(lsg_ticket, key.data(), 24); - - const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); - - const std::string enc_key(reinterpret_cast(&ticket[32]), 24); - auto enc_ticket = utils::cryptography::des3::encrypt( - std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); - - register_hash(&sha1_desc); - register_prng(&yarrow_desc); - - std::string encrypted_key = utils::cryptography::rsa::encrypt(std::string(SERVER_CD_KEY, 24), - std::string("DW-RSAENC", 10), - std::string(PCHAR(rsakey), sizeof(rsakey))); - - bit_buffer response; - response.set_use_data_types(false); - response.write_bool(false); - response.write_uint32(700); - response.write_uint32(seed); - response.write_bytes(enc_ticket.size(), enc_ticket.data()); - response.write_bytes(sizeof(lsg_ticket), lsg_ticket); - response.write_bytes(encrypted_key.size(), encrypted_key.data()); - - auto reply = server->create_message(29); - reply->send(&response, false); - } -} +#include +#include "bdDediRSAAuth.hpp" +#include "game/game.hpp" +#include "steam/steam.hpp" +#include "utils/cryptography.hpp" + +namespace demonware +{ + void bdDediRSAAuth::call_service(i_server* server, const std::string& data) + { + bit_buffer buffer(data); + + bool more_data; + buffer.set_use_data_types(false); + buffer.read_bool(&more_data); + buffer.set_use_data_types(true); + + uint32_t seed, title_id, ticket_size; + buffer.read_uint32(&seed); + buffer.read_uint32(&title_id); + + unsigned char rsakey[140]; + buffer.read_bytes(sizeof(rsakey), rsakey); + + uint8_t ticket[1024]; + buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); + + game::native::bdAuthTicket auth_ticket{}; + std::memset(&auth_ticket, 0xA, sizeof auth_ticket); + + auth_ticket.m_magicNumber = 0x0EFBDADDE; + auth_ticket.m_type = 0; + auth_ticket.m_titleID = title_id; + auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; + + auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY); + + strcpy_s(auth_ticket.m_username, "Open-IW5 Server"); + std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); + auth_ticket.m_timeIssued = static_cast(time(nullptr)); + + uint8_t lsg_ticket[128]; + ZeroMemory(&lsg_ticket, sizeof lsg_ticket); + std::memcpy(lsg_ticket, key.data(), 24); + + const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); + + const std::string enc_key(reinterpret_cast(&ticket[32]), 24); + auto enc_ticket = utils::cryptography::des3::encrypt( + std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); + + register_hash(&sha1_desc); + register_prng(&yarrow_desc); + + std::string encrypted_key = utils::cryptography::rsa::encrypt(std::string(SERVER_CD_KEY, 24), + std::string("DW-RSAENC", 10), + std::string(PCHAR(rsakey), sizeof(rsakey))); + + bit_buffer response; + response.set_use_data_types(false); + response.write_bool(false); + response.write_uint32(700); + response.write_uint32(seed); + response.write_bytes(enc_ticket.size(), enc_ticket.data()); + response.write_bytes(sizeof(lsg_ticket), lsg_ticket); + response.write_bytes(encrypted_key.size(), encrypted_key.data()); + + auto reply = server->create_message(29); + reply->send(&response, false); + } +} diff --git a/src/game/demonware/services/bdSteamAuth.cpp b/src/game/demonware/services/bdSteamAuth.cpp index db54ca7..4bc4f8c 100644 --- a/src/game/demonware/services/bdSteamAuth.cpp +++ b/src/game/demonware/services/bdSteamAuth.cpp @@ -1,61 +1,61 @@ -#include -#include "bdSteamAuth.hpp" -#include "game/structs.hpp" -#include "steam/steam.hpp" -#include "utils/cryptography.hpp" - -namespace demonware -{ - void bdSteamAuth::call_service(i_server* server, const std::string& data) - { - bit_buffer buffer(data); - - bool more_data; - buffer.set_use_data_types(false); - buffer.read_bool(&more_data); - buffer.set_use_data_types(true); - - uint32_t seed, title_id, ticket_size; - buffer.read_uint32(&seed); - buffer.read_uint32(&title_id); - buffer.read_uint32(&ticket_size); - - uint8_t ticket[1024]; - buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); - - game::native::bdAuthTicket auth_ticket{}; - std::memset(&auth_ticket, 0xA, sizeof auth_ticket); - - auth_ticket.m_magicNumber = 0x0EFBDADDE; - auth_ticket.m_type = 0; - auth_ticket.m_titleID = title_id; - auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; - - auto key = utils::cryptography::tiger::compute("Open-IW5"); - - strcpy_s(auth_ticket.m_username, "Open-IW5 User"); - std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); - auth_ticket.m_timeIssued = static_cast(time(nullptr)); - - uint8_t lsg_ticket[128]; - ZeroMemory(&lsg_ticket, sizeof lsg_ticket); - std::memcpy(lsg_ticket, key.data(), 24); - - const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); - - const std::string enc_key(reinterpret_cast(&ticket[32]), 24); - auto enc_ticket = utils::cryptography::des3::encrypt( - std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); - - bit_buffer response; - response.set_use_data_types(false); - response.write_bool(false); - response.write_uint32(700); - response.write_uint32(seed); - response.write_bytes(enc_ticket.size(), enc_ticket.data()); - response.write_bytes(sizeof(lsg_ticket), lsg_ticket); - - auto reply = server->create_message(29); - reply->send(&response, false); - } -} +#include +#include "bdSteamAuth.hpp" +#include "game/structs.hpp" +#include "steam/steam.hpp" +#include "utils/cryptography.hpp" + +namespace demonware +{ + void bdSteamAuth::call_service(i_server* server, const std::string& data) + { + bit_buffer buffer(data); + + bool more_data; + buffer.set_use_data_types(false); + buffer.read_bool(&more_data); + buffer.set_use_data_types(true); + + uint32_t seed, title_id, ticket_size; + buffer.read_uint32(&seed); + buffer.read_uint32(&title_id); + buffer.read_uint32(&ticket_size); + + uint8_t ticket[1024]; + buffer.read_bytes(std::min(ticket_size, static_cast(sizeof(ticket))), ticket); + + game::native::bdAuthTicket auth_ticket{}; + std::memset(&auth_ticket, 0xA, sizeof auth_ticket); + + auth_ticket.m_magicNumber = 0x0EFBDADDE; + auth_ticket.m_type = 0; + auth_ticket.m_titleID = title_id; + auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits; + + auto key = utils::cryptography::tiger::compute("Open-IW5"); + + strcpy_s(auth_ticket.m_username, "Open-IW5 User"); + std::memcpy(auth_ticket.m_sessionKey, key.data(), 24); + auth_ticket.m_timeIssued = static_cast(time(nullptr)); + + uint8_t lsg_ticket[128]; + ZeroMemory(&lsg_ticket, sizeof lsg_ticket); + std::memcpy(lsg_ticket, key.data(), 24); + + const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&seed), 4)); + + const std::string enc_key(reinterpret_cast(&ticket[32]), 24); + auto enc_ticket = utils::cryptography::des3::encrypt( + std::string(reinterpret_cast(&auth_ticket), sizeof(auth_ticket)), iv, enc_key); + + bit_buffer response; + response.set_use_data_types(false); + response.write_bool(false); + response.write_uint32(700); + response.write_uint32(seed); + response.write_bytes(enc_ticket.size(), enc_ticket.data()); + response.write_bytes(sizeof(lsg_ticket), lsg_ticket); + + auto reply = server->create_message(29); + reply->send(&response, false); + } +} diff --git a/src/game/game.cpp b/src/game/game.cpp index c505a81..f9caa74 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1,374 +1,374 @@ -#include -#include "game.hpp" - -namespace game -{ - namespace native - { - Cmd_AddCommand_t Cmd_AddCommand; - - Com_Error_t Com_Error; - - DB_LoadXAssets_t DB_LoadXAssets; - - Dvar_SetFromStringByName_t Dvar_SetFromStringByName; - - G_RunFrame_t G_RunFrame; - - MSG_ReadData_t MSG_ReadData; - - MT_AllocIndex_t MT_AllocIndex; - - RemoveRefToValue_t RemoveRefToValue; - - SL_GetStringOfSize_t SL_GetStringOfSize; - - Sys_ShowConsole_t Sys_ShowConsole; - - VM_Notify_t VM_Notify; - - decltype(longjmp)* _longjmp; - - int* cmd_args; - int* cmd_argc; - const char*** cmd_argv; - - short* scrVarGlob; - char** scrMemTreePub; - char* scrMemTreeGlob; - - scrVmPub_t* scr_VmPub; - - scr_call_t* scr_instanceFunctions; - scr_call_t* scr_globalFunctions; - - unsigned int* levelEntityId; - - int* g_script_error_level; - jmp_buf* g_script_error; - - scr_classStruct_t* g_classMap; - - void AddRefToValue(VariableValue* value) - { - if (value->type == SCRIPT_OBJECT) - { - ++scrVarGlob[4 * value->u.entityId]; - } - else if ((value->type & ~1) == SCRIPT_STRING) - { - static const auto size = is_sp() ? 16 : 12; - const auto ref_count = reinterpret_cast(*scrMemTreePub + size * value - ->u.stringValue); - InterlockedIncrement(ref_count); - } - else if (value->type == SCRIPT_VECTOR) - { - if (!*PBYTE(value->u.vectorValue - 1)) - { - ++*PWORD(value->u.vectorValue - 4); - } - } - } - - __declspec(naked) unsigned int conbuf_append_text_dedicated(const char* message) - { - static DWORD func = 0x53C790; - - __asm - { - mov ecx, message - call func - retn - } - } - - void Conbuf_AppendText(const char* message) - { - if(is_dedi()) - { - conbuf_append_text_dedicated(message); - } - else - { - reinterpret_cast(SELECT_VALUE(0x4C84E0, 0x5CF610, 0))(message); - } - } - - __declspec(naked) unsigned int find_variable_dedicated(unsigned int parentId, unsigned int name) - { - static DWORD func = 0x4E7ED0; - - __asm - { - mov eax, name - mov ecx, parentId - call func - retn - } - } - - unsigned int FindVariable(const unsigned int parentId, const unsigned int name) - { - if (is_dedi()) - { - return find_variable_dedicated(parentId, name); - } - else - { - return reinterpret_cast // - (SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0))(parentId, name); - } - } - - __declspec(naked) VariableValue get_entity_field_value_dedicated(unsigned int classnum, int entnum, int _offset) - { - static DWORD func = 0x4F1400; - - __asm - { - push _offset - push entnum - mov ecx, classnum - call func - add esp, 8h - retn - } - } - - VariableValue GetEntityFieldValue(const unsigned int classnum, const int entnum, const int offset) - { - if (is_dedi()) - { - return get_entity_field_value_dedicated(classnum, entnum, offset); - } - else - { - return reinterpret_cast // - (SELECT_VALUE(0x530E30, 0x56AF20, 0x0))(classnum, entnum, offset); - } - } - - void* MT_Alloc(const int numBytes, const int type) - { - return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type)); - } - - const float* Scr_AllocVector(const float* v) - { - const auto mem = static_cast(MT_Alloc(16, 2)); - *mem = 0; - - const auto array = reinterpret_cast(mem + 1); - array[0] = v[0]; - array[1] = v[1]; - array[2] = v[2]; - - return array; - } - - void Scr_ClearOutParams() - { - const auto num_params = scr_VmPub->outparamcount; - for (unsigned int i = num_params; i > 0; --i) - { - const auto value = scr_VmPub->top[i - 1]; - RemoveRefToValue(value.type, value.u); - } - - scr_VmPub->top -= num_params; - } - - scr_entref_t Scr_GetEntityIdRef(const unsigned int id) - { - static auto class_array = reinterpret_cast(SELECT_VALUE(0x19AFC84, 0x1E72184, 0x1D3C804)); - static auto ent_array = reinterpret_cast(SELECT_VALUE(0x19AFC82, 0x1E72182, 0x1D3C802)); - - scr_entref_t result{}; - result.raw.classnum = static_cast(class_array[2 * id]) >> 8; - result.raw.entnum = ent_array[4 * id]; - - return result; - } - - scr_call_t Scr_GetFunc(const unsigned int index) - { - if (index > 0x1C7) - { - return scr_instanceFunctions[index]; - } - else - { - return scr_globalFunctions[index]; - } - } - - __declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int stringValue, - unsigned int paramcount) - { - static DWORD func = 0x56B5E0; - - __asm - { - mov eax, paramcount - push stringValue - push id - call func - add esp, 8h - retn - } - } - - __declspec(naked) void scr_notify_id_singleplayer(unsigned int id, unsigned int stringValue, - unsigned int paramcount) - { - static DWORD func = 0x610980; - - __asm - { - mov eax, paramcount - push stringValue - push id - call func - add esp, 8h - retn - } - } - - void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount) - { - if (is_mp()) - { - return scr_notify_id_multiplayer(id, stringValue, paramcount); - } - else if (is_sp()) - { - return scr_notify_id_singleplayer(id, stringValue, paramcount); - } - else - { - return reinterpret_cast(0x4EFAA0)(id, stringValue, paramcount); - } - } - - __declspec(naked) int scr_set_object_field_dedicated(unsigned int classnum, int entnum, int _offset) - { - static DWORD func = 0x4B15C0; - - __asm - { - mov ecx, _offset - mov eax, entnum - push classnum - call func - add esp, 4h - retn - } - } - - int Scr_SetObjectField(const unsigned int classnum, const int entnum, const int offset) - { - if (is_dedi()) - { - return scr_set_object_field_dedicated(classnum, entnum, offset); - } - else - { - return reinterpret_cast // - (SELECT_VALUE(0x42CAD0, 0x52BCC0, 0x0))(classnum, entnum, offset); - } - } - - const char* SL_ConvertToString(const unsigned int stringValue) - { - if (!stringValue) return nullptr; - - static const auto size = is_sp() ? 16 : 12; - return *scrMemTreePub + size * stringValue + 4; - } - - unsigned int SL_GetString(const char* str, const unsigned int user) - { - return SL_GetStringOfSize(str, user, strlen(str) + 1, 7); - } - } - - launcher::mode mode = launcher::mode::none; - - launcher::mode get_mode() - { - if(mode == launcher::mode::none) - { - throw std::runtime_error("Launcher mode not valid. Something must be wrong."); - } - - return mode; - } - - bool is_mp() - { - return get_mode() == launcher::mode::multiplayer; - } - - bool is_sp() - { - return get_mode() == launcher::mode::singleplayer; - } - - bool is_dedi() - { - return get_mode() == launcher::mode::server; - } - - void initialize(const launcher::mode _mode) - { - mode = _mode; - - native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0)); - - native::Com_Error = native::Com_Error_t(SELECT_VALUE(0x425540, 0x555450, 0x4D93F0)); - - native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770)); - - native::Dvar_SetFromStringByName = native::Dvar_SetFromStringByName_t(SELECT_VALUE(0x4DD090, 0x5BF740, 0x518DF0)); - - native::G_RunFrame = native::G_RunFrame_t(SELECT_VALUE(0x52EAA0, 0x50CB70, 0x48AD60)); - - native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0)); - - native::MT_AllocIndex = native::MT_AllocIndex_t(SELECT_VALUE(0x4B9610, 0x562080, 0x4E6C30)); - - native::RemoveRefToValue = native::RemoveRefToValue_t(SELECT_VALUE(0x477EA0, 0x565730, 0x4E8A40)); - - native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650, 0x4E7490)); - - native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); - - native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450)); - - native::_longjmp = reinterpret_cast(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558)); - - native::cmd_args = reinterpret_cast(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8)); - native::cmd_argc = reinterpret_cast(SELECT_VALUE(0x1750794, 0x1C97914, 0x1B4563C)); - native::cmd_argv = reinterpret_cast(SELECT_VALUE(0x17507B4, 0x1C97934, 0x1B4565C)); - - native::scrVarGlob = reinterpret_cast(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800)); - native::scrMemTreePub = reinterpret_cast(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4)); - native::scrMemTreeGlob = reinterpret_cast(SELECT_VALUE(0x186DA00, 0x1D6FF00, 0x1C16600)); - - native::scr_VmPub = reinterpret_cast(SELECT_VALUE(0x1BF2580, 0x20B4A80, 0x1F5B080)); - - native::scr_instanceFunctions = reinterpret_cast( SELECT_VALUE(0x184CDB0, 0x1D4F258, - 0x1BF59C8)); - native::scr_globalFunctions = reinterpret_cast( SELECT_VALUE(0x186C68C, 0x1D6EB34, - 0x1C152A4 -)); - - native::g_script_error_level = reinterpret_cast(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058)); - native::g_script_error = reinterpret_cast(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818)); - - native::g_classMap = reinterpret_cast(SELECT_VALUE(0x92D140, 0x8B4300, 0x7C0408)); - - native::levelEntityId = reinterpret_cast(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C)); - } -} +#include +#include "game.hpp" + +namespace game +{ + namespace native + { + Cmd_AddCommand_t Cmd_AddCommand; + + Com_Error_t Com_Error; + + DB_LoadXAssets_t DB_LoadXAssets; + + Dvar_SetFromStringByName_t Dvar_SetFromStringByName; + + G_RunFrame_t G_RunFrame; + + MSG_ReadData_t MSG_ReadData; + + MT_AllocIndex_t MT_AllocIndex; + + RemoveRefToValue_t RemoveRefToValue; + + SL_GetStringOfSize_t SL_GetStringOfSize; + + Sys_ShowConsole_t Sys_ShowConsole; + + VM_Notify_t VM_Notify; + + decltype(longjmp)* _longjmp; + + int* cmd_args; + int* cmd_argc; + const char*** cmd_argv; + + short* scrVarGlob; + char** scrMemTreePub; + char* scrMemTreeGlob; + + scrVmPub_t* scr_VmPub; + + scr_call_t* scr_instanceFunctions; + scr_call_t* scr_globalFunctions; + + unsigned int* levelEntityId; + + int* g_script_error_level; + jmp_buf* g_script_error; + + scr_classStruct_t* g_classMap; + + void AddRefToValue(VariableValue* value) + { + if (value->type == SCRIPT_OBJECT) + { + ++scrVarGlob[4 * value->u.entityId]; + } + else if ((value->type & ~1) == SCRIPT_STRING) + { + static const auto size = is_sp() ? 16 : 12; + const auto ref_count = reinterpret_cast(*scrMemTreePub + size * value + ->u.stringValue); + InterlockedIncrement(ref_count); + } + else if (value->type == SCRIPT_VECTOR) + { + if (!*PBYTE(value->u.vectorValue - 1)) + { + ++*PWORD(value->u.vectorValue - 4); + } + } + } + + __declspec(naked) unsigned int conbuf_append_text_dedicated(const char* message) + { + static DWORD func = 0x53C790; + + __asm + { + mov ecx, message + call func + retn + } + } + + void Conbuf_AppendText(const char* message) + { + if(is_dedi()) + { + conbuf_append_text_dedicated(message); + } + else + { + reinterpret_cast(SELECT_VALUE(0x4C84E0, 0x5CF610, 0))(message); + } + } + + __declspec(naked) unsigned int find_variable_dedicated(unsigned int parentId, unsigned int name) + { + static DWORD func = 0x4E7ED0; + + __asm + { + mov eax, name + mov ecx, parentId + call func + retn + } + } + + unsigned int FindVariable(const unsigned int parentId, const unsigned int name) + { + if (is_dedi()) + { + return find_variable_dedicated(parentId, name); + } + else + { + return reinterpret_cast // + (SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0))(parentId, name); + } + } + + __declspec(naked) VariableValue get_entity_field_value_dedicated(unsigned int classnum, int entnum, int _offset) + { + static DWORD func = 0x4F1400; + + __asm + { + push _offset + push entnum + mov ecx, classnum + call func + add esp, 8h + retn + } + } + + VariableValue GetEntityFieldValue(const unsigned int classnum, const int entnum, const int offset) + { + if (is_dedi()) + { + return get_entity_field_value_dedicated(classnum, entnum, offset); + } + else + { + return reinterpret_cast // + (SELECT_VALUE(0x530E30, 0x56AF20, 0x0))(classnum, entnum, offset); + } + } + + void* MT_Alloc(const int numBytes, const int type) + { + return scrMemTreeGlob + 12 * size_t(MT_AllocIndex(numBytes, type)); + } + + const float* Scr_AllocVector(const float* v) + { + const auto mem = static_cast(MT_Alloc(16, 2)); + *mem = 0; + + const auto array = reinterpret_cast(mem + 1); + array[0] = v[0]; + array[1] = v[1]; + array[2] = v[2]; + + return array; + } + + void Scr_ClearOutParams() + { + const auto num_params = scr_VmPub->outparamcount; + for (unsigned int i = num_params; i > 0; --i) + { + const auto value = scr_VmPub->top[i - 1]; + RemoveRefToValue(value.type, value.u); + } + + scr_VmPub->top -= num_params; + } + + scr_entref_t Scr_GetEntityIdRef(const unsigned int id) + { + static auto class_array = reinterpret_cast(SELECT_VALUE(0x19AFC84, 0x1E72184, 0x1D3C804)); + static auto ent_array = reinterpret_cast(SELECT_VALUE(0x19AFC82, 0x1E72182, 0x1D3C802)); + + scr_entref_t result{}; + result.raw.classnum = static_cast(class_array[2 * id]) >> 8; + result.raw.entnum = ent_array[4 * id]; + + return result; + } + + scr_call_t Scr_GetFunc(const unsigned int index) + { + if (index > 0x1C7) + { + return scr_instanceFunctions[index]; + } + else + { + return scr_globalFunctions[index]; + } + } + + __declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int stringValue, + unsigned int paramcount) + { + static DWORD func = 0x56B5E0; + + __asm + { + mov eax, paramcount + push stringValue + push id + call func + add esp, 8h + retn + } + } + + __declspec(naked) void scr_notify_id_singleplayer(unsigned int id, unsigned int stringValue, + unsigned int paramcount) + { + static DWORD func = 0x610980; + + __asm + { + mov eax, paramcount + push stringValue + push id + call func + add esp, 8h + retn + } + } + + void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount) + { + if (is_mp()) + { + return scr_notify_id_multiplayer(id, stringValue, paramcount); + } + else if (is_sp()) + { + return scr_notify_id_singleplayer(id, stringValue, paramcount); + } + else + { + return reinterpret_cast(0x4EFAA0)(id, stringValue, paramcount); + } + } + + __declspec(naked) int scr_set_object_field_dedicated(unsigned int classnum, int entnum, int _offset) + { + static DWORD func = 0x4B15C0; + + __asm + { + mov ecx, _offset + mov eax, entnum + push classnum + call func + add esp, 4h + retn + } + } + + int Scr_SetObjectField(const unsigned int classnum, const int entnum, const int offset) + { + if (is_dedi()) + { + return scr_set_object_field_dedicated(classnum, entnum, offset); + } + else + { + return reinterpret_cast // + (SELECT_VALUE(0x42CAD0, 0x52BCC0, 0x0))(classnum, entnum, offset); + } + } + + const char* SL_ConvertToString(const unsigned int stringValue) + { + if (!stringValue) return nullptr; + + static const auto size = is_sp() ? 16 : 12; + return *scrMemTreePub + size * stringValue + 4; + } + + unsigned int SL_GetString(const char* str, const unsigned int user) + { + return SL_GetStringOfSize(str, user, strlen(str) + 1, 7); + } + } + + launcher::mode mode = launcher::mode::none; + + launcher::mode get_mode() + { + if(mode == launcher::mode::none) + { + throw std::runtime_error("Launcher mode not valid. Something must be wrong."); + } + + return mode; + } + + bool is_mp() + { + return get_mode() == launcher::mode::multiplayer; + } + + bool is_sp() + { + return get_mode() == launcher::mode::singleplayer; + } + + bool is_dedi() + { + return get_mode() == launcher::mode::server; + } + + void initialize(const launcher::mode _mode) + { + mode = _mode; + + native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0)); + + native::Com_Error = native::Com_Error_t(SELECT_VALUE(0x425540, 0x555450, 0x4D93F0)); + + native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770)); + + native::Dvar_SetFromStringByName = native::Dvar_SetFromStringByName_t(SELECT_VALUE(0x4DD090, 0x5BF740, 0x518DF0)); + + native::G_RunFrame = native::G_RunFrame_t(SELECT_VALUE(0x52EAA0, 0x50CB70, 0x48AD60)); + + native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0)); + + native::MT_AllocIndex = native::MT_AllocIndex_t(SELECT_VALUE(0x4B9610, 0x562080, 0x4E6C30)); + + native::RemoveRefToValue = native::RemoveRefToValue_t(SELECT_VALUE(0x477EA0, 0x565730, 0x4E8A40)); + + native::SL_GetStringOfSize = native::SL_GetStringOfSize_t(SELECT_VALUE(0x4E13F0, 0x564650, 0x4E7490)); + + native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); + + native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450)); + + native::_longjmp = reinterpret_cast(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558)); + + native::cmd_args = reinterpret_cast(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8)); + native::cmd_argc = reinterpret_cast(SELECT_VALUE(0x1750794, 0x1C97914, 0x1B4563C)); + native::cmd_argv = reinterpret_cast(SELECT_VALUE(0x17507B4, 0x1C97934, 0x1B4565C)); + + native::scrVarGlob = reinterpret_cast(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800)); + native::scrMemTreePub = reinterpret_cast(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4)); + native::scrMemTreeGlob = reinterpret_cast(SELECT_VALUE(0x186DA00, 0x1D6FF00, 0x1C16600)); + + native::scr_VmPub = reinterpret_cast(SELECT_VALUE(0x1BF2580, 0x20B4A80, 0x1F5B080)); + + native::scr_instanceFunctions = reinterpret_cast( SELECT_VALUE(0x184CDB0, 0x1D4F258, + 0x1BF59C8)); + native::scr_globalFunctions = reinterpret_cast( SELECT_VALUE(0x186C68C, 0x1D6EB34, + 0x1C152A4 +)); + + native::g_script_error_level = reinterpret_cast(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058)); + native::g_script_error = reinterpret_cast(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818)); + + native::g_classMap = reinterpret_cast(SELECT_VALUE(0x92D140, 0x8B4300, 0x7C0408)); + + native::levelEntityId = reinterpret_cast(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C)); + } +} diff --git a/src/game/game.hpp b/src/game/game.hpp index 1b02182..16c8931 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -1,95 +1,95 @@ -#pragma once - -#include "structs.hpp" -#include "launcher/launcher.hpp" - -#define SELECT_VALUE(sp, mp, dedi) (game::is_sp() ? (sp) : (game::is_mp() ? (mp) : (dedi))) - -#define SERVER_CD_KEY "Open-IW5-CD-Key" - -namespace game -{ - namespace native - { - typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd); - extern Cmd_AddCommand_t Cmd_AddCommand; - - typedef void (*Com_Error_t)(int code, const char* fmt, ...); - extern Com_Error_t Com_Error; - - typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync); - extern DB_LoadXAssets_t DB_LoadXAssets; - - typedef void (*Dvar_SetFromStringByName_t)(const char *dvarName, const char *string); - extern Dvar_SetFromStringByName_t Dvar_SetFromStringByName; - - typedef int (*G_RunFrame_t)(int, int); - extern G_RunFrame_t G_RunFrame; - - typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len); - extern MSG_ReadData_t MSG_ReadData; - - typedef void* (*MT_AllocIndex_t)(int numBytes, int type); - extern MT_AllocIndex_t MT_AllocIndex; - - typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u); - extern RemoveRefToValue_t RemoveRefToValue; - - typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type); - extern SL_GetStringOfSize_t SL_GetStringOfSize; - - typedef void (*Sys_ShowConsole_t)(); - extern Sys_ShowConsole_t Sys_ShowConsole; - - typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top); - extern VM_Notify_t VM_Notify; - - extern decltype(longjmp)* _longjmp; - - extern int* cmd_args; - extern int* cmd_argc; - extern const char*** cmd_argv; - - extern short* scrVarGlob; - extern char** scrMemTreePub; - extern char* scrMemTreeGlob; - - extern scrVmPub_t* scr_VmPub; - - extern scr_call_t* scr_instanceFunctions; - extern scr_call_t* scr_globalFunctions; - - extern unsigned int* levelEntityId; - - extern int* g_script_error_level; - extern jmp_buf* g_script_error; - - extern scr_classStruct_t* g_classMap; - - void AddRefToValue(VariableValue* value); - - void Conbuf_AppendText(const char* message); - - unsigned int FindVariable(unsigned int parentId, unsigned int name); - - VariableValue GetEntityFieldValue(unsigned int classnum, int entnum, int offset); - - void* MT_Alloc(int numBytes, int type); - - const float* Scr_AllocVector(const float* v); - void Scr_ClearOutParams(); - scr_entref_t Scr_GetEntityIdRef(unsigned int id); - scr_call_t Scr_GetFunc(unsigned int index); - void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount); - int Scr_SetObjectField(unsigned int classnum, int entnum, int offset); - - const char* SL_ConvertToString(unsigned int stringValue); - unsigned int SL_GetString(const char* str, unsigned int user); - } - - bool is_mp(); - bool is_sp(); - bool is_dedi(); - - void initialize(launcher::mode mode); -} +#pragma once + +#include "structs.hpp" +#include "launcher/launcher.hpp" + +#define SELECT_VALUE(sp, mp, dedi) (game::is_sp() ? (sp) : (game::is_mp() ? (mp) : (dedi))) + +#define SERVER_CD_KEY "Open-IW5-CD-Key" + +namespace game +{ + namespace native + { + typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd); + extern Cmd_AddCommand_t Cmd_AddCommand; + + typedef void (*Com_Error_t)(int code, const char* fmt, ...); + extern Com_Error_t Com_Error; + + typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync); + extern DB_LoadXAssets_t DB_LoadXAssets; + + typedef void (*Dvar_SetFromStringByName_t)(const char *dvarName, const char *string); + extern Dvar_SetFromStringByName_t Dvar_SetFromStringByName; + + typedef int (*G_RunFrame_t)(int, int); + extern G_RunFrame_t G_RunFrame; + + typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len); + extern MSG_ReadData_t MSG_ReadData; + + typedef void* (*MT_AllocIndex_t)(int numBytes, int type); + extern MT_AllocIndex_t MT_AllocIndex; + + typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u); + extern RemoveRefToValue_t RemoveRefToValue; + + typedef unsigned int (*SL_GetStringOfSize_t)(const char* str, unsigned int user, unsigned int len, int type); + extern SL_GetStringOfSize_t SL_GetStringOfSize; + + typedef void (*Sys_ShowConsole_t)(); + extern Sys_ShowConsole_t Sys_ShowConsole; + + typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top); + extern VM_Notify_t VM_Notify; + + extern decltype(longjmp)* _longjmp; + + extern int* cmd_args; + extern int* cmd_argc; + extern const char*** cmd_argv; + + extern short* scrVarGlob; + extern char** scrMemTreePub; + extern char* scrMemTreeGlob; + + extern scrVmPub_t* scr_VmPub; + + extern scr_call_t* scr_instanceFunctions; + extern scr_call_t* scr_globalFunctions; + + extern unsigned int* levelEntityId; + + extern int* g_script_error_level; + extern jmp_buf* g_script_error; + + extern scr_classStruct_t* g_classMap; + + void AddRefToValue(VariableValue* value); + + void Conbuf_AppendText(const char* message); + + unsigned int FindVariable(unsigned int parentId, unsigned int name); + + VariableValue GetEntityFieldValue(unsigned int classnum, int entnum, int offset); + + void* MT_Alloc(int numBytes, int type); + + const float* Scr_AllocVector(const float* v); + void Scr_ClearOutParams(); + scr_entref_t Scr_GetEntityIdRef(unsigned int id); + scr_call_t Scr_GetFunc(unsigned int index); + void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount); + int Scr_SetObjectField(unsigned int classnum, int entnum, int offset); + + const char* SL_ConvertToString(unsigned int stringValue); + unsigned int SL_GetString(const char* str, unsigned int user); + } + + bool is_mp(); + bool is_sp(); + bool is_dedi(); + + void initialize(launcher::mode mode); +} diff --git a/src/launcher/html/doc_host_ui_handler.cpp b/src/launcher/html/doc_host_ui_handler.cpp index 515bf1d..c889638 100644 --- a/src/launcher/html/doc_host_ui_handler.cpp +++ b/src/launcher/html/doc_host_ui_handler.cpp @@ -1,115 +1,115 @@ -#include -#include "html_frame.hpp" - -doc_host_ui_handler::doc_host_ui_handler(html_frame* frame): frame_(frame) -{ -} - -HRESULT doc_host_ui_handler::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - auto client_site = this->frame_->get_client_site(); - if (client_site) - { - return client_site->QueryInterface(riid, ppvObj); - } - - return E_NOINTERFACE; -} - -ULONG doc_host_ui_handler::AddRef() -{ - return 1; -} - -ULONG doc_host_ui_handler::Release() -{ - return 1; -} - -HRESULT doc_host_ui_handler::ShowContextMenu(DWORD /*dwID*/, POINT* /*ppt*/, IUnknown* /*pcmdtReserved*/, - IDispatch* /*pdispReserved*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::ShowUI(DWORD /*dwID*/, IOleInPlaceActiveObject* /*pActiveObject*/, - IOleCommandTarget* /*pCommandTarget*/, - IOleInPlaceFrame* /*pFrame*/, IOleInPlaceUIWindow* /*pDoc*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::HideUI() -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::UpdateUI() -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::EnableModeless(BOOL /*fEnable*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::OnDocWindowActivate(BOOL /*fActivate*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::OnFrameWindowActivate(BOOL /*fActivate*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::ResizeBorder(LPCRECT /*prcBorder*/, IOleInPlaceUIWindow* /*pUIWindow*/, - BOOL /*fRameWindow*/) -{ - return S_OK; -} - -HRESULT doc_host_ui_handler::TranslateAcceleratorA(LPMSG /*lpMsg*/, const GUID* pguidCmdGroup, DWORD /*nCmdID*/) -{ - pguidCmdGroup = nullptr; - return S_FALSE; -} - -HRESULT doc_host_ui_handler::GetOptionKeyPath(LPOLESTR* /*pchKey*/, DWORD /*dw*/) -{ - return S_FALSE; -} - -HRESULT doc_host_ui_handler::GetDropTarget(IDropTarget* /*pDropTarget*/, IDropTarget** /*ppDropTarget*/) -{ - return S_FALSE; -} - -HRESULT doc_host_ui_handler::GetExternal(IDispatch** ppDispatch) -{ - *ppDispatch = this->frame_->get_html_dispatch(); - return (*ppDispatch) ? S_OK : S_FALSE; -} - -HRESULT doc_host_ui_handler::FilterDataObject(IDataObject* /*pDO*/, IDataObject** ppDORet) -{ - *ppDORet = nullptr; - return S_FALSE; -} - -HRESULT STDMETHODCALLTYPE doc_host_ui_handler::TranslateUrl(DWORD /*dwTranslate*/, OLECHAR __RPC_FAR* /*pchURLIn*/, - OLECHAR __RPC_FAR* __RPC_FAR* ppchURLOut) -{ - *ppchURLOut = nullptr; - return S_FALSE; -} - -HRESULT doc_host_ui_handler::GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) -{ - pInfo->cbSize = sizeof(DOCHOSTUIINFO); - pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_DPI_AWARE | DOCHOSTUIFLAG_SCROLL_NO; - pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; - - return S_OK; -} +#include +#include "html_frame.hpp" + +doc_host_ui_handler::doc_host_ui_handler(html_frame* frame): frame_(frame) +{ +} + +HRESULT doc_host_ui_handler::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + auto client_site = this->frame_->get_client_site(); + if (client_site) + { + return client_site->QueryInterface(riid, ppvObj); + } + + return E_NOINTERFACE; +} + +ULONG doc_host_ui_handler::AddRef() +{ + return 1; +} + +ULONG doc_host_ui_handler::Release() +{ + return 1; +} + +HRESULT doc_host_ui_handler::ShowContextMenu(DWORD /*dwID*/, POINT* /*ppt*/, IUnknown* /*pcmdtReserved*/, + IDispatch* /*pdispReserved*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::ShowUI(DWORD /*dwID*/, IOleInPlaceActiveObject* /*pActiveObject*/, + IOleCommandTarget* /*pCommandTarget*/, + IOleInPlaceFrame* /*pFrame*/, IOleInPlaceUIWindow* /*pDoc*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::HideUI() +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::UpdateUI() +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::EnableModeless(BOOL /*fEnable*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::OnDocWindowActivate(BOOL /*fActivate*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::OnFrameWindowActivate(BOOL /*fActivate*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::ResizeBorder(LPCRECT /*prcBorder*/, IOleInPlaceUIWindow* /*pUIWindow*/, + BOOL /*fRameWindow*/) +{ + return S_OK; +} + +HRESULT doc_host_ui_handler::TranslateAcceleratorA(LPMSG /*lpMsg*/, const GUID* pguidCmdGroup, DWORD /*nCmdID*/) +{ + pguidCmdGroup = nullptr; + return S_FALSE; +} + +HRESULT doc_host_ui_handler::GetOptionKeyPath(LPOLESTR* /*pchKey*/, DWORD /*dw*/) +{ + return S_FALSE; +} + +HRESULT doc_host_ui_handler::GetDropTarget(IDropTarget* /*pDropTarget*/, IDropTarget** /*ppDropTarget*/) +{ + return S_FALSE; +} + +HRESULT doc_host_ui_handler::GetExternal(IDispatch** ppDispatch) +{ + *ppDispatch = this->frame_->get_html_dispatch(); + return (*ppDispatch) ? S_OK : S_FALSE; +} + +HRESULT doc_host_ui_handler::FilterDataObject(IDataObject* /*pDO*/, IDataObject** ppDORet) +{ + *ppDORet = nullptr; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE doc_host_ui_handler::TranslateUrl(DWORD /*dwTranslate*/, OLECHAR __RPC_FAR* /*pchURLIn*/, + OLECHAR __RPC_FAR* __RPC_FAR* ppchURLOut) +{ + *ppchURLOut = nullptr; + return S_FALSE; +} + +HRESULT doc_host_ui_handler::GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) +{ + pInfo->cbSize = sizeof(DOCHOSTUIINFO); + pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_DPI_AWARE | DOCHOSTUIFLAG_SCROLL_NO; + pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; + + return S_OK; +} diff --git a/src/launcher/html/doc_host_ui_handler.hpp b/src/launcher/html/doc_host_ui_handler.hpp index 0b538b4..2313388 100644 --- a/src/launcher/html/doc_host_ui_handler.hpp +++ b/src/launcher/html/doc_host_ui_handler.hpp @@ -1,47 +1,47 @@ -#pragma once - -class html_frame; - -class doc_host_ui_handler final : public IDocHostUIHandler -{ -public: - doc_host_ui_handler(html_frame* frame); - virtual ~doc_host_ui_handler() = default; - -private: - html_frame* frame_; - -public: // IDocHostUIHandler interface - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - HRESULT STDMETHODCALLTYPE ShowContextMenu( - DWORD dwID, - POINT __RPC_FAR * ppt, - IUnknown __RPC_FAR * pcmdtReserved, - IDispatch __RPC_FAR * pdispReserved) override; - HRESULT STDMETHODCALLTYPE ShowUI( - DWORD dwID, - IOleInPlaceActiveObject __RPC_FAR * pActiveObject, - IOleCommandTarget __RPC_FAR * pCommandTarget, - IOleInPlaceFrame __RPC_FAR * pFrame, - IOleInPlaceUIWindow __RPC_FAR * pDoc) override; - HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) override; - HRESULT STDMETHODCALLTYPE HideUI() override; - HRESULT STDMETHODCALLTYPE UpdateUI() override; - HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override; - HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) override; - HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) override; - HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR * pUIWindow, - BOOL fRameWindow) override; - HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR * pguidCmdGroup, DWORD nCmdID) - override; - HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR __RPC_FAR * pchKey, DWORD dw) override; - HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget __RPC_FAR * pDropTarget, - IDropTarget __RPC_FAR *__RPC_FAR * ppDropTarget) override; - HRESULT STDMETHODCALLTYPE GetExternal(IDispatch __RPC_FAR *__RPC_FAR * ppDispatch) override; - HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR * pchURLIn, - OLECHAR __RPC_FAR *__RPC_FAR * ppchURLOut) override; - HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject __RPC_FAR * pDO, IDataObject __RPC_FAR *__RPC_FAR * ppDORet) - override; -}; +#pragma once + +class html_frame; + +class doc_host_ui_handler final : public IDocHostUIHandler +{ +public: + doc_host_ui_handler(html_frame* frame); + virtual ~doc_host_ui_handler() = default; + +private: + html_frame* frame_; + +public: // IDocHostUIHandler interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE ShowContextMenu( + DWORD dwID, + POINT __RPC_FAR * ppt, + IUnknown __RPC_FAR * pcmdtReserved, + IDispatch __RPC_FAR * pdispReserved) override; + HRESULT STDMETHODCALLTYPE ShowUI( + DWORD dwID, + IOleInPlaceActiveObject __RPC_FAR * pActiveObject, + IOleCommandTarget __RPC_FAR * pCommandTarget, + IOleInPlaceFrame __RPC_FAR * pFrame, + IOleInPlaceUIWindow __RPC_FAR * pDoc) override; + HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO __RPC_FAR * pInfo) override; + HRESULT STDMETHODCALLTYPE HideUI() override; + HRESULT STDMETHODCALLTYPE UpdateUI() override; + HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override; + HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate) override; + HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate) override; + HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR * pUIWindow, + BOOL fRameWindow) override; + HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR * pguidCmdGroup, DWORD nCmdID) + override; + HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR __RPC_FAR * pchKey, DWORD dw) override; + HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget __RPC_FAR * pDropTarget, + IDropTarget __RPC_FAR *__RPC_FAR * ppDropTarget) override; + HRESULT STDMETHODCALLTYPE GetExternal(IDispatch __RPC_FAR *__RPC_FAR * ppDispatch) override; + HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR * pchURLIn, + OLECHAR __RPC_FAR *__RPC_FAR * ppchURLOut) override; + HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject __RPC_FAR * pDO, IDataObject __RPC_FAR *__RPC_FAR * ppDORet) + override; +}; diff --git a/src/launcher/html/html_argument.cpp b/src/launcher/html/html_argument.cpp index ba79db5..d169b54 100644 --- a/src/launcher/html/html_argument.cpp +++ b/src/launcher/html/html_argument.cpp @@ -1,48 +1,48 @@ -#include -#include "html_argument.hpp" - -html_argument::html_argument(VARIANT* val) : value_(val) -{ -} - -bool html_argument::is_empty() const -{ - return this->value_ == nullptr || this->value_->vt == VT_EMPTY; -} - -bool html_argument::is_string() const -{ - if (this->is_empty()) return false; - return this->value_->vt == VT_BSTR; -} - -bool html_argument::is_number() const -{ - if (this->is_empty()) return false; - return this->value_->vt == VT_I4; -} - -bool html_argument::is_bool() const -{ - if (this->is_empty()) return false; - return this->value_->vt == VT_BOOL; -} - -std::string html_argument::get_string() const -{ - if (!this->is_string()) return {}; - std::wstring wide_string(this->value_->bstrVal); - return std::string(wide_string.begin(), wide_string.end()); -} - -int html_argument::get_number() const -{ - if (!this->is_number()) return 0; - return this->value_->intVal; -} - -bool html_argument::get_bool() const -{ - if(!this->is_bool()) return false; - return this->value_->boolVal != FALSE; -} +#include +#include "html_argument.hpp" + +html_argument::html_argument(VARIANT* val) : value_(val) +{ +} + +bool html_argument::is_empty() const +{ + return this->value_ == nullptr || this->value_->vt == VT_EMPTY; +} + +bool html_argument::is_string() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_BSTR; +} + +bool html_argument::is_number() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_I4; +} + +bool html_argument::is_bool() const +{ + if (this->is_empty()) return false; + return this->value_->vt == VT_BOOL; +} + +std::string html_argument::get_string() const +{ + if (!this->is_string()) return {}; + std::wstring wide_string(this->value_->bstrVal); + return std::string(wide_string.begin(), wide_string.end()); +} + +int html_argument::get_number() const +{ + if (!this->is_number()) return 0; + return this->value_->intVal; +} + +bool html_argument::get_bool() const +{ + if(!this->is_bool()) return false; + return this->value_->boolVal != FALSE; +} diff --git a/src/launcher/html/html_argument.hpp b/src/launcher/html/html_argument.hpp index 6e460e2..3c10502 100644 --- a/src/launcher/html/html_argument.hpp +++ b/src/launcher/html/html_argument.hpp @@ -1,20 +1,20 @@ -#pragma once - -class html_argument final -{ -public: - html_argument(VARIANT* val); - - bool is_empty() const; - - bool is_string() const; - bool is_number() const; - bool is_bool() const; - - std::string get_string() const; - int get_number() const; - bool get_bool() const; - -private: - VARIANT* value_; -}; +#pragma once + +class html_argument final +{ +public: + html_argument(VARIANT* val); + + bool is_empty() const; + + bool is_string() const; + bool is_number() const; + bool is_bool() const; + + std::string get_string() const; + int get_number() const; + bool get_bool() const; + +private: + VARIANT* value_; +}; diff --git a/src/launcher/html/html_dispatch.cpp b/src/launcher/html/html_dispatch.cpp index 0cbea58..f47bfe7 100644 --- a/src/launcher/html/html_dispatch.cpp +++ b/src/launcher/html/html_dispatch.cpp @@ -1,61 +1,61 @@ -#include -#include "html_frame.hpp" - -html_dispatch::html_dispatch(html_frame* frame) : frame_(frame) -{ -} - -HRESULT html_dispatch::QueryInterface(const IID& riid, LPVOID* ppvObj) -{ - if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) || - !memcmp(&riid, &IID_IDispatch, sizeof(GUID))) - { - *ppvObj = this; - this->AddRef(); - return S_OK; - } - - *ppvObj = nullptr; - return E_NOINTERFACE; -} - -ULONG html_dispatch::AddRef() -{ - return 1; -} - -ULONG html_dispatch::Release() -{ - return 1; -} - -HRESULT html_dispatch::GetTypeInfoCount(UINT* pctinfo) -{ - return S_FALSE; -} - -HRESULT html_dispatch::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) -{ - return S_FALSE; -} - -HRESULT html_dispatch::GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) -{ - for (unsigned int i = 0; i < cNames; ++i) - { - std::wstring wide_name(rgszNames[i]); - std::string name(wide_name.begin(), wide_name.end()); - - rgDispId[i] = this->frame_->get_callback_id(name); - } - - return S_OK; -} - -HRESULT html_dispatch::Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, - VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) -{ - html_frame::callback_params params(pDispParams, pVarResult); - this->frame_->invoke_callback(dispIdMember, ¶ms); - return S_OK; -} +#include +#include "html_frame.hpp" + +html_dispatch::html_dispatch(html_frame* frame) : frame_(frame) +{ +} + +HRESULT html_dispatch::QueryInterface(const IID& riid, LPVOID* ppvObj) +{ + if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) || + !memcmp(&riid, &IID_IDispatch, sizeof(GUID))) + { + *ppvObj = this; + this->AddRef(); + return S_OK; + } + + *ppvObj = nullptr; + return E_NOINTERFACE; +} + +ULONG html_dispatch::AddRef() +{ + return 1; +} + +ULONG html_dispatch::Release() +{ + return 1; +} + +HRESULT html_dispatch::GetTypeInfoCount(UINT* pctinfo) +{ + return S_FALSE; +} + +HRESULT html_dispatch::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) +{ + return S_FALSE; +} + +HRESULT html_dispatch::GetIDsOfNames(const IID& riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) +{ + for (unsigned int i = 0; i < cNames; ++i) + { + std::wstring wide_name(rgszNames[i]); + std::string name(wide_name.begin(), wide_name.end()); + + rgDispId[i] = this->frame_->get_callback_id(name); + } + + return S_OK; +} + +HRESULT html_dispatch::Invoke(DISPID dispIdMember, const IID& riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, + VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) +{ + html_frame::callback_params params(pDispParams, pVarResult); + this->frame_->invoke_callback(dispIdMember, ¶ms); + return S_OK; +} diff --git a/src/launcher/html/html_dispatch.hpp b/src/launcher/html/html_dispatch.hpp index 869af7a..671c843 100644 --- a/src/launcher/html/html_dispatch.hpp +++ b/src/launcher/html/html_dispatch.hpp @@ -1,24 +1,24 @@ -#pragma once - -class html_frame; - -class html_dispatch final : public IDispatch -{ -public: - html_dispatch(html_frame* frame); - virtual ~html_dispatch() = default; - -private: - html_frame* frame_; - -public: // IDispatch interface - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* pctinfo) override; - HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override; - HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) - override; - HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, - VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override; -}; +#pragma once + +class html_frame; + +class html_dispatch final : public IDispatch +{ +public: + html_dispatch(html_frame* frame); + virtual ~html_dispatch() = default; + +private: + html_frame* frame_; + +public: // IDispatch interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* pctinfo) override; + HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) override; + HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) + override; + HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, + VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) override; +}; diff --git a/src/launcher/html/html_frame.cpp b/src/launcher/html/html_frame.cpp index d3f53de..69510ad 100644 --- a/src/launcher/html/html_frame.cpp +++ b/src/launcher/html/html_frame.cpp @@ -1,262 +1,262 @@ -#include -#include "html_frame.hpp" -#include "utils/nt.hpp" - -std::atomic html_frame::frame_count_ = 0; - -html_frame::callback_params::callback_params(DISPPARAMS* params, VARIANT* res) : result(res) -{ - for (auto i = params->cArgs; i > 0; --i) - { - auto param = ¶ms->rgvarg[i - 1]; - this->arguments.emplace_back(param); - } -} - -html_frame::html_frame() : in_place_frame_(this), in_place_site_(this), ui_handler_(this), client_site_(this), - html_dispatch_(this) -{ - if (frame_count_++ == 0 && OleInitialize(nullptr) != S_OK) - { - throw std::runtime_error("Unable to initialize the OLE library"); - } - - set_browser_feature("FEATURE_BROWSER_EMULATION", 11000); - set_browser_feature("FEATURE_GPU_RENDERING", 1); -} - -html_frame::~html_frame() -{ - if (--frame_count_ <= 0) - { - frame_count_ = 0; - OleUninitialize(); - } -} - -void html_frame::object_deleter(IUnknown* object) -{ - if (object) - { - object->Release(); - } -} - -HWND html_frame::get_window() const -{ - return this->window_; -} - -std::shared_ptr html_frame::get_browser_object() const -{ - return this->browser_object_; -} - -ole_in_place_frame* html_frame::get_in_place_frame() -{ - return &this->in_place_frame_; -} - -ole_in_place_site* html_frame::get_in_place_site() -{ - return &this->in_place_site_; -} - -doc_host_ui_handler* html_frame::get_ui_handler() -{ - return &this->ui_handler_; -} - -ole_client_site* html_frame::get_client_site() -{ - return &this->client_site_; -} - -html_dispatch* html_frame::get_html_dispatch() -{ - return &this->html_dispatch_; -} - -std::shared_ptr html_frame::get_web_browser() const -{ - if (!this->browser_object_) return {}; - - IWebBrowser2* web_browser = nullptr; - if (this->browser_object_->QueryInterface(IID_IWebBrowser2, reinterpret_cast(&web_browser)) || !web_browser) - return {}; - - return std::shared_ptr(web_browser, object_deleter); -} - -std::shared_ptr html_frame::get_dispatch() const -{ - const auto web_browser = this->get_web_browser(); - if (!web_browser) return {}; - - IDispatch* dispatch = nullptr; - if (web_browser->get_Document(&dispatch) || !dispatch) return {}; - - return std::shared_ptr(dispatch, object_deleter); -} - -std::shared_ptr html_frame::get_document() const -{ - const auto dispatch = this->get_dispatch(); - if (!dispatch) return {}; - - IHTMLDocument2* document = nullptr; - if (dispatch->QueryInterface(IID_IHTMLDocument2, reinterpret_cast(&document)) || !document) return {}; - - return std::shared_ptr(document, object_deleter); -} - -void html_frame::initialize(const HWND window) -{ - if (this->window_) return; - this->window_ = window; - - this->create_browser(); - this->initialize_browser(); -} - -void html_frame::create_browser() -{ - LPCLASSFACTORY class_factory = nullptr; - if (CoGetClassObject(CLSID_WebBrowser, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, nullptr, IID_IClassFactory, - reinterpret_cast(&class_factory)) || !class_factory) - { - throw std::runtime_error("Unable to get the class factory"); - } - - IOleObject* browser_object = nullptr; - class_factory->CreateInstance(nullptr, IID_IOleObject, reinterpret_cast(&browser_object)); - class_factory->Release(); - - if (!browser_object) - { - throw std::runtime_error("Unable to create browser object"); - } - - this->browser_object_ = std::shared_ptr(browser_object, [](IOleObject* browser_object) - { - if (browser_object) - { - browser_object->Close(OLECLOSE_NOSAVE); - object_deleter(browser_object); - } - }); -} - -void html_frame::initialize_browser() -{ - this->browser_object_->SetClientSite(this->get_client_site()); - this->browser_object_->SetHostNames(L"Hostname", nullptr); - - RECT rect; - GetClientRect(this->get_window(), &rect); - OleSetContainedObject(this->browser_object_.get(), TRUE); - - this->browser_object_->DoVerb(OLEIVERB_SHOW, nullptr, this->get_client_site(), -1, this->get_window(), &rect); - this->resize(rect.right, rect.bottom); -} - -void html_frame::set_browser_feature(const std::string& feature, DWORD value) -{ - const auto registry_path = R"(SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\)" + feature; - - HKEY key = nullptr; - if (RegOpenKeyExA( - HKEY_CURRENT_USER, registry_path.data(), 0, - KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) - return; - - const utils::nt::module self; - const auto name = self.get_name(); - RegSetValueExA(key, name.data(), 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); - - RegCloseKey(key); -} - -void html_frame::resize(const DWORD width, const DWORD height) const -{ - auto web_browser = this->get_web_browser(); - if (web_browser) - { - web_browser->put_Left(0); - web_browser->put_Top(0); - web_browser->put_Width(width); - web_browser->put_Height(height); - } -} - -bool html_frame::load_url(const std::string& url) const -{ - auto web_browser = this->get_web_browser(); - if (!web_browser) return false; - - std::wstring wide_url(url.begin(), url.end()); - - VARIANT my_url; - VariantInit(&my_url); - my_url.vt = VT_BSTR; - my_url.bstrVal = SysAllocString(wide_url.data()); - - const auto _ = gsl::finally([&my_url]() { VariantClear(&my_url); }); - if (!my_url.bstrVal) return false; - - return SUCCEEDED(web_browser->Navigate2(&my_url, nullptr, nullptr, nullptr, nullptr)); -} - -bool html_frame::load_html(const std::string& html) const -{ - if (!this->load_url("about:blank")) return false; - - const auto document = this->get_document(); - if (!document) return false; - - SAFEARRAYBOUND safe_array_bound = {1, 0}; - auto safe_array = SafeArrayCreate(VT_VARIANT, 1, &safe_array_bound); - if (!safe_array) return false; - - const auto _ = gsl::finally([safe_array]() { SafeArrayDestroy(safe_array); }); - - VARIANT* variant = nullptr; - if (SafeArrayAccessData(safe_array, reinterpret_cast(&variant)) || !variant) return false; - - std::wstring wide_html(html.begin(), html.end()); - - variant->vt = VT_BSTR; - variant->bstrVal = SysAllocString(wide_html.data()); - if (!variant->bstrVal) return false; - - document->write(safe_array); - document->close(); - - return true; -} - -int html_frame::get_callback_id(const std::string& name) -{ - for (auto i = 0u; i < this->callbacks_.size(); ++i) - { - if (this->callbacks_[i].first == name) - { - return i; - } - } - - return -1; -} - -void html_frame::invoke_callback(const int id, callback_params* params) -{ - if (id >= 0 && static_cast(id) < this->callbacks_.size()) - { - this->callbacks_[id].second(params); - } -} - -void html_frame::register_callback(const std::string& name, const std::function& callback) -{ - this->callbacks_.emplace_back(name, callback); -} +#include +#include "html_frame.hpp" +#include "utils/nt.hpp" + +std::atomic html_frame::frame_count_ = 0; + +html_frame::callback_params::callback_params(DISPPARAMS* params, VARIANT* res) : result(res) +{ + for (auto i = params->cArgs; i > 0; --i) + { + auto param = ¶ms->rgvarg[i - 1]; + this->arguments.emplace_back(param); + } +} + +html_frame::html_frame() : in_place_frame_(this), in_place_site_(this), ui_handler_(this), client_site_(this), + html_dispatch_(this) +{ + if (frame_count_++ == 0 && OleInitialize(nullptr) != S_OK) + { + throw std::runtime_error("Unable to initialize the OLE library"); + } + + set_browser_feature("FEATURE_BROWSER_EMULATION", 11000); + set_browser_feature("FEATURE_GPU_RENDERING", 1); +} + +html_frame::~html_frame() +{ + if (--frame_count_ <= 0) + { + frame_count_ = 0; + OleUninitialize(); + } +} + +void html_frame::object_deleter(IUnknown* object) +{ + if (object) + { + object->Release(); + } +} + +HWND html_frame::get_window() const +{ + return this->window_; +} + +std::shared_ptr html_frame::get_browser_object() const +{ + return this->browser_object_; +} + +ole_in_place_frame* html_frame::get_in_place_frame() +{ + return &this->in_place_frame_; +} + +ole_in_place_site* html_frame::get_in_place_site() +{ + return &this->in_place_site_; +} + +doc_host_ui_handler* html_frame::get_ui_handler() +{ + return &this->ui_handler_; +} + +ole_client_site* html_frame::get_client_site() +{ + return &this->client_site_; +} + +html_dispatch* html_frame::get_html_dispatch() +{ + return &this->html_dispatch_; +} + +std::shared_ptr html_frame::get_web_browser() const +{ + if (!this->browser_object_) return {}; + + IWebBrowser2* web_browser = nullptr; + if (this->browser_object_->QueryInterface(IID_IWebBrowser2, reinterpret_cast(&web_browser)) || !web_browser) + return {}; + + return std::shared_ptr(web_browser, object_deleter); +} + +std::shared_ptr html_frame::get_dispatch() const +{ + const auto web_browser = this->get_web_browser(); + if (!web_browser) return {}; + + IDispatch* dispatch = nullptr; + if (web_browser->get_Document(&dispatch) || !dispatch) return {}; + + return std::shared_ptr(dispatch, object_deleter); +} + +std::shared_ptr html_frame::get_document() const +{ + const auto dispatch = this->get_dispatch(); + if (!dispatch) return {}; + + IHTMLDocument2* document = nullptr; + if (dispatch->QueryInterface(IID_IHTMLDocument2, reinterpret_cast(&document)) || !document) return {}; + + return std::shared_ptr(document, object_deleter); +} + +void html_frame::initialize(const HWND window) +{ + if (this->window_) return; + this->window_ = window; + + this->create_browser(); + this->initialize_browser(); +} + +void html_frame::create_browser() +{ + LPCLASSFACTORY class_factory = nullptr; + if (CoGetClassObject(CLSID_WebBrowser, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, nullptr, IID_IClassFactory, + reinterpret_cast(&class_factory)) || !class_factory) + { + throw std::runtime_error("Unable to get the class factory"); + } + + IOleObject* browser_object = nullptr; + class_factory->CreateInstance(nullptr, IID_IOleObject, reinterpret_cast(&browser_object)); + class_factory->Release(); + + if (!browser_object) + { + throw std::runtime_error("Unable to create browser object"); + } + + this->browser_object_ = std::shared_ptr(browser_object, [](IOleObject* browser_object) + { + if (browser_object) + { + browser_object->Close(OLECLOSE_NOSAVE); + object_deleter(browser_object); + } + }); +} + +void html_frame::initialize_browser() +{ + this->browser_object_->SetClientSite(this->get_client_site()); + this->browser_object_->SetHostNames(L"Hostname", nullptr); + + RECT rect; + GetClientRect(this->get_window(), &rect); + OleSetContainedObject(this->browser_object_.get(), TRUE); + + this->browser_object_->DoVerb(OLEIVERB_SHOW, nullptr, this->get_client_site(), -1, this->get_window(), &rect); + this->resize(rect.right, rect.bottom); +} + +void html_frame::set_browser_feature(const std::string& feature, DWORD value) +{ + const auto registry_path = R"(SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\)" + feature; + + HKEY key = nullptr; + if (RegOpenKeyExA( + HKEY_CURRENT_USER, registry_path.data(), 0, + KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) + return; + + const utils::nt::module self; + const auto name = self.get_name(); + RegSetValueExA(key, name.data(), 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); + + RegCloseKey(key); +} + +void html_frame::resize(const DWORD width, const DWORD height) const +{ + auto web_browser = this->get_web_browser(); + if (web_browser) + { + web_browser->put_Left(0); + web_browser->put_Top(0); + web_browser->put_Width(width); + web_browser->put_Height(height); + } +} + +bool html_frame::load_url(const std::string& url) const +{ + auto web_browser = this->get_web_browser(); + if (!web_browser) return false; + + std::wstring wide_url(url.begin(), url.end()); + + VARIANT my_url; + VariantInit(&my_url); + my_url.vt = VT_BSTR; + my_url.bstrVal = SysAllocString(wide_url.data()); + + const auto _ = gsl::finally([&my_url]() { VariantClear(&my_url); }); + if (!my_url.bstrVal) return false; + + return SUCCEEDED(web_browser->Navigate2(&my_url, nullptr, nullptr, nullptr, nullptr)); +} + +bool html_frame::load_html(const std::string& html) const +{ + if (!this->load_url("about:blank")) return false; + + const auto document = this->get_document(); + if (!document) return false; + + SAFEARRAYBOUND safe_array_bound = {1, 0}; + auto safe_array = SafeArrayCreate(VT_VARIANT, 1, &safe_array_bound); + if (!safe_array) return false; + + const auto _ = gsl::finally([safe_array]() { SafeArrayDestroy(safe_array); }); + + VARIANT* variant = nullptr; + if (SafeArrayAccessData(safe_array, reinterpret_cast(&variant)) || !variant) return false; + + std::wstring wide_html(html.begin(), html.end()); + + variant->vt = VT_BSTR; + variant->bstrVal = SysAllocString(wide_html.data()); + if (!variant->bstrVal) return false; + + document->write(safe_array); + document->close(); + + return true; +} + +int html_frame::get_callback_id(const std::string& name) +{ + for (auto i = 0u; i < this->callbacks_.size(); ++i) + { + if (this->callbacks_[i].first == name) + { + return i; + } + } + + return -1; +} + +void html_frame::invoke_callback(const int id, callback_params* params) +{ + if (id >= 0 && static_cast(id) < this->callbacks_.size()) + { + this->callbacks_[id].second(params); + } +} + +void html_frame::register_callback(const std::string& name, const std::function& callback) +{ + this->callbacks_.emplace_back(name, callback); +} diff --git a/src/launcher/html/html_frame.hpp b/src/launcher/html/html_frame.hpp index 35eac17..6f582ea 100644 --- a/src/launcher/html/html_frame.hpp +++ b/src/launcher/html/html_frame.hpp @@ -1,67 +1,67 @@ -#pragma once -#include "ole_in_place_frame.hpp" -#include "ole_in_place_site.hpp" -#include "doc_host_ui_handler.hpp" -#include "ole_client_site.hpp" -#include "html_dispatch.hpp" -#include "html_argument.hpp" - -class html_frame -{ -public: - class callback_params final - { - public: - callback_params(DISPPARAMS* params, VARIANT* res); - - std::vector arguments; - html_argument result; - }; - - html_frame(); - virtual ~html_frame(); - - void initialize(HWND window); - - void resize(DWORD width, DWORD height) const; - bool load_url(const std::string& url) const; - bool load_html(const std::string& html) const; - - HWND get_window() const; - - std::shared_ptr get_browser_object() const; - std::shared_ptr get_web_browser() const; - std::shared_ptr get_dispatch() const; - std::shared_ptr get_document() const; - - ole_in_place_frame* get_in_place_frame(); - ole_in_place_site* get_in_place_site(); - doc_host_ui_handler* get_ui_handler(); - ole_client_site* get_client_site(); - html_dispatch* get_html_dispatch(); - - int get_callback_id(const std::string& name); - void invoke_callback(int id, callback_params* params); - - void register_callback(const std::string& name, const std::function& callback); - -private: - HWND window_ = nullptr; - std::shared_ptr browser_object_; - - ole_in_place_frame in_place_frame_; - ole_in_place_site in_place_site_; - doc_host_ui_handler ui_handler_; - ole_client_site client_site_; - html_dispatch html_dispatch_; - - std::vector>> callbacks_; - - void create_browser(); - void initialize_browser(); - - static void set_browser_feature(const std::string& feature, DWORD value); - static void object_deleter(IUnknown* object); - - static std::atomic frame_count_; -}; +#pragma once +#include "ole_in_place_frame.hpp" +#include "ole_in_place_site.hpp" +#include "doc_host_ui_handler.hpp" +#include "ole_client_site.hpp" +#include "html_dispatch.hpp" +#include "html_argument.hpp" + +class html_frame +{ +public: + class callback_params final + { + public: + callback_params(DISPPARAMS* params, VARIANT* res); + + std::vector arguments; + html_argument result; + }; + + html_frame(); + virtual ~html_frame(); + + void initialize(HWND window); + + void resize(DWORD width, DWORD height) const; + bool load_url(const std::string& url) const; + bool load_html(const std::string& html) const; + + HWND get_window() const; + + std::shared_ptr get_browser_object() const; + std::shared_ptr get_web_browser() const; + std::shared_ptr get_dispatch() const; + std::shared_ptr get_document() const; + + ole_in_place_frame* get_in_place_frame(); + ole_in_place_site* get_in_place_site(); + doc_host_ui_handler* get_ui_handler(); + ole_client_site* get_client_site(); + html_dispatch* get_html_dispatch(); + + int get_callback_id(const std::string& name); + void invoke_callback(int id, callback_params* params); + + void register_callback(const std::string& name, const std::function& callback); + +private: + HWND window_ = nullptr; + std::shared_ptr browser_object_; + + ole_in_place_frame in_place_frame_; + ole_in_place_site in_place_site_; + doc_host_ui_handler ui_handler_; + ole_client_site client_site_; + html_dispatch html_dispatch_; + + std::vector>> callbacks_; + + void create_browser(); + void initialize_browser(); + + static void set_browser_feature(const std::string& feature, DWORD value); + static void object_deleter(IUnknown* object); + + static std::atomic frame_count_; +}; diff --git a/src/launcher/html/html_window.cpp b/src/launcher/html/html_window.cpp index bf01098..5b075b5 100644 --- a/src/launcher/html/html_window.cpp +++ b/src/launcher/html/html_window.cpp @@ -1,29 +1,29 @@ -#include -#include "html_window.hpp" - -window* html_window::get_window() -{ - return this; -} - -html_frame* html_window::get_html_frame() -{ - return this; -} - -LRESULT html_window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) -{ - if (message == WM_SIZE) - { - this->resize(LOWORD(l_param), HIWORD(l_param)); - return 0; - } - - if (message == WM_CREATE) - { - this->initialize(*this); - return 0; - } - - return window::processor(message, w_param, l_param); -} +#include +#include "html_window.hpp" + +window* html_window::get_window() +{ + return this; +} + +html_frame* html_window::get_html_frame() +{ + return this; +} + +LRESULT html_window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) +{ + if (message == WM_SIZE) + { + this->resize(LOWORD(l_param), HIWORD(l_param)); + return 0; + } + + if (message == WM_CREATE) + { + this->initialize(*this); + return 0; + } + + return window::processor(message, w_param, l_param); +} diff --git a/src/launcher/html/html_window.hpp b/src/launcher/html/html_window.hpp index 5b9f25b..7c99280 100644 --- a/src/launcher/html/html_window.hpp +++ b/src/launcher/html/html_window.hpp @@ -1,15 +1,15 @@ -#pragma once -#include "../window.hpp" -#include "html_frame.hpp" - -class html_window final : public window, public html_frame -{ -public: - ~html_window() = default; - - window* get_window(); - html_frame* get_html_frame(); - -private: - LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param) override; -}; +#pragma once +#include "../window.hpp" +#include "html_frame.hpp" + +class html_window final : public window, public html_frame +{ +public: + ~html_window() = default; + + window* get_window(); + html_frame* get_html_frame(); + +private: + LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param) override; +}; diff --git a/src/launcher/html/ole_client_site.cpp b/src/launcher/html/ole_client_site.cpp index 6e3914e..8030390 100644 --- a/src/launcher/html/ole_client_site.cpp +++ b/src/launcher/html/ole_client_site.cpp @@ -1,77 +1,77 @@ -#include -#include "html_frame.hpp" - -ole_client_site::ole_client_site(html_frame* frame): frame_(frame) -{ -} - -HRESULT ole_client_site::QueryInterface(REFIID riid, LPVOID* ppvObject) -{ - if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) || - !memcmp(&riid, &IID_IOleClientSite, sizeof(GUID))) - { - *ppvObject = this; - this->AddRef(); - return S_OK; - } - - if (!memcmp(&riid, &IID_IOleInPlaceSite, sizeof(GUID))) - { - auto in_place_site = this->frame_->get_in_place_site(); - in_place_site->AddRef(); - *ppvObject = in_place_site; - return S_OK; - } - - if (!memcmp(&riid, &IID_IDocHostUIHandler, sizeof(GUID))) - { - auto ui_handler = this->frame_->get_ui_handler(); - ui_handler->AddRef(); - *ppvObject = ui_handler; - return S_OK; - } - - *ppvObject = nullptr; - return E_NOINTERFACE; -} - -ULONG ole_client_site::AddRef() -{ - return 1; -} - -ULONG ole_client_site::Release() -{ - return 1; -} - -HRESULT ole_client_site::SaveObject() -{ - return E_NOTIMPL; -} - -HRESULT ole_client_site::GetMoniker(DWORD /*dwAssign*/, DWORD /*dwWhichMoniker*/, IMoniker** /*ppmk*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_client_site::GetContainer(LPOLECONTAINER* ppContainer) -{ - *ppContainer = nullptr; - return E_NOINTERFACE; -} - -HRESULT ole_client_site::ShowObject() -{ - return NOERROR; -} - -HRESULT ole_client_site::OnShowWindow(BOOL /*fShow*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_client_site::RequestNewObjectLayout() -{ - return E_NOTIMPL; -} +#include +#include "html_frame.hpp" + +ole_client_site::ole_client_site(html_frame* frame): frame_(frame) +{ +} + +HRESULT ole_client_site::QueryInterface(REFIID riid, LPVOID* ppvObject) +{ + if (!memcmp(&riid, &IID_IUnknown, sizeof(GUID)) || + !memcmp(&riid, &IID_IOleClientSite, sizeof(GUID))) + { + *ppvObject = this; + this->AddRef(); + return S_OK; + } + + if (!memcmp(&riid, &IID_IOleInPlaceSite, sizeof(GUID))) + { + auto in_place_site = this->frame_->get_in_place_site(); + in_place_site->AddRef(); + *ppvObject = in_place_site; + return S_OK; + } + + if (!memcmp(&riid, &IID_IDocHostUIHandler, sizeof(GUID))) + { + auto ui_handler = this->frame_->get_ui_handler(); + ui_handler->AddRef(); + *ppvObject = ui_handler; + return S_OK; + } + + *ppvObject = nullptr; + return E_NOINTERFACE; +} + +ULONG ole_client_site::AddRef() +{ + return 1; +} + +ULONG ole_client_site::Release() +{ + return 1; +} + +HRESULT ole_client_site::SaveObject() +{ + return E_NOTIMPL; +} + +HRESULT ole_client_site::GetMoniker(DWORD /*dwAssign*/, DWORD /*dwWhichMoniker*/, IMoniker** /*ppmk*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_client_site::GetContainer(LPOLECONTAINER* ppContainer) +{ + *ppContainer = nullptr; + return E_NOINTERFACE; +} + +HRESULT ole_client_site::ShowObject() +{ + return NOERROR; +} + +HRESULT ole_client_site::OnShowWindow(BOOL /*fShow*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_client_site::RequestNewObjectLayout() +{ + return E_NOTIMPL; +} diff --git a/src/launcher/html/ole_client_site.hpp b/src/launcher/html/ole_client_site.hpp index d0adc80..bd06193 100644 --- a/src/launcher/html/ole_client_site.hpp +++ b/src/launcher/html/ole_client_site.hpp @@ -1,24 +1,24 @@ -#pragma once - -class html_frame; - -class ole_client_site final : public IOleClientSite -{ -public: - ole_client_site(html_frame* frame); - virtual ~ole_client_site() = default; - -private: - html_frame* frame_; - -public: - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObject) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - HRESULT STDMETHODCALLTYPE SaveObject() override; - HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override; - HRESULT STDMETHODCALLTYPE GetContainer(LPOLECONTAINER FAR* ppContainer) override; - HRESULT STDMETHODCALLTYPE ShowObject() override; - HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow) override; - HRESULT STDMETHODCALLTYPE RequestNewObjectLayout() override; -}; +#pragma once + +class html_frame; + +class ole_client_site final : public IOleClientSite +{ +public: + ole_client_site(html_frame* frame); + virtual ~ole_client_site() = default; + +private: + html_frame* frame_; + +public: + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE SaveObject() override; + HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk) override; + HRESULT STDMETHODCALLTYPE GetContainer(LPOLECONTAINER FAR* ppContainer) override; + HRESULT STDMETHODCALLTYPE ShowObject() override; + HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow) override; + HRESULT STDMETHODCALLTYPE RequestNewObjectLayout() override; +}; diff --git a/src/launcher/html/ole_in_place_frame.cpp b/src/launcher/html/ole_in_place_frame.cpp index 172e5db..3bb79e7 100644 --- a/src/launcher/html/ole_in_place_frame.cpp +++ b/src/launcher/html/ole_in_place_frame.cpp @@ -1,82 +1,82 @@ -#include -#include "html_frame.hpp" - -ole_in_place_frame::ole_in_place_frame(html_frame* frame): frame_(frame) -{ -} - -HRESULT ole_in_place_frame::QueryInterface(REFIID /*riid*/, LPVOID* /*ppvObj*/) -{ - return E_NOTIMPL; -} - -ULONG ole_in_place_frame::AddRef() -{ - return 1; -} - -ULONG ole_in_place_frame::Release() -{ - return 1; -} - -HRESULT ole_in_place_frame::GetWindow(HWND* lphwnd) -{ - *lphwnd = this->frame_->get_window(); - return S_OK; -} - -HRESULT ole_in_place_frame::ContextSensitiveHelp(BOOL /*fEnterMode*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::GetBorder(LPRECT /*lprectBorder*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::RequestBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::SetBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::SetActiveObject(IOleInPlaceActiveObject* /*pActiveObject*/, LPCOLESTR /*pszObjName*/) -{ - return S_OK; -} - -HRESULT ole_in_place_frame::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::SetMenu(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/) -{ - return S_OK; -} - -HRESULT ole_in_place_frame::RemoveMenus(HMENU /*hmenuShared*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_frame::SetStatusText(LPCOLESTR /*pszStatusText*/) -{ - return S_OK; -} - -HRESULT ole_in_place_frame::EnableModeless(BOOL /*fEnable*/) -{ - return S_OK; -} - -HRESULT ole_in_place_frame::TranslateAcceleratorA(LPMSG /*lpmsg*/, WORD /*wID*/) -{ - return E_NOTIMPL; -} +#include +#include "html_frame.hpp" + +ole_in_place_frame::ole_in_place_frame(html_frame* frame): frame_(frame) +{ +} + +HRESULT ole_in_place_frame::QueryInterface(REFIID /*riid*/, LPVOID* /*ppvObj*/) +{ + return E_NOTIMPL; +} + +ULONG ole_in_place_frame::AddRef() +{ + return 1; +} + +ULONG ole_in_place_frame::Release() +{ + return 1; +} + +HRESULT ole_in_place_frame::GetWindow(HWND* lphwnd) +{ + *lphwnd = this->frame_->get_window(); + return S_OK; +} + +HRESULT ole_in_place_frame::ContextSensitiveHelp(BOOL /*fEnterMode*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::GetBorder(LPRECT /*lprectBorder*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::RequestBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::SetBorderSpace(LPCBORDERWIDTHS /*pborderwidths*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::SetActiveObject(IOleInPlaceActiveObject* /*pActiveObject*/, LPCOLESTR /*pszObjName*/) +{ + return S_OK; +} + +HRESULT ole_in_place_frame::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::SetMenu(HMENU /*hmenuShared*/, HOLEMENU /*holemenu*/, HWND /*hwndActiveObject*/) +{ + return S_OK; +} + +HRESULT ole_in_place_frame::RemoveMenus(HMENU /*hmenuShared*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_frame::SetStatusText(LPCOLESTR /*pszStatusText*/) +{ + return S_OK; +} + +HRESULT ole_in_place_frame::EnableModeless(BOOL /*fEnable*/) +{ + return S_OK; +} + +HRESULT ole_in_place_frame::TranslateAcceleratorA(LPMSG /*lpmsg*/, WORD /*wID*/) +{ + return E_NOTIMPL; +} diff --git a/src/launcher/html/ole_in_place_frame.hpp b/src/launcher/html/ole_in_place_frame.hpp index 4a39d7f..fed9a47 100644 --- a/src/launcher/html/ole_in_place_frame.hpp +++ b/src/launcher/html/ole_in_place_frame.hpp @@ -1,30 +1,30 @@ -#pragma once - -class html_frame; - -class ole_in_place_frame final : public IOleInPlaceFrame -{ -public: - ole_in_place_frame(html_frame* frame); - virtual ~ole_in_place_frame() = default; - -private: - html_frame* frame_; - -public: // IOleInPlaceFrame interface - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override; - HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override; - HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder) override; - HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) override; - HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths) override; - HRESULT STDMETHODCALLTYPE SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR pszObjName) override; - HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) override; - HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) override; - HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared) override; - HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText) override; - HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override; - HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg, WORD wID) override; -}; +#pragma once + +class html_frame; + +class ole_in_place_frame final : public IOleInPlaceFrame +{ +public: + ole_in_place_frame(html_frame* frame); + virtual ~ole_in_place_frame() = default; + +private: + html_frame* frame_; + +public: // IOleInPlaceFrame interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override; + HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override; + HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder) override; + HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) override; + HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths) override; + HRESULT STDMETHODCALLTYPE SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR pszObjName) override; + HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) override; + HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) override; + HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared) override; + HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText) override; + HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable) override; + HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg, WORD wID) override; +}; diff --git a/src/launcher/html/ole_in_place_site.cpp b/src/launcher/html/ole_in_place_site.cpp index d6cd3d8..f3da450 100644 --- a/src/launcher/html/ole_in_place_site.cpp +++ b/src/launcher/html/ole_in_place_site.cpp @@ -1,105 +1,105 @@ -#include -#include "html_frame.hpp" - -ole_in_place_site::ole_in_place_site(html_frame* frame) : frame_(frame) -{ -} - -HRESULT ole_in_place_site::QueryInterface(REFIID riid, LPVOID FAR* ppvObj) -{ - auto client_site = this->frame_->get_client_site(); - if (client_site) - { - return client_site->QueryInterface(riid, ppvObj); - } - - return E_NOINTERFACE; -} - -ULONG ole_in_place_site::AddRef() -{ - return 1; -} - -ULONG ole_in_place_site::Release() -{ - return 1; -} - -HRESULT ole_in_place_site::GetWindow(HWND* lphwnd) -{ - *lphwnd = this->frame_->get_window(); - return S_OK; -} - -HRESULT ole_in_place_site::ContextSensitiveHelp(BOOL /*fEnterMode*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_site::CanInPlaceActivate() -{ - return S_OK; -} - -HRESULT ole_in_place_site::OnInPlaceActivate() -{ - return S_OK; -} - -HRESULT ole_in_place_site::OnUIActivate() -{ - return S_OK; -} - -HRESULT ole_in_place_site::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, - LPRECT /*lprcPosRect*/, LPRECT /*lprcClipRect*/, - LPOLEINPLACEFRAMEINFO lpFrameInfo) -{ - *lplpFrame = this->frame_->get_in_place_frame(); - *lplpDoc = nullptr; - - lpFrameInfo->fMDIApp = FALSE; - lpFrameInfo->hwndFrame = this->frame_->get_window(); - lpFrameInfo->haccel = nullptr; - lpFrameInfo->cAccelEntries = 0; - - return S_OK; -} - -HRESULT ole_in_place_site::Scroll(SIZE /*scrollExtent*/) -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_site::OnUIDeactivate(BOOL /*fUndoable*/) -{ - return S_OK; -} - -HRESULT ole_in_place_site::OnInPlaceDeactivate() -{ - return S_OK; -} - -HRESULT ole_in_place_site::DiscardUndoState() -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_site::DeactivateAndUndo() -{ - return E_NOTIMPL; -} - -HRESULT ole_in_place_site::OnPosRectChange(LPCRECT lprcPosRect) -{ - IOleInPlaceObject* in_place = nullptr; - if (!this->frame_->get_browser_object()->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast(&in_place))) - { - in_place->SetObjectRects(lprcPosRect, lprcPosRect); - in_place->Release(); - } - - return S_OK; -} +#include +#include "html_frame.hpp" + +ole_in_place_site::ole_in_place_site(html_frame* frame) : frame_(frame) +{ +} + +HRESULT ole_in_place_site::QueryInterface(REFIID riid, LPVOID FAR* ppvObj) +{ + auto client_site = this->frame_->get_client_site(); + if (client_site) + { + return client_site->QueryInterface(riid, ppvObj); + } + + return E_NOINTERFACE; +} + +ULONG ole_in_place_site::AddRef() +{ + return 1; +} + +ULONG ole_in_place_site::Release() +{ + return 1; +} + +HRESULT ole_in_place_site::GetWindow(HWND* lphwnd) +{ + *lphwnd = this->frame_->get_window(); + return S_OK; +} + +HRESULT ole_in_place_site::ContextSensitiveHelp(BOOL /*fEnterMode*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_site::CanInPlaceActivate() +{ + return S_OK; +} + +HRESULT ole_in_place_site::OnInPlaceActivate() +{ + return S_OK; +} + +HRESULT ole_in_place_site::OnUIActivate() +{ + return S_OK; +} + +HRESULT ole_in_place_site::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, + LPRECT /*lprcPosRect*/, LPRECT /*lprcClipRect*/, + LPOLEINPLACEFRAMEINFO lpFrameInfo) +{ + *lplpFrame = this->frame_->get_in_place_frame(); + *lplpDoc = nullptr; + + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = this->frame_->get_window(); + lpFrameInfo->haccel = nullptr; + lpFrameInfo->cAccelEntries = 0; + + return S_OK; +} + +HRESULT ole_in_place_site::Scroll(SIZE /*scrollExtent*/) +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_site::OnUIDeactivate(BOOL /*fUndoable*/) +{ + return S_OK; +} + +HRESULT ole_in_place_site::OnInPlaceDeactivate() +{ + return S_OK; +} + +HRESULT ole_in_place_site::DiscardUndoState() +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_site::DeactivateAndUndo() +{ + return E_NOTIMPL; +} + +HRESULT ole_in_place_site::OnPosRectChange(LPCRECT lprcPosRect) +{ + IOleInPlaceObject* in_place = nullptr; + if (!this->frame_->get_browser_object()->QueryInterface(IID_IOleInPlaceObject, reinterpret_cast(&in_place))) + { + in_place->SetObjectRects(lprcPosRect, lprcPosRect); + in_place->Release(); + } + + return S_OK; +} diff --git a/src/launcher/html/ole_in_place_site.hpp b/src/launcher/html/ole_in_place_site.hpp index 3dad18c..4981573 100644 --- a/src/launcher/html/ole_in_place_site.hpp +++ b/src/launcher/html/ole_in_place_site.hpp @@ -1,32 +1,32 @@ -#pragma once - -class html_frame; - -class ole_in_place_site final : public IOleInPlaceSite -{ -public: - ole_in_place_site(html_frame* frame); - virtual ~ole_in_place_site() = default; - -private: - html_frame* frame_; - -public: // IOleInPlaceSite interface - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; - HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override; - HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override; - HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override; - HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override; - HRESULT STDMETHODCALLTYPE OnUIActivate() override; - HRESULT STDMETHODCALLTYPE GetWindowContext(LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc, - LPRECT lprcPosRect, LPRECT lprcClipRect, - LPOLEINPLACEFRAMEINFO lpFrameInfo) override; - HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtent) override; - HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable) override; - HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override; - HRESULT STDMETHODCALLTYPE DiscardUndoState() override; - HRESULT STDMETHODCALLTYPE DeactivateAndUndo() override; - HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect) override; -}; +#pragma once + +class html_frame; + +class ole_in_place_site final : public IOleInPlaceSite +{ +public: + ole_in_place_site(html_frame* frame); + virtual ~ole_in_place_site() = default; + +private: + html_frame* frame_; + +public: // IOleInPlaceSite interface + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + HRESULT STDMETHODCALLTYPE GetWindow(HWND FAR* lphwnd) override; + HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode) override; + HRESULT STDMETHODCALLTYPE CanInPlaceActivate() override; + HRESULT STDMETHODCALLTYPE OnInPlaceActivate() override; + HRESULT STDMETHODCALLTYPE OnUIActivate() override; + HRESULT STDMETHODCALLTYPE GetWindowContext(LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc, + LPRECT lprcPosRect, LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo) override; + HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtent) override; + HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable) override; + HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate() override; + HRESULT STDMETHODCALLTYPE DiscardUndoState() override; + HRESULT STDMETHODCALLTYPE DeactivateAndUndo() override; + HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect) override; +}; diff --git a/src/launcher/launcher.cpp b/src/launcher/launcher.cpp index 4336f12..38f2178 100644 --- a/src/launcher/launcher.cpp +++ b/src/launcher/launcher.cpp @@ -1,88 +1,88 @@ -#include -#include "launcher.hpp" -#include "utils/nt.hpp" - -launcher::launcher() -{ - this->create_settings_menu(); - this->create_main_menu(); -} - -void launcher::create_main_menu() -{ - this->main_window_.register_callback("selectMode", [this](html_frame::callback_params* params) - { - if (params->arguments.empty()) return; - - const auto param = params->arguments[0]; - if (!param.is_number()) return; - - const auto number = param.get_number(); - if (number == singleplayer || number == multiplayer) - { - this->select_mode(static_cast(number)); - } - }); - - this->main_window_.register_callback("showSettings", [this](html_frame::callback_params*) - { - this->settings_window_.show(); - }); - - this->main_window_.set_callback( - [](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT - { - if (message == WM_CLOSE) - { - window::close_all(); - } - - return DefWindowProcA(*window, message, w_param, l_param); - }); - - this->main_window_.create("Open-IW5", 615, 300); - this->main_window_.load_html(load_content(MENU_MAIN)); - this->main_window_.show(); -} - -void launcher::create_settings_menu() -{ - this->settings_window_.set_callback( - [](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT - { - if (message == WM_CLOSE) - { - window->hide(); - return TRUE; - } - - return DefWindowProcA(*window, message, w_param, l_param); - }); - - this->settings_window_.create("Open-IW5 Settings", 400, 200, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU); - this->settings_window_.load_html(load_content(MENU_SETTINGS)); -} - -launcher::mode launcher::run() const -{ - window::run(); - return this->mode_; -} - -void launcher::select_mode(const mode mode) -{ - this->mode_ = mode; - this->settings_window_.close(); - this->main_window_.close(); -} - -std::string launcher::load_content(const int res) -{ - const auto resource = FindResource(utils::nt::module(), MAKEINTRESOURCE(res), RT_RCDATA); - if (!resource) return {}; - - const auto handle = LoadResource(nullptr, resource); - if (!handle) return {}; - - return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, resource)); -} +#include +#include "launcher.hpp" +#include "utils/nt.hpp" + +launcher::launcher() +{ + this->create_settings_menu(); + this->create_main_menu(); +} + +void launcher::create_main_menu() +{ + this->main_window_.register_callback("selectMode", [this](html_frame::callback_params* params) + { + if (params->arguments.empty()) return; + + const auto param = params->arguments[0]; + if (!param.is_number()) return; + + const auto number = param.get_number(); + if (number == singleplayer || number == multiplayer) + { + this->select_mode(static_cast(number)); + } + }); + + this->main_window_.register_callback("showSettings", [this](html_frame::callback_params*) + { + this->settings_window_.show(); + }); + + this->main_window_.set_callback( + [](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT + { + if (message == WM_CLOSE) + { + window::close_all(); + } + + return DefWindowProcA(*window, message, w_param, l_param); + }); + + this->main_window_.create("Open-IW5", 615, 300); + this->main_window_.load_html(load_content(MENU_MAIN)); + this->main_window_.show(); +} + +void launcher::create_settings_menu() +{ + this->settings_window_.set_callback( + [](window* window, const UINT message, const WPARAM w_param, const LPARAM l_param) -> LRESULT + { + if (message == WM_CLOSE) + { + window->hide(); + return TRUE; + } + + return DefWindowProcA(*window, message, w_param, l_param); + }); + + this->settings_window_.create("Open-IW5 Settings", 400, 200, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU); + this->settings_window_.load_html(load_content(MENU_SETTINGS)); +} + +launcher::mode launcher::run() const +{ + window::run(); + return this->mode_; +} + +void launcher::select_mode(const mode mode) +{ + this->mode_ = mode; + this->settings_window_.close(); + this->main_window_.close(); +} + +std::string launcher::load_content(const int res) +{ + const auto resource = FindResource(utils::nt::module(), MAKEINTRESOURCE(res), RT_RCDATA); + if (!resource) return {}; + + const auto handle = LoadResource(nullptr, resource); + if (!handle) return {}; + + return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, resource)); +} diff --git a/src/launcher/launcher.hpp b/src/launcher/launcher.hpp index bebb75b..c10679f 100644 --- a/src/launcher/launcher.hpp +++ b/src/launcher/launcher.hpp @@ -1,31 +1,31 @@ -#pragma once -#include "html/html_window.hpp" - -class launcher final -{ -public: - enum mode - { - none, - singleplayer, - multiplayer, - server, - }; - - launcher(); - - mode run() const; - -private: - mode mode_ = none; - - html_window main_window_; - html_window settings_window_; - - void select_mode(mode mode); - - void create_main_menu(); - void create_settings_menu(); - - static std::string load_content(int res); -}; +#pragma once +#include "html/html_window.hpp" + +class launcher final +{ +public: + enum mode + { + none, + singleplayer, + multiplayer, + server, + }; + + launcher(); + + mode run() const; + +private: + mode mode_ = none; + + html_window main_window_; + html_window settings_window_; + + void select_mode(mode mode); + + void create_main_menu(); + void create_settings_menu(); + + static std::string load_content(int res); +}; diff --git a/src/launcher/window.cpp b/src/launcher/window.cpp index 411af8e..cfd666f 100644 --- a/src/launcher/window.cpp +++ b/src/launcher/window.cpp @@ -1,180 +1,180 @@ -#include -#include "window.hpp" - -std::mutex window::mutex_; -std::vector window::windows_; - -window::window() -{ - ZeroMemory(&this->wc_, sizeof(this->wc_)); - - this->classname_ = "window-base-" + std::to_string(time(nullptr)); - - this->wc_.cbSize = sizeof(this->wc_); - this->wc_.style = CS_HREDRAW | CS_VREDRAW; - this->wc_.lpfnWndProc = static_processor; - this->wc_.hInstance = GetModuleHandle(nullptr); - this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW); - this->wc_.hIcon = LoadIcon(this->wc_.hInstance, MAKEINTRESOURCE(102)); - this->wc_.hIconSm = this->wc_.hIcon; - this->wc_.hbrBackground = HBRUSH(COLOR_WINDOW); - this->wc_.lpszClassName = this->classname_.data(); - RegisterClassEx(&this->wc_); -} - -void window::create(const std::string& title, const int width, const int height, const long flags) -{ - { - std::lock_guard _(mutex_); - windows_.push_back(this); - } - - const auto x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; - const auto y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; - - this->handle_ = CreateWindowExA(NULL, this->wc_.lpszClassName, title.data(), flags, x, y, width, height, nullptr, - nullptr, this->wc_.hInstance, this); -} - -window::~window() -{ - this->close(); - UnregisterClass(this->wc_.lpszClassName, this->wc_.hInstance); -} - -void window::close() -{ - if (!this->handle_) return; - - SendMessageA(this->handle_, WM_KILL_WINDOW, NULL, NULL); - this->handle_ = nullptr; -} - -void window::run() -{ - MSG msg; - while (GetMessage(&msg, nullptr, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -void window::close_all() -{ - std::unique_lock lock(mutex_); - auto window_list = windows_; - lock.unlock(); - - const auto current_thread_id = GetCurrentThreadId(); - for (auto& window : window_list) - { - const auto thread_id = GetWindowThreadProcessId(*window, nullptr); - - if (thread_id == current_thread_id) - { - window->close(); - } - } -} - -void window::remove_window(const window* window) -{ - std::lock_guard _(mutex_); - - for (auto i = windows_.begin(); i != windows_.end(); ++i) - { - if (*i == window) - { - windows_.erase(i); - break; - } - } -} - -int window::get_window_count() -{ - std::lock_guard _(mutex_); - - auto count = 0; - const auto current_thread_id = GetCurrentThreadId(); - - for (const auto& window : windows_) - { - const auto thread_id = GetWindowThreadProcessId(*window, nullptr); - - if (thread_id == current_thread_id) - { - ++count; - } - } - - return count; -} - -void window::show() const -{ - ShowWindow(this->handle_, SW_SHOW); - UpdateWindow(this->handle_); -} - -void window::hide() const -{ - ShowWindow(this->handle_, SW_HIDE); - UpdateWindow(this->handle_); -} - -void window::set_callback(const std::function& callback) -{ - this->callback_ = callback; -} - -LRESULT window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) -{ - if (message == WM_DESTROY) - { - remove_window(this); - - if (get_window_count() == 0) - { - PostQuitMessage(0); - } - - return TRUE; - } - - if (message == WM_KILL_WINDOW) - { - DestroyWindow(*this); - return 0; - } - - if (this->callback_) - { - return this->callback_(this, message, w_param, l_param); - } - - return DefWindowProc(*this, message, w_param, l_param); -} - -LRESULT CALLBACK window::static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) -{ - if (message == WM_CREATE) - { - auto data = reinterpret_cast(l_param); - SetWindowLongPtrA(hwnd, GWLP_USERDATA, LONG_PTR(data->lpCreateParams)); - - reinterpret_cast(data->lpCreateParams)->handle_ = hwnd; - } - - const auto self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); - if (self) return self->processor(message, w_param, l_param); - - return DefWindowProc(hwnd, message, w_param, l_param); -} - - -window::operator HWND() const -{ - return this->handle_; -} +#include +#include "window.hpp" + +std::mutex window::mutex_; +std::vector window::windows_; + +window::window() +{ + ZeroMemory(&this->wc_, sizeof(this->wc_)); + + this->classname_ = "window-base-" + std::to_string(time(nullptr)); + + this->wc_.cbSize = sizeof(this->wc_); + this->wc_.style = CS_HREDRAW | CS_VREDRAW; + this->wc_.lpfnWndProc = static_processor; + this->wc_.hInstance = GetModuleHandle(nullptr); + this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW); + this->wc_.hIcon = LoadIcon(this->wc_.hInstance, MAKEINTRESOURCE(102)); + this->wc_.hIconSm = this->wc_.hIcon; + this->wc_.hbrBackground = HBRUSH(COLOR_WINDOW); + this->wc_.lpszClassName = this->classname_.data(); + RegisterClassEx(&this->wc_); +} + +void window::create(const std::string& title, const int width, const int height, const long flags) +{ + { + std::lock_guard _(mutex_); + windows_.push_back(this); + } + + const auto x = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; + const auto y = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; + + this->handle_ = CreateWindowExA(NULL, this->wc_.lpszClassName, title.data(), flags, x, y, width, height, nullptr, + nullptr, this->wc_.hInstance, this); +} + +window::~window() +{ + this->close(); + UnregisterClass(this->wc_.lpszClassName, this->wc_.hInstance); +} + +void window::close() +{ + if (!this->handle_) return; + + SendMessageA(this->handle_, WM_KILL_WINDOW, NULL, NULL); + this->handle_ = nullptr; +} + +void window::run() +{ + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void window::close_all() +{ + std::unique_lock lock(mutex_); + auto window_list = windows_; + lock.unlock(); + + const auto current_thread_id = GetCurrentThreadId(); + for (auto& window : window_list) + { + const auto thread_id = GetWindowThreadProcessId(*window, nullptr); + + if (thread_id == current_thread_id) + { + window->close(); + } + } +} + +void window::remove_window(const window* window) +{ + std::lock_guard _(mutex_); + + for (auto i = windows_.begin(); i != windows_.end(); ++i) + { + if (*i == window) + { + windows_.erase(i); + break; + } + } +} + +int window::get_window_count() +{ + std::lock_guard _(mutex_); + + auto count = 0; + const auto current_thread_id = GetCurrentThreadId(); + + for (const auto& window : windows_) + { + const auto thread_id = GetWindowThreadProcessId(*window, nullptr); + + if (thread_id == current_thread_id) + { + ++count; + } + } + + return count; +} + +void window::show() const +{ + ShowWindow(this->handle_, SW_SHOW); + UpdateWindow(this->handle_); +} + +void window::hide() const +{ + ShowWindow(this->handle_, SW_HIDE); + UpdateWindow(this->handle_); +} + +void window::set_callback(const std::function& callback) +{ + this->callback_ = callback; +} + +LRESULT window::processor(const UINT message, const WPARAM w_param, const LPARAM l_param) +{ + if (message == WM_DESTROY) + { + remove_window(this); + + if (get_window_count() == 0) + { + PostQuitMessage(0); + } + + return TRUE; + } + + if (message == WM_KILL_WINDOW) + { + DestroyWindow(*this); + return 0; + } + + if (this->callback_) + { + return this->callback_(this, message, w_param, l_param); + } + + return DefWindowProc(*this, message, w_param, l_param); +} + +LRESULT CALLBACK window::static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) +{ + if (message == WM_CREATE) + { + auto data = reinterpret_cast(l_param); + SetWindowLongPtrA(hwnd, GWLP_USERDATA, LONG_PTR(data->lpCreateParams)); + + reinterpret_cast(data->lpCreateParams)->handle_ = hwnd; + } + + const auto self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (self) return self->processor(message, w_param, l_param); + + return DefWindowProc(hwnd, message, w_param, l_param); +} + + +window::operator HWND() const +{ + return this->handle_; +} diff --git a/src/launcher/window.hpp b/src/launcher/window.hpp index 1c5f28e..a9f9779 100644 --- a/src/launcher/window.hpp +++ b/src/launcher/window.hpp @@ -1,42 +1,42 @@ -#pragma once - -#define WM_KILL_WINDOW (WM_USER+0) - -class window -{ -public: - window(); - virtual ~window(); - - void create(const std::string& title, int width, int height, - long flags = (WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX))); - - void close(); - - void show() const; - void hide() const; - - void set_callback(const std::function& callback); - - operator HWND() const; - - static void run(); - static void close_all(); - -protected: - virtual LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param); - -private: - WNDCLASSEX wc_{}; - HWND handle_ = nullptr; - std::string classname_; - std::function callback_; - - static LRESULT CALLBACK static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param); - - static std::mutex mutex_; - static std::vector windows_; - - static void remove_window(const window* window); - static int get_window_count(); -}; +#pragma once + +#define WM_KILL_WINDOW (WM_USER+0) + +class window +{ +public: + window(); + virtual ~window(); + + void create(const std::string& title, int width, int height, + long flags = (WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX))); + + void close(); + + void show() const; + void hide() const; + + void set_callback(const std::function& callback); + + operator HWND() const; + + static void run(); + static void close_all(); + +protected: + virtual LRESULT processor(UINT message, WPARAM w_param, LPARAM l_param); + +private: + WNDCLASSEX wc_{}; + HWND handle_ = nullptr; + std::string classname_; + std::function callback_; + + static LRESULT CALLBACK static_processor(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param); + + static std::mutex mutex_; + static std::vector windows_; + + static void remove_window(const window* window); + static int get_window_count(); +}; diff --git a/src/loader/binary_loader.cpp b/src/loader/binary_loader.cpp index 78d56c9..5e89548 100644 --- a/src/loader/binary_loader.cpp +++ b/src/loader/binary_loader.cpp @@ -1,121 +1,121 @@ -#include -#include "binary_loader.hpp" -#include "utils/nt.hpp" -#include "utils/io.hpp" -#include "utils/cryptography.hpp" -#include "utils/string.hpp" -#include "utils/compression.hpp" - -#define DEDI_HASH "F271C305117B79242E254E9F64BD5AA2993CAC8E57975243EBD44CD576418D20" - -namespace binary_loader -{ - std::string load_resource(const int id) - { - const auto res = FindResource(::utils::nt::module(), MAKEINTRESOURCE(id), RT_RCDATA); - if (!res) return {}; - - const auto handle = LoadResource(nullptr, res); - if (!handle) return {}; - - return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); - } - - std::string load_delta(const launcher::mode mode) - { - if (mode == launcher::mode::singleplayer) - { - return load_resource(BINARY_SP); - } - - if (mode == launcher::mode::multiplayer) - { - return load_resource(BINARY_MP); - } - - return {}; - } - - std::string load_base(const bool verify = true) - { - std::string data; - if (!utils::io::read_file("iw5mp_server.exe", &data)) - { - throw std::runtime_error("Unable to load iw5mp_server.exe"); - } - - if (verify && utils::cryptography::sha256::compute(data, true) != DEDI_HASH) - { - throw std::runtime_error("Your iw5mp_server.exe is incompatible with this client."); - } - - return data; - } - - void create_for_file(const std::string& file, const std::string& base) - { - std::string data; - if (!utils::io::read_file(file, &data)) - { - throw std::runtime_error(utils::string::va("Unable to load file %s!", file.data())); - } - - const auto new_data = reinterpret_cast(data.data()); - const auto old_data = reinterpret_cast(base.data()); - - std::vector diff; - create_diff(new_data, new_data + data.size(), old_data, old_data + base.size(), diff); - - const unsigned long long size = data.size(); - - std::string result(reinterpret_cast(diff.data()), diff.size()); - result.append(reinterpret_cast(&size), sizeof(size)); - result = utils::compression::zlib::compress(result); - - utils::io::write_file(file + ".diff", result); - } - - void create() - { - const auto base = load_base(false); - - utils::io::write_file("hash.txt", utils::cryptography::sha256::compute(base, true)); - - create_for_file("iw5sp.exe", base); - create_for_file("iw5mp.exe", base); - } - - std::string build_binary(const std::string& base, const std::string& diff) - { - const auto* size = reinterpret_cast(diff.data() + diff.size() - sizeof(unsigned long - long)); - - std::string binary; - binary.resize(size_t(*size)); - - const auto new_data = reinterpret_cast(binary.data()); - const auto old_data = reinterpret_cast(base.data()); - const auto diff_data = reinterpret_cast(diff.data()); - - if (patch(new_data, new_data + binary.size(), old_data, old_data + base.size(), diff_data, - diff_data + diff.size() - sizeof(*size)) == hpatch_FALSE || binary.empty()) - { - throw std::runtime_error("Unable to create binary from patch!"); - } - - return binary; - } - - std::string load(const launcher::mode mode) - { - auto base = load_base(); - if (mode == launcher::mode::server) - { - return base; - } - - auto delta = load_delta(mode); - delta = utils::compression::zlib::decompress(delta); - return build_binary(base, delta); - } -} +#include +#include "binary_loader.hpp" +#include "utils/nt.hpp" +#include "utils/io.hpp" +#include "utils/cryptography.hpp" +#include "utils/string.hpp" +#include "utils/compression.hpp" + +#define DEDI_HASH "F271C305117B79242E254E9F64BD5AA2993CAC8E57975243EBD44CD576418D20" + +namespace binary_loader +{ + std::string load_resource(const int id) + { + const auto res = FindResource(::utils::nt::module(), MAKEINTRESOURCE(id), RT_RCDATA); + if (!res) return {}; + + const auto handle = LoadResource(nullptr, res); + if (!handle) return {}; + + return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); + } + + std::string load_delta(const launcher::mode mode) + { + if (mode == launcher::mode::singleplayer) + { + return load_resource(BINARY_SP); + } + + if (mode == launcher::mode::multiplayer) + { + return load_resource(BINARY_MP); + } + + return {}; + } + + std::string load_base(const bool verify = true) + { + std::string data; + if (!utils::io::read_file("iw5mp_server.exe", &data)) + { + throw std::runtime_error("Unable to load iw5mp_server.exe"); + } + + if (verify && utils::cryptography::sha256::compute(data, true) != DEDI_HASH) + { + throw std::runtime_error("Your iw5mp_server.exe is incompatible with this client."); + } + + return data; + } + + void create_for_file(const std::string& file, const std::string& base) + { + std::string data; + if (!utils::io::read_file(file, &data)) + { + throw std::runtime_error(utils::string::va("Unable to load file %s!", file.data())); + } + + const auto new_data = reinterpret_cast(data.data()); + const auto old_data = reinterpret_cast(base.data()); + + std::vector diff; + create_diff(new_data, new_data + data.size(), old_data, old_data + base.size(), diff); + + const unsigned long long size = data.size(); + + std::string result(reinterpret_cast(diff.data()), diff.size()); + result.append(reinterpret_cast(&size), sizeof(size)); + result = utils::compression::zlib::compress(result); + + utils::io::write_file(file + ".diff", result); + } + + void create() + { + const auto base = load_base(false); + + utils::io::write_file("hash.txt", utils::cryptography::sha256::compute(base, true)); + + create_for_file("iw5sp.exe", base); + create_for_file("iw5mp.exe", base); + } + + std::string build_binary(const std::string& base, const std::string& diff) + { + const auto* size = reinterpret_cast(diff.data() + diff.size() - sizeof(unsigned long + long)); + + std::string binary; + binary.resize(size_t(*size)); + + const auto new_data = reinterpret_cast(binary.data()); + const auto old_data = reinterpret_cast(base.data()); + const auto diff_data = reinterpret_cast(diff.data()); + + if (patch(new_data, new_data + binary.size(), old_data, old_data + base.size(), diff_data, + diff_data + diff.size() - sizeof(*size)) == hpatch_FALSE || binary.empty()) + { + throw std::runtime_error("Unable to create binary from patch!"); + } + + return binary; + } + + std::string load(const launcher::mode mode) + { + auto base = load_base(); + if (mode == launcher::mode::server) + { + return base; + } + + auto delta = load_delta(mode); + delta = utils::compression::zlib::decompress(delta); + return build_binary(base, delta); + } +} diff --git a/src/loader/binary_loader.hpp b/src/loader/binary_loader.hpp index de069d8..0af65b1 100644 --- a/src/loader/binary_loader.hpp +++ b/src/loader/binary_loader.hpp @@ -1,8 +1,8 @@ -#pragma once -#include "launcher/launcher.hpp" - -namespace binary_loader -{ - void create(); - std::string load(launcher::mode mode); -} +#pragma once +#include "launcher/launcher.hpp" + +namespace binary_loader +{ + void create(); + std::string load(launcher::mode mode); +} diff --git a/src/loader/loader.cpp b/src/loader/loader.cpp index bd83c84..9d250f4 100644 --- a/src/loader/loader.cpp +++ b/src/loader/loader.cpp @@ -1,165 +1,165 @@ -#include -#include "loader.hpp" -#include "binary_loader.hpp" -#include "utils/string.hpp" - -loader::loader(const launcher::mode mode) : mode_(mode) -{ -} - -FARPROC loader::load(const utils::nt::module& module) const -{ - const auto buffer = binary_loader::load(this->mode_); - if (buffer.empty()) return nullptr; - - utils::nt::module source(HMODULE(buffer.data())); - if (!source) return nullptr; - - this->load_sections(module, source); - this->load_imports(module, source); - - if (source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) - { - const auto target_tls = reinterpret_cast(module.get_ptr() + module - .get_optional_header() - -> - DataDirectory - [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); - const auto source_tls = reinterpret_cast(module.get_ptr() + source - .get_optional_header() - -> - DataDirectory - [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); - - const auto tls_size = source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData; - const auto tls_index = *reinterpret_cast(target_tls->AddressOfIndex); - *reinterpret_cast(source_tls->AddressOfIndex) = tls_index; - - if (tls_size > TLS_PAYLOAD_SIZE) - { - throw std::runtime_error(utils::string::va( - "TLS data is of size 0x%X, but we have only reserved 0x%X bytes!", tls_size, TLS_PAYLOAD_SIZE)); - } - - DWORD old_protect; - VirtualProtect(PVOID(target_tls->StartAddressOfRawData), - source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData, PAGE_READWRITE, - &old_protect); - - const auto tls_base = *reinterpret_cast(__readfsdword(0x2C) + 4 * tls_index); - std::memmove(tls_base, PVOID(source_tls->StartAddressOfRawData), tls_size); - std::memmove(PVOID(target_tls->StartAddressOfRawData), PVOID(source_tls->StartAddressOfRawData), tls_size); - } - - DWORD oldProtect; - VirtualProtect(module.get_nt_headers(), 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect); - - module.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] = source - .get_optional_header()->DataDirectory[ - IMAGE_DIRECTORY_ENTRY_IMPORT]; - std::memmove(module.get_nt_headers(), source.get_nt_headers(), - sizeof(IMAGE_NT_HEADERS) + (source.get_nt_headers()->FileHeader.NumberOfSections * (sizeof( - IMAGE_SECTION_HEADER)))); - - return FARPROC(module.get_ptr() + source.get_relative_entry_point()); -} - -void loader::set_import_resolver(const std::function& resolver) -{ - this->import_resolver_ = resolver; -} - -void loader::load_section(const utils::nt::module& target, const utils::nt::module& source, - IMAGE_SECTION_HEADER* section) -{ - void* target_ptr = target.get_ptr() + section->VirtualAddress; - const void* source_ptr = source.get_ptr() + section->PointerToRawData; - - if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE)) - { - throw std::runtime_error("Section exceeds the binary payload size, please increase it!"); - } - - if (section->SizeOfRawData > 0) - { - const auto size_of_data = std::min(section->SizeOfRawData, section->Misc.VirtualSize); - std::memmove(target_ptr, source_ptr, size_of_data); - - DWORD old_protect; - VirtualProtect(target_ptr, size_of_data, PAGE_EXECUTE_READWRITE, &old_protect); - } -} - -void loader::load_sections(const utils::nt::module& target, const utils::nt::module& source) const -{ - for (auto& section : source.get_section_headers()) - { - this->load_section(target, source, section); - } -} - -void loader::load_imports(const utils::nt::module& target, const utils::nt::module& source) const -{ - const auto import_directory = &source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; - - auto descriptor = PIMAGE_IMPORT_DESCRIPTOR(target.get_ptr() + import_directory->VirtualAddress); - - while (descriptor->Name) - { - std::string name = LPSTR(target.get_ptr() + descriptor->Name); - - auto name_table_entry = reinterpret_cast(target.get_ptr() + descriptor->OriginalFirstThunk); - auto address_table_entry = reinterpret_cast(target.get_ptr() + descriptor->FirstThunk); - - if (!descriptor->OriginalFirstThunk) - { - name_table_entry = reinterpret_cast(target.get_ptr() + descriptor->FirstThunk); - } - - while (*name_table_entry) - { - FARPROC function = nullptr; - std::string function_name; - - // is this an ordinal-only import? - if (IMAGE_SNAP_BY_ORDINAL(*name_table_entry)) - { - auto module = utils::nt::module::load(name); - if (module) - { - function = GetProcAddress(module, MAKEINTRESOURCEA(IMAGE_ORDINAL(*name_table_entry))); - } - - function_name = "#" + std::to_string(IMAGE_ORDINAL(*name_table_entry)); - } - else - { - auto import = PIMAGE_IMPORT_BY_NAME(target.get_ptr() + *name_table_entry); - function_name = import->Name; - - if (this->import_resolver_) function = this->import_resolver_(name, function_name); - if (!function) - { - auto module = utils::nt::module::load(name); - if (module) - { - function = GetProcAddress(module, function_name.data()); - } - } - } - - if (!function) - { - throw std::runtime_error(utils::string::va("Unable to load import '%s' from module '%s'", - function_name.data(), name.data())); - } - - *address_table_entry = reinterpret_cast(function); - - name_table_entry++; - address_table_entry++; - } - - descriptor++; - } -} +#include +#include "loader.hpp" +#include "binary_loader.hpp" +#include "utils/string.hpp" + +loader::loader(const launcher::mode mode) : mode_(mode) +{ +} + +FARPROC loader::load(const utils::nt::module& module) const +{ + const auto buffer = binary_loader::load(this->mode_); + if (buffer.empty()) return nullptr; + + utils::nt::module source(HMODULE(buffer.data())); + if (!source) return nullptr; + + this->load_sections(module, source); + this->load_imports(module, source); + + if (source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) + { + const auto target_tls = reinterpret_cast(module.get_ptr() + module + .get_optional_header() + -> + DataDirectory + [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); + const auto source_tls = reinterpret_cast(module.get_ptr() + source + .get_optional_header() + -> + DataDirectory + [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); + + const auto tls_size = source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData; + const auto tls_index = *reinterpret_cast(target_tls->AddressOfIndex); + *reinterpret_cast(source_tls->AddressOfIndex) = tls_index; + + if (tls_size > TLS_PAYLOAD_SIZE) + { + throw std::runtime_error(utils::string::va( + "TLS data is of size 0x%X, but we have only reserved 0x%X bytes!", tls_size, TLS_PAYLOAD_SIZE)); + } + + DWORD old_protect; + VirtualProtect(PVOID(target_tls->StartAddressOfRawData), + source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData, PAGE_READWRITE, + &old_protect); + + const auto tls_base = *reinterpret_cast(__readfsdword(0x2C) + 4 * tls_index); + std::memmove(tls_base, PVOID(source_tls->StartAddressOfRawData), tls_size); + std::memmove(PVOID(target_tls->StartAddressOfRawData), PVOID(source_tls->StartAddressOfRawData), tls_size); + } + + DWORD oldProtect; + VirtualProtect(module.get_nt_headers(), 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect); + + module.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] = source + .get_optional_header()->DataDirectory[ + IMAGE_DIRECTORY_ENTRY_IMPORT]; + std::memmove(module.get_nt_headers(), source.get_nt_headers(), + sizeof(IMAGE_NT_HEADERS) + (source.get_nt_headers()->FileHeader.NumberOfSections * (sizeof( + IMAGE_SECTION_HEADER)))); + + return FARPROC(module.get_ptr() + source.get_relative_entry_point()); +} + +void loader::set_import_resolver(const std::function& resolver) +{ + this->import_resolver_ = resolver; +} + +void loader::load_section(const utils::nt::module& target, const utils::nt::module& source, + IMAGE_SECTION_HEADER* section) +{ + void* target_ptr = target.get_ptr() + section->VirtualAddress; + const void* source_ptr = source.get_ptr() + section->PointerToRawData; + + if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE)) + { + throw std::runtime_error("Section exceeds the binary payload size, please increase it!"); + } + + if (section->SizeOfRawData > 0) + { + const auto size_of_data = std::min(section->SizeOfRawData, section->Misc.VirtualSize); + std::memmove(target_ptr, source_ptr, size_of_data); + + DWORD old_protect; + VirtualProtect(target_ptr, size_of_data, PAGE_EXECUTE_READWRITE, &old_protect); + } +} + +void loader::load_sections(const utils::nt::module& target, const utils::nt::module& source) const +{ + for (auto& section : source.get_section_headers()) + { + this->load_section(target, source, section); + } +} + +void loader::load_imports(const utils::nt::module& target, const utils::nt::module& source) const +{ + const auto import_directory = &source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + + auto descriptor = PIMAGE_IMPORT_DESCRIPTOR(target.get_ptr() + import_directory->VirtualAddress); + + while (descriptor->Name) + { + std::string name = LPSTR(target.get_ptr() + descriptor->Name); + + auto name_table_entry = reinterpret_cast(target.get_ptr() + descriptor->OriginalFirstThunk); + auto address_table_entry = reinterpret_cast(target.get_ptr() + descriptor->FirstThunk); + + if (!descriptor->OriginalFirstThunk) + { + name_table_entry = reinterpret_cast(target.get_ptr() + descriptor->FirstThunk); + } + + while (*name_table_entry) + { + FARPROC function = nullptr; + std::string function_name; + + // is this an ordinal-only import? + if (IMAGE_SNAP_BY_ORDINAL(*name_table_entry)) + { + auto module = utils::nt::module::load(name); + if (module) + { + function = GetProcAddress(module, MAKEINTRESOURCEA(IMAGE_ORDINAL(*name_table_entry))); + } + + function_name = "#" + std::to_string(IMAGE_ORDINAL(*name_table_entry)); + } + else + { + auto import = PIMAGE_IMPORT_BY_NAME(target.get_ptr() + *name_table_entry); + function_name = import->Name; + + if (this->import_resolver_) function = this->import_resolver_(name, function_name); + if (!function) + { + auto module = utils::nt::module::load(name); + if (module) + { + function = GetProcAddress(module, function_name.data()); + } + } + } + + if (!function) + { + throw std::runtime_error(utils::string::va("Unable to load import '%s' from module '%s'", + function_name.data(), name.data())); + } + + *address_table_entry = reinterpret_cast(function); + + name_table_entry++; + address_table_entry++; + } + + descriptor++; + } +} diff --git a/src/loader/loader.hpp b/src/loader/loader.hpp index df6b1e0..b95f1fa 100644 --- a/src/loader/loader.hpp +++ b/src/loader/loader.hpp @@ -1,22 +1,22 @@ -#pragma once -#include "utils/nt.hpp" -#include "launcher/launcher.hpp" - -class loader final -{ -public: - explicit loader(launcher::mode mode); - - FARPROC load(const utils::nt::module& module) const; - - void set_import_resolver(const std::function& resolver); - -private: - launcher::mode mode_; - std::function import_resolver_; - - static void load_section(const utils::nt::module& target, const utils::nt::module& source, - IMAGE_SECTION_HEADER* section); - void load_sections(const utils::nt::module& target, const utils::nt::module& source) const; - void load_imports(const utils::nt::module& target, const utils::nt::module& source) const; -}; +#pragma once +#include "utils/nt.hpp" +#include "launcher/launcher.hpp" + +class loader final +{ +public: + explicit loader(launcher::mode mode); + + FARPROC load(const utils::nt::module& module) const; + + void set_import_resolver(const std::function& resolver); + +private: + launcher::mode mode_; + std::function import_resolver_; + + static void load_section(const utils::nt::module& target, const utils::nt::module& source, + IMAGE_SECTION_HEADER* section); + void load_sections(const utils::nt::module& target, const utils::nt::module& source) const; + void load_imports(const utils::nt::module& target, const utils::nt::module& source) const; +}; diff --git a/src/loader/module_loader.cpp b/src/loader/module_loader.cpp index bce9176..615d11b 100644 --- a/src/loader/module_loader.cpp +++ b/src/loader/module_loader.cpp @@ -1,100 +1,100 @@ -#include -#include "module_loader.hpp" - -std::vector>* module_loader::modules_ = nullptr; - -void module_loader::register_module(std::unique_ptr&& module_) -{ - if (!modules_) - { - modules_ = new std::vector>(); - atexit(destroy_modules); - } - - modules_->push_back(std::move(module_)); -} - -bool module_loader::post_start() -{ - static auto handled = false; - if (handled || !modules_) return true; - handled = true; - - try - { - for (const auto& module_ : *modules_) - { - module_->post_start(); - } - } - catch(premature_shutdown_trigger&) - { - return false; - } - - return true; -} - -bool module_loader::post_load() -{ - static auto handled = false; - if (handled || !modules_) return true; - handled = true; - - try - { - for (const auto& module_ : *modules_) - { - module_->post_load(); - } - } - catch (premature_shutdown_trigger&) - { - return false; - } - - return true; -} - -void module_loader::pre_destroy() -{ - static auto handled = false; - if (handled || !modules_) return; - handled = true; - - for (const auto& module_ : *modules_) - { - module_->pre_destroy(); - } -} - -void* module_loader::load_import(const std::string& module, const std::string& function) -{ - void* function_ptr = nullptr; - - for (const auto& module_ : *modules_) - { - const auto module_function_ptr = module_->load_import(module, function); - if(module_function_ptr) - { - function_ptr = module_function_ptr; - } - } - - return function_ptr; -} - -void module_loader::destroy_modules() -{ - pre_destroy(); - - if (!modules_) return; - - delete modules_; - modules_ = nullptr; -} - -void module_loader::trigger_premature_shutdown() -{ - throw premature_shutdown_trigger(); -} +#include +#include "module_loader.hpp" + +std::vector>* module_loader::modules_ = nullptr; + +void module_loader::register_module(std::unique_ptr&& module_) +{ + if (!modules_) + { + modules_ = new std::vector>(); + atexit(destroy_modules); + } + + modules_->push_back(std::move(module_)); +} + +bool module_loader::post_start() +{ + static auto handled = false; + if (handled || !modules_) return true; + handled = true; + + try + { + for (const auto& module_ : *modules_) + { + module_->post_start(); + } + } + catch(premature_shutdown_trigger&) + { + return false; + } + + return true; +} + +bool module_loader::post_load() +{ + static auto handled = false; + if (handled || !modules_) return true; + handled = true; + + try + { + for (const auto& module_ : *modules_) + { + module_->post_load(); + } + } + catch (premature_shutdown_trigger&) + { + return false; + } + + return true; +} + +void module_loader::pre_destroy() +{ + static auto handled = false; + if (handled || !modules_) return; + handled = true; + + for (const auto& module_ : *modules_) + { + module_->pre_destroy(); + } +} + +void* module_loader::load_import(const std::string& module, const std::string& function) +{ + void* function_ptr = nullptr; + + for (const auto& module_ : *modules_) + { + const auto module_function_ptr = module_->load_import(module, function); + if(module_function_ptr) + { + function_ptr = module_function_ptr; + } + } + + return function_ptr; +} + +void module_loader::destroy_modules() +{ + pre_destroy(); + + if (!modules_) return; + + delete modules_; + modules_ = nullptr; +} + +void module_loader::trigger_premature_shutdown() +{ + throw premature_shutdown_trigger(); +} diff --git a/src/main.cpp b/src/main.cpp index 868c1a5..acff003 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,132 +1,132 @@ -#include -#include "launcher/launcher.hpp" -#include "loader/loader.hpp" -#include "loader/module_loader.hpp" -#include "game/game.hpp" -#include "loader/binary_loader.hpp" -#include "utils/string.hpp" -#include "utils/flags.hpp" - -//#define GENERATE_DIFFS - -DECLSPEC_NORETURN void WINAPI exit_hook(const int code) -{ - module_loader::pre_destroy(); - exit(code); -} - -void verify_tls() -{ - utils::nt::module self; - const auto self_tls = reinterpret_cast(self.get_ptr() + self - .get_optional_header() - -> - DataDirectory - [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); - - const auto ref = DWORD(&tls_data); - const auto tls_index = *reinterpret_cast(self_tls->AddressOfIndex); - const auto tls_vector = *reinterpret_cast(__readfsdword(0x2C) + 4 * tls_index); - const auto offset = ref - tls_vector; - - if (offset != 0 && offset != 8) // Actually 8 is bad, but I think msvc places custom stuff before - { - throw std::runtime_error(utils::string::va("TLS payload is at offset 0x%X, but should be at 0!", - offset)); - } -} - -launcher::mode detect_mode_from_arguments() -{ - if (utils::flags::has_flag("dedicated")) - { - return launcher::mode::server; - } - - if (utils::flags::has_flag("multiplayer")) - { - return launcher::mode::multiplayer; - } - - if (utils::flags::has_flag("singleplayer")) - { - return launcher::mode::singleplayer; - } - - return launcher::mode::none; -} - -FARPROC load_binary(const launcher::mode mode) -{ - loader loader(mode); - utils::nt::module self; - - loader.set_import_resolver([self](const std::string& module, const std::string& function) -> FARPROC - { - if (module == "steam_api.dll") - { - return self.get_proc(function); - } - else if (function == "ExitProcess") - { - return FARPROC(exit_hook); - } - - return FARPROC(module_loader::load_import(module, function)); - }); - - return loader.load(self); -} - -int main() -{ - FARPROC entry_point; - - { - auto premature_shutdown = true; - const auto _ = gsl::finally([&premature_shutdown]() - { - if (premature_shutdown) - { - module_loader::pre_destroy(); - } - }); - - try - { -#ifdef GENERATE_DIFFS - binary_loader::create(); - return 0; -#endif - - verify_tls(); - if (!module_loader::post_start()) return 0; - - auto mode = detect_mode_from_arguments(); - if (mode == launcher::mode::none) - { - launcher launcher; - mode = launcher.run(); - if (mode == launcher::mode::none) return 0; - } - - entry_point = load_binary(mode); - if (!entry_point) - { - throw std::runtime_error("Unable to load binary into memory"); - } - - game::initialize(mode); - if (!module_loader::post_load()) return 0; - - premature_shutdown = false; - } - catch (std::exception& e) - { - MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); - return 1; - } - } - - return entry_point(); -} +#include +#include "launcher/launcher.hpp" +#include "loader/loader.hpp" +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "loader/binary_loader.hpp" +#include "utils/string.hpp" +#include "utils/flags.hpp" + +//#define GENERATE_DIFFS + +DECLSPEC_NORETURN void WINAPI exit_hook(const int code) +{ + module_loader::pre_destroy(); + exit(code); +} + +void verify_tls() +{ + utils::nt::module self; + const auto self_tls = reinterpret_cast(self.get_ptr() + self + .get_optional_header() + -> + DataDirectory + [IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); + + const auto ref = DWORD(&tls_data); + const auto tls_index = *reinterpret_cast(self_tls->AddressOfIndex); + const auto tls_vector = *reinterpret_cast(__readfsdword(0x2C) + 4 * tls_index); + const auto offset = ref - tls_vector; + + if (offset != 0 && offset != 8) // Actually 8 is bad, but I think msvc places custom stuff before + { + throw std::runtime_error(utils::string::va("TLS payload is at offset 0x%X, but should be at 0!", + offset)); + } +} + +launcher::mode detect_mode_from_arguments() +{ + if (utils::flags::has_flag("dedicated")) + { + return launcher::mode::server; + } + + if (utils::flags::has_flag("multiplayer")) + { + return launcher::mode::multiplayer; + } + + if (utils::flags::has_flag("singleplayer")) + { + return launcher::mode::singleplayer; + } + + return launcher::mode::none; +} + +FARPROC load_binary(const launcher::mode mode) +{ + loader loader(mode); + utils::nt::module self; + + loader.set_import_resolver([self](const std::string& module, const std::string& function) -> FARPROC + { + if (module == "steam_api.dll") + { + return self.get_proc(function); + } + else if (function == "ExitProcess") + { + return FARPROC(exit_hook); + } + + return FARPROC(module_loader::load_import(module, function)); + }); + + return loader.load(self); +} + +int main() +{ + FARPROC entry_point; + + { + auto premature_shutdown = true; + const auto _ = gsl::finally([&premature_shutdown]() + { + if (premature_shutdown) + { + module_loader::pre_destroy(); + } + }); + + try + { +#ifdef GENERATE_DIFFS + binary_loader::create(); + return 0; +#endif + + verify_tls(); + if (!module_loader::post_start()) return 0; + + auto mode = detect_mode_from_arguments(); + if (mode == launcher::mode::none) + { + launcher launcher; + mode = launcher.run(); + if (mode == launcher::mode::none) return 0; + } + + entry_point = load_binary(mode); + if (!entry_point) + { + throw std::runtime_error("Unable to load binary into memory"); + } + + game::initialize(mode); + if (!module_loader::post_load()) return 0; + + premature_shutdown = false; + } + catch (std::exception& e) + { + MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); + return 1; + } + } + + return entry_point(); +} diff --git a/src/module/ceg.cpp b/src/module/ceg.cpp index 706c814..ba1d851 100644 --- a/src/module/ceg.cpp +++ b/src/module/ceg.cpp @@ -1,90 +1,90 @@ -#include -#include "loader/module_loader.hpp" -#include "utils/hook.hpp" -#include "game/game.hpp" - -class ceg final : public module -{ -public: - void post_load() override - { - // Only SP has CEG - // CEG in MP has accidentally been removed due to CVE-2018-10718 - if (!game::is_sp()) return; - - utils::hook::signature signature(0x401000, 0x3E1000); - - signature.add({ - "\x56\xE8\x00\x00\x00\x00\x8B\xF0\xE8\x00\x00\x00\x00\x50\x56\xE8", "xx????xxx????xxx", [](char* address) - { - utils::hook::set(address, 0xC301B0); - } - }); - - // Generic killer caller. - signature.add({ - "\x55\x8B\xEC\x80\x7D\x08\x00\x75\x55", "xxxxxx?xx", [](char* address) - { - utils::hook::set(address, 0xC301B0); - } - }); - - // CEG initialization. - signature.add({ - "\x55\x8B\xEC\x83\xEC\x18\x53\x56\x57\xE8\x00\x00\x00\x00", "xxxxxxxxxx????", [](char* address) - { - utils::hook::set(address, 0xC3); - } - }); - - // Some odd trap. - signature.add({ - "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\x53\x56\x57\x8B\x3D", "xxxxx??xxxxxxx", [](char* address) - { - utils::hook::set(address, 0xC301B0); - } - }); - - // Custom shit - signature.add({ - "\x55\x8B\xEC\x68\x00\x00\x00\x00\x68\x00\x00\x00\x00\x64\xFF\x35\x00\x00\x00\x00\x64\x89\x25\x00\x00\x00\x00\xE8", - "xxxx????x????xxx????xxx????x", [](char* address) - { - utils::hook::set(address, 0xC3); - } - }); - - // hkcr guid check - signature.add({ - "\x55\x8B\xEC\xB8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", - "xxxx????x????x????xxxx", [](char* address) - { - utils::hook::nop(address + 0xD, 5); // Call - utils::hook::nop(address + 0x14, 2); // Jump - } - }); - - // hkcr guid check 2 - signature.add({ - "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", "xxxxx????x????xxxx", [ - ](char* address) - { - utils::hook::nop(address + 0x9, 5); // Call - utils::hook::nop(address + 0x10, 2); // Jump - } - }); - - signature.process(); - - // Function fixup - utils::hook(0x4CA310, game::native::DB_LoadXAssets, HOOK_JUMP).install()->quick(); - - // Some value obfuscation - utils::hook(0x493B81, 0x493BFC, HOOK_JUMP).install()->quick(); - - // CEG uninitialization - utils::hook::set(0x527110, 0xC3); - } -}; - -REGISTER_MODULE(ceg) +#include +#include "loader/module_loader.hpp" +#include "utils/hook.hpp" +#include "game/game.hpp" + +class ceg final : public module +{ +public: + void post_load() override + { + // Only SP has CEG + // CEG in MP has accidentally been removed due to CVE-2018-10718 + if (!game::is_sp()) return; + + utils::hook::signature signature(0x401000, 0x3E1000); + + signature.add({ + "\x56\xE8\x00\x00\x00\x00\x8B\xF0\xE8\x00\x00\x00\x00\x50\x56\xE8", "xx????xxx????xxx", [](char* address) + { + utils::hook::set(address, 0xC301B0); + } + }); + + // Generic killer caller. + signature.add({ + "\x55\x8B\xEC\x80\x7D\x08\x00\x75\x55", "xxxxxx?xx", [](char* address) + { + utils::hook::set(address, 0xC301B0); + } + }); + + // CEG initialization. + signature.add({ + "\x55\x8B\xEC\x83\xEC\x18\x53\x56\x57\xE8\x00\x00\x00\x00", "xxxxxxxxxx????", [](char* address) + { + utils::hook::set(address, 0xC3); + } + }); + + // Some odd trap. + signature.add({ + "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\x53\x56\x57\x8B\x3D", "xxxxx??xxxxxxx", [](char* address) + { + utils::hook::set(address, 0xC301B0); + } + }); + + // Custom shit + signature.add({ + "\x55\x8B\xEC\x68\x00\x00\x00\x00\x68\x00\x00\x00\x00\x64\xFF\x35\x00\x00\x00\x00\x64\x89\x25\x00\x00\x00\x00\xE8", + "xxxx????x????xxx????xxx????x", [](char* address) + { + utils::hook::set(address, 0xC3); + } + }); + + // hkcr guid check + signature.add({ + "\x55\x8B\xEC\xB8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", + "xxxx????x????x????xxxx", [](char* address) + { + utils::hook::nop(address + 0xD, 5); // Call + utils::hook::nop(address + 0x14, 2); // Jump + } + }); + + // hkcr guid check 2 + signature.add({ + "\x55\x8B\xEC\x81\xEC\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x84\xC0\x75\x06", "xxxxx????x????xxxx", [ + ](char* address) + { + utils::hook::nop(address + 0x9, 5); // Call + utils::hook::nop(address + 0x10, 2); // Jump + } + }); + + signature.process(); + + // Function fixup + utils::hook(0x4CA310, game::native::DB_LoadXAssets, HOOK_JUMP).install()->quick(); + + // Some value obfuscation + utils::hook(0x493B81, 0x493BFC, HOOK_JUMP).install()->quick(); + + // CEG uninitialization + utils::hook::set(0x527110, 0xC3); + } +}; + +REGISTER_MODULE(ceg) diff --git a/src/module/console.cpp b/src/module/console.cpp index be56bfd..47d9cda 100644 --- a/src/module/console.cpp +++ b/src/module/console.cpp @@ -1,115 +1,115 @@ -#include -#include "loader/module_loader.hpp" -#include "game/game.hpp" -#include "scheduler.hpp" - -class console final : public module -{ -public: - console() - { - ShowWindow(GetConsoleWindow(), SW_HIDE); - - _pipe(this->handles_, 1024, _O_TEXT); - _dup2(this->handles_[1], 1); - _dup2(this->handles_[1], 2); - - //setvbuf(stdout, nullptr, _IONBF, 0); - //setvbuf(stderr, nullptr, _IONBF, 0); - } - - void post_start() override - { - scheduler::on_frame(std::bind(&console::log_messages, this)); - this->console_runner_ = std::thread(std::bind(&console::runner, this)); - } - - void pre_destroy() override - { - this->terminate_runner_ = true; - - printf("\r\n"); - _flushall(); - - _close(this->handles_[0]); - _close(this->handles_[1]); - - if (this->console_runner_.joinable()) - { - this->console_runner_.join(); - } - } - - void post_load() override - { - if (!game::is_dedi()) - { - game::native::Sys_ShowConsole(); - } - - std::lock_guard _(this->mutex_); - this->console_initialized_ = true; - } - -private: - bool console_initialized_ = false; - bool terminate_runner_ = false; - - std::mutex mutex_; - std::thread console_runner_; - std::queue message_queue_; - - int handles_[2]{}; - - void log_messages() - { - while (this->console_initialized_ && !this->message_queue_.empty()) - { - std::queue message_queue_copy; - - { - std::lock_guard _(this->mutex_); - message_queue_copy = this->message_queue_; - this->message_queue_ = {}; - } - - while (!message_queue_copy.empty()) - { - log_message(message_queue_copy.front()); - message_queue_copy.pop(); - } - } - - fflush(stdout); - fflush(stderr); - } - - static void log_message(const std::string& message) - { - OutputDebugStringA(message.data()); - game::native::Conbuf_AppendText(message.data()); - } - - void runner() - { - char buffer[1024]; - - while (!this->terminate_runner_ && this->handles_[0]) - { - const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); - if (len > 0) - { - std::lock_guard _(this->mutex_); - this->message_queue_.push(std::string(buffer, len)); - } - else - { - std::this_thread::sleep_for(10ms); - } - } - - std::this_thread::yield(); - } -}; - -REGISTER_MODULE(console) +#include +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "scheduler.hpp" + +class console final : public module +{ +public: + console() + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + + _pipe(this->handles_, 1024, _O_TEXT); + _dup2(this->handles_[1], 1); + _dup2(this->handles_[1], 2); + + //setvbuf(stdout, nullptr, _IONBF, 0); + //setvbuf(stderr, nullptr, _IONBF, 0); + } + + void post_start() override + { + scheduler::on_frame(std::bind(&console::log_messages, this)); + this->console_runner_ = std::thread(std::bind(&console::runner, this)); + } + + void pre_destroy() override + { + this->terminate_runner_ = true; + + printf("\r\n"); + _flushall(); + + _close(this->handles_[0]); + _close(this->handles_[1]); + + if (this->console_runner_.joinable()) + { + this->console_runner_.join(); + } + } + + void post_load() override + { + if (!game::is_dedi()) + { + game::native::Sys_ShowConsole(); + } + + std::lock_guard _(this->mutex_); + this->console_initialized_ = true; + } + +private: + bool console_initialized_ = false; + bool terminate_runner_ = false; + + std::mutex mutex_; + std::thread console_runner_; + std::queue message_queue_; + + int handles_[2]{}; + + void log_messages() + { + while (this->console_initialized_ && !this->message_queue_.empty()) + { + std::queue message_queue_copy; + + { + std::lock_guard _(this->mutex_); + message_queue_copy = this->message_queue_; + this->message_queue_ = {}; + } + + while (!message_queue_copy.empty()) + { + log_message(message_queue_copy.front()); + message_queue_copy.pop(); + } + } + + fflush(stdout); + fflush(stderr); + } + + static void log_message(const std::string& message) + { + OutputDebugStringA(message.data()); + game::native::Conbuf_AppendText(message.data()); + } + + void runner() + { + char buffer[1024]; + + while (!this->terminate_runner_ && this->handles_[0]) + { + const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); + if (len > 0) + { + std::lock_guard _(this->mutex_); + this->message_queue_.push(std::string(buffer, len)); + } + else + { + std::this_thread::sleep_for(10ms); + } + } + + std::this_thread::yield(); + } +}; + +REGISTER_MODULE(console) diff --git a/src/module/discord.cpp b/src/module/discord.cpp index ec3e003..627f597 100644 --- a/src/module/discord.cpp +++ b/src/module/discord.cpp @@ -1,50 +1,50 @@ -#include -#include -#include "loader/module_loader.hpp" -#include "scheduler.hpp" -#include "game/game.hpp" - -class discord final : public module -{ -public: - void post_load() override - { - if (game::is_dedi()) return; - - DiscordEventHandlers handlers; - ZeroMemory(&handlers, sizeof(handlers)); - handlers.ready = ready; - handlers.errored = errored; - handlers.disconnected = errored; - handlers.joinGame = nullptr; - handlers.spectateGame = nullptr; - handlers.joinRequest = nullptr; - - Discord_Initialize("531526691319971880", &handlers, 1, nullptr); - - scheduler::on_frame(Discord_RunCallbacks); - } - - void pre_destroy() override - { - Discord_Shutdown(); - } - -private: - static void ready(const DiscordUser* request) - { - DiscordRichPresence discord_presence; - ZeroMemory(&discord_presence, sizeof(discord_presence)); - - discord_presence.state = game::is_mp() ? "Multiplayer" : "Singleplayer"; - discord_presence.instance = 1; - Discord_UpdatePresence(&discord_presence); - } - - static void errored(const int error_code, const char* message) - { - printf("Discord: (%i) %s", error_code, message); - } -}; - -//REGISTER_MODULE(discord) +#include +#include +#include "loader/module_loader.hpp" +#include "scheduler.hpp" +#include "game/game.hpp" + +class discord final : public module +{ +public: + void post_load() override + { + if (game::is_dedi()) return; + + DiscordEventHandlers handlers; + ZeroMemory(&handlers, sizeof(handlers)); + handlers.ready = ready; + handlers.errored = errored; + handlers.disconnected = errored; + handlers.joinGame = nullptr; + handlers.spectateGame = nullptr; + handlers.joinRequest = nullptr; + + Discord_Initialize("531526691319971880", &handlers, 1, nullptr); + + scheduler::on_frame(Discord_RunCallbacks); + } + + void pre_destroy() override + { + Discord_Shutdown(); + } + +private: + static void ready(const DiscordUser* request) + { + DiscordRichPresence discord_presence; + ZeroMemory(&discord_presence, sizeof(discord_presence)); + + discord_presence.state = game::is_mp() ? "Multiplayer" : "Singleplayer"; + discord_presence.instance = 1; + Discord_UpdatePresence(&discord_presence); + } + + static void errored(const int error_code, const char* message) + { + printf("Discord: (%i) %s", error_code, message); + } +}; + +//REGISTER_MODULE(discord) diff --git a/src/module/fastfiles.cpp b/src/module/fastfiles.cpp index 29157bc..9485bf4 100644 --- a/src/module/fastfiles.cpp +++ b/src/module/fastfiles.cpp @@ -1,60 +1,60 @@ -#include -#include "loader/module_loader.hpp" -#include "game/structs.hpp" -#include "game/game.hpp" -#include "utils/hook.hpp" - -static __declspec(naked) void db_load_stub_client(game::native::XZoneInfo*, unsigned int, int) -{ - __asm - { - sub esp, 0Ch - mov eax, [esp + 18h] - - mov ecx, game::native::DB_LoadXAssets - add ecx, 7h - push ecx - retn - } -} - -static __declspec(naked) void db_load_stub_server(game::native::XZoneInfo*, unsigned int, int) -{ - __asm - { - sub esp, 10h - mov eax, [esp + 1Ch] - - mov ecx, game::native::DB_LoadXAssets - add ecx, 7h - push ecx - retn - } -} - -class fastfiles final : public module -{ -public: - void post_load() override - { - utils::hook(game::native::DB_LoadXAssets, db_load_stub, HOOK_JUMP).install()->quick(); - } - -private: - static void db_load_stub(game::native::XZoneInfo* zone_info, const unsigned int zone_count, const int sync) - { - for (unsigned int i = 0; i < zone_count; ++i) - { - if (zone_info[i].name) - { - printf("Loading FastFile: %s (0x%X | 0x%X)\n", zone_info[i].name, zone_info[i].allocFlags, - zone_info[i].freeFlags); - } - } - - if (game::is_dedi()) return db_load_stub_server(zone_info, zone_count, sync); - else return db_load_stub_client(zone_info, zone_count, sync); - } -}; - -REGISTER_MODULE(fastfiles) +#include +#include "loader/module_loader.hpp" +#include "game/structs.hpp" +#include "game/game.hpp" +#include "utils/hook.hpp" + +static __declspec(naked) void db_load_stub_client(game::native::XZoneInfo*, unsigned int, int) +{ + __asm + { + sub esp, 0Ch + mov eax, [esp + 18h] + + mov ecx, game::native::DB_LoadXAssets + add ecx, 7h + push ecx + retn + } +} + +static __declspec(naked) void db_load_stub_server(game::native::XZoneInfo*, unsigned int, int) +{ + __asm + { + sub esp, 10h + mov eax, [esp + 1Ch] + + mov ecx, game::native::DB_LoadXAssets + add ecx, 7h + push ecx + retn + } +} + +class fastfiles final : public module +{ +public: + void post_load() override + { + utils::hook(game::native::DB_LoadXAssets, db_load_stub, HOOK_JUMP).install()->quick(); + } + +private: + static void db_load_stub(game::native::XZoneInfo* zone_info, const unsigned int zone_count, const int sync) + { + for (unsigned int i = 0; i < zone_count; ++i) + { + if (zone_info[i].name) + { + printf("Loading FastFile: %s (0x%X | 0x%X)\n", zone_info[i].name, zone_info[i].allocFlags, + zone_info[i].freeFlags); + } + } + + if (game::is_dedi()) return db_load_stub_server(zone_info, zone_count, sync); + else return db_load_stub_client(zone_info, zone_count, sync); + } +}; + +REGISTER_MODULE(fastfiles) diff --git a/src/module/fov.cpp b/src/module/fov.cpp index ba87d60..ed65f64 100644 --- a/src/module/fov.cpp +++ b/src/module/fov.cpp @@ -1,37 +1,37 @@ -#include -#include "loader/module_loader.hpp" -#include "utils/hook.hpp" -#include "game/game.hpp" - -class fov final : public module -{ -public: - void post_load() override - { - if (game::is_dedi()) return; - - // Set dvar flag - utils::hook::set(SELECT_VALUE(0x4302C5, 0x455155, 0), 0x1 | (game::is_mp() ? 0x40 : 0)); - - if (game::is_mp()) - { - // Set dvar limit - static const auto cg_fov_limit = 90.0f; - utils::hook::set(0x455148, &cg_fov_limit); - - // Prevent value change via internal scripts - utils::hook(0x4698BA, &set_server_command_dvar_stub, HOOK_CALL).install()->quick(); - } - } - -private: - static void set_server_command_dvar_stub(const char* dvar, const char* value) - { - if (strcmp(dvar, "cg_fov") != 0 || strcmp(value, "65") != 0) - { - game::native::Dvar_SetFromStringByName(dvar, value); - } - } -}; - -REGISTER_MODULE(fov) +#include +#include "loader/module_loader.hpp" +#include "utils/hook.hpp" +#include "game/game.hpp" + +class fov final : public module +{ +public: + void post_load() override + { + if (game::is_dedi()) return; + + // Set dvar flag + utils::hook::set(SELECT_VALUE(0x4302C5, 0x455155, 0), 0x1 | (game::is_mp() ? 0x40 : 0)); + + if (game::is_mp()) + { + // Set dvar limit + static const auto cg_fov_limit = 90.0f; + utils::hook::set(0x455148, &cg_fov_limit); + + // Prevent value change via internal scripts + utils::hook(0x4698BA, &set_server_command_dvar_stub, HOOK_CALL).install()->quick(); + } + } + +private: + static void set_server_command_dvar_stub(const char* dvar, const char* value) + { + if (strcmp(dvar, "cg_fov") != 0 || strcmp(value, "65") != 0) + { + game::native::Dvar_SetFromStringByName(dvar, value); + } + } +}; + +REGISTER_MODULE(fov) diff --git a/src/module/patches.cpp b/src/module/patches.cpp index 6285af9..babe16e 100644 --- a/src/module/patches.cpp +++ b/src/module/patches.cpp @@ -1,61 +1,61 @@ -#include -#include "loader/module_loader.hpp" -#include "utils/hook.hpp" -#include "game/game.hpp" - -class patches final : public module -{ -public: - void post_load() override - { - if (!game::is_dedi()) this->patch_clients(); - - if (game::is_sp()) this->patch_sp(); - else if (game::is_mp()) this->patch_mp(); - else if (game::is_dedi()) this->patch_dedi(); - - utils::hook(game::native::_longjmp, long_jump_stub, HOOK_JUMP).install()->quick(); - } - -private: - void patch_clients() const - { - // Remove improper quit check - utils::hook::nop(SELECT_VALUE(0x53444A, 0x5CCDC0, 0), 9); - - // Ignore sdm files - utils::hook::nop(SELECT_VALUE(0x4438BA, 0x6371EA, 0), 2); - } - - void patch_sp() const - { - // SP doesn't initialize WSA - WSADATA wsa_data; - WSAStartup(MAKEWORD(2, 2), &wsa_data); - - // Disable remote storage - utils::hook::set(0x663B5A, 0xEB); - utils::hook::set(0x663C54, 0xEB); - } - - void patch_mp() const - { - } - - void patch_dedi() const - { - } - - static __declspec(noreturn) void long_jump_stub(jmp_buf buf, const int value) noexcept(false) - { -#ifdef DEBUG - { - printf("Unwinding the stack...\n"); - } -#endif - - longjmp(buf, value); - } -}; - -REGISTER_MODULE(patches) +#include +#include "loader/module_loader.hpp" +#include "utils/hook.hpp" +#include "game/game.hpp" + +class patches final : public module +{ +public: + void post_load() override + { + if (!game::is_dedi()) this->patch_clients(); + + if (game::is_sp()) this->patch_sp(); + else if (game::is_mp()) this->patch_mp(); + else if (game::is_dedi()) this->patch_dedi(); + + utils::hook(game::native::_longjmp, long_jump_stub, HOOK_JUMP).install()->quick(); + } + +private: + void patch_clients() const + { + // Remove improper quit check + utils::hook::nop(SELECT_VALUE(0x53444A, 0x5CCDC0, 0), 9); + + // Ignore sdm files + utils::hook::nop(SELECT_VALUE(0x4438BA, 0x6371EA, 0), 2); + } + + void patch_sp() const + { + // SP doesn't initialize WSA + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 2), &wsa_data); + + // Disable remote storage + utils::hook::set(0x663B5A, 0xEB); + utils::hook::set(0x663C54, 0xEB); + } + + void patch_mp() const + { + } + + void patch_dedi() const + { + } + + static __declspec(noreturn) void long_jump_stub(jmp_buf buf, const int value) noexcept(false) + { +#ifdef DEBUG + { + printf("Unwinding the stack...\n"); + } +#endif + + longjmp(buf, value); + } +}; + +REGISTER_MODULE(patches) diff --git a/src/module/scripting.cpp b/src/module/scripting.cpp index cc039da..cb4ffed 100644 --- a/src/module/scripting.cpp +++ b/src/module/scripting.cpp @@ -1,162 +1,162 @@ -#include -#include "loader/module_loader.hpp" -#include "utils/hook.hpp" -#include "utils/io.hpp" -#include "game/scripting/context.hpp" -#include "scheduler.hpp" - -class scripting final : public module -{ -public: - void post_load() override - { - start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); - - stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); - - utils::hook(SELECT_VALUE(0x4F9706, 0x5772A0, 0x4FAB88), &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(SELECT_VALUE(0x4FFA48, 0x5774AB, 0x4FEFD7), &frame_stub, HOOK_CALL).install()->quick(); // Only relevant one? - - utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), &vm_notify_stub, HOOK_CALL).install()->quick(); - utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), &vm_notify_stub, HOOK_CALL).install()->quick(); - - if (game::is_sp()) - { - utils::hook(0x50C57E, &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(0x6523A3, &frame_stub, HOOK_CALL).install()->quick(); - utils::hook(0x5145D2, &frame_stub, HOOK_CALL).install()->quick(); - - utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); - } - } - - void pre_destroy() override - { - this->scripts_.clear(); - } - -private: - std::vector> scripts_; - - void load_scripts() - { - const auto scripts = utils::io::list_files("open-iw5/scripts/"); - - for (const auto& script : scripts) - { - if (script.substr(script.find_last_of('.') + 1) == "chai") - { - try - { - auto context = std::make_unique(); - context->get_chai()->eval_file(script); - this->scripts_.push_back(std::move(context)); - } - catch (chaiscript::exception::eval_error& e) - { - throw std::runtime_error(e.pretty_print()); - } - } - } - } - - void start_execution() - { - try - { - this->load_scripts(); - } - catch (std::exception& e) - { - propagate_error(e); - } - } - - void stop_execution() - { - this->scripts_.clear(); - } - - void run_frame() - { - for (const auto& script : this->scripts_) - { - script->get_scheduler()->run_frame(); - } - } - - void dispatch(game::scripting::event* event) - { - for (const auto& script : this->scripts_) - { - script->get_event_handler()->dispatch(event); - } - } - - static utils::hook start_hook_; - static utils::hook stop_hook_; - - static void propagate_error(const std::exception& e) - { - printf("\n******* Script execution error *******\n"); - printf("%s\n", e.what()); - printf("**************************************\n\n"); - - scheduler::error("Script execution error\n(see console for actual details)\n", 5); - } - - static void start_execution_stub() - { - module_loader::get()->start_execution(); - reinterpret_cast(start_hook_.get_original())(); - } - - static void stop_execution_stub() - { - module_loader::get()->stop_execution(); - reinterpret_cast(stop_hook_.get_original())(); - } - - static void vm_notify_stub(const unsigned int notify_id, const unsigned short type, - game::native::VariableValue* stack) - { - try - { - game::scripting::event e; - e.name = game::native::SL_ConvertToString(type); - e.entity_id = notify_id; - - if (e.name == "touch") return; // Skip that for now - - //printf("%X: %s\n", e.entity_id, e.name.data()); - - for (auto value = stack; value->type != game::native::SCRIPT_END; --value) - { - e.arguments.emplace_back(*value); - } - - module_loader::get()->dispatch(&e); - } - catch (std::exception& e) - { - propagate_error(e); - } - - game::native::VM_Notify(notify_id, type, stack); - } - - static int frame_stub(int a1, int a2) - { - module_loader::get()->run_frame(); - return game::native::G_RunFrame(a1, a2); - } -}; - -utils::hook scripting::start_hook_; -utils::hook scripting::stop_hook_; - -REGISTER_MODULE(scripting) +#include +#include "loader/module_loader.hpp" +#include "utils/hook.hpp" +#include "utils/io.hpp" +#include "game/scripting/context.hpp" +#include "scheduler.hpp" + +class scripting final : public module +{ +public: + void post_load() override + { + start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + utils::hook(SELECT_VALUE(0x4F9706, 0x5772A0, 0x4FAB88), &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x4FFA48, 0x5774AB, 0x4FEFD7), &frame_stub, HOOK_CALL).install()->quick(); // Only relevant one? + + utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), &vm_notify_stub, HOOK_CALL).install()->quick(); + utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), &vm_notify_stub, HOOK_CALL).install()->quick(); + + if (game::is_sp()) + { + utils::hook(0x50C57E, &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x6523A3, &frame_stub, HOOK_CALL).install()->quick(); + utils::hook(0x5145D2, &frame_stub, HOOK_CALL).install()->quick(); + + utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); + } + } + + void pre_destroy() override + { + this->scripts_.clear(); + } + +private: + std::vector> scripts_; + + void load_scripts() + { + const auto scripts = utils::io::list_files("open-iw5/scripts/"); + + for (const auto& script : scripts) + { + if (script.substr(script.find_last_of('.') + 1) == "chai") + { + try + { + auto context = std::make_unique(); + context->get_chai()->eval_file(script); + this->scripts_.push_back(std::move(context)); + } + catch (chaiscript::exception::eval_error& e) + { + throw std::runtime_error(e.pretty_print()); + } + } + } + } + + void start_execution() + { + try + { + this->load_scripts(); + } + catch (std::exception& e) + { + propagate_error(e); + } + } + + void stop_execution() + { + this->scripts_.clear(); + } + + void run_frame() + { + for (const auto& script : this->scripts_) + { + script->get_scheduler()->run_frame(); + } + } + + void dispatch(game::scripting::event* event) + { + for (const auto& script : this->scripts_) + { + script->get_event_handler()->dispatch(event); + } + } + + static utils::hook start_hook_; + static utils::hook stop_hook_; + + static void propagate_error(const std::exception& e) + { + printf("\n******* Script execution error *******\n"); + printf("%s\n", e.what()); + printf("**************************************\n\n"); + + scheduler::error("Script execution error\n(see console for actual details)\n", 5); + } + + static void start_execution_stub() + { + module_loader::get()->start_execution(); + reinterpret_cast(start_hook_.get_original())(); + } + + static void stop_execution_stub() + { + module_loader::get()->stop_execution(); + reinterpret_cast(stop_hook_.get_original())(); + } + + static void vm_notify_stub(const unsigned int notify_id, const unsigned short type, + game::native::VariableValue* stack) + { + try + { + game::scripting::event e; + e.name = game::native::SL_ConvertToString(type); + e.entity_id = notify_id; + + if (e.name == "touch") return; // Skip that for now + + //printf("%X: %s\n", e.entity_id, e.name.data()); + + for (auto value = stack; value->type != game::native::SCRIPT_END; --value) + { + e.arguments.emplace_back(*value); + } + + module_loader::get()->dispatch(&e); + } + catch (std::exception& e) + { + propagate_error(e); + } + + game::native::VM_Notify(notify_id, type, stack); + } + + static int frame_stub(int a1, int a2) + { + module_loader::get()->run_frame(); + return game::native::G_RunFrame(a1, a2); + } +}; + +utils::hook scripting::start_hook_; +utils::hook scripting::stop_hook_; + +REGISTER_MODULE(scripting) diff --git a/src/module/security.cpp b/src/module/security.cpp index 34f3b88..9b304d2 100644 --- a/src/module/security.cpp +++ b/src/module/security.cpp @@ -1,25 +1,25 @@ -#include -#include "loader/module_loader.hpp" -#include "game/game.hpp" -#include "utils/hook.hpp" - -class security final : public module -{ -public: - void post_load() override - { - if(game::is_mp()) - { - utils::hook(0x4AECD4, read_p2p_auth_ticket_stub, HOOK_JUMP).install()->quick(); - } - } - -private: - static void read_p2p_auth_ticket_stub(game::native::msg_t* msg, void* data, const int len) - { - if (len < 0) return; - return game::native::MSG_ReadData(msg, data, std::min(len, 200)); - } -}; - -REGISTER_MODULE(security) +#include +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "utils/hook.hpp" + +class security final : public module +{ +public: + void post_load() override + { + if(game::is_mp()) + { + utils::hook(0x4AECD4, read_p2p_auth_ticket_stub, HOOK_JUMP).install()->quick(); + } + } + +private: + static void read_p2p_auth_ticket_stub(game::native::msg_t* msg, void* data, const int len) + { + if (len < 0) return; + return game::native::MSG_ReadData(msg, data, std::min(len, 200)); + } +}; + +REGISTER_MODULE(security) diff --git a/src/module/steam_proxy.cpp b/src/module/steam_proxy.cpp index 353bfc5..014e60a 100644 --- a/src/module/steam_proxy.cpp +++ b/src/module/steam_proxy.cpp @@ -1,178 +1,178 @@ -#include -#include "loader/module_loader.hpp" -#include "game/game.hpp" -#include "utils/nt.hpp" -#include "steam/steam.hpp" -#include "steam/interface.hpp" -#include "utils/string.hpp" -#include "scheduler.hpp" - -class steam_proxy final : public module -{ -public: - void post_start() override - { - if (game::is_dedi()) return; - - this->run_mod(); - this->load_client(); - - this->clean_up_on_error(); - } - - void post_load() override - { - if (game::is_dedi()) return; - - try - { - if (game::is_sp()) - { - this->start_mod("Open-IW5 Singleplayer", 42680); - } - else if (game::is_mp()) - { - this->start_mod("Open-IW5 Multiplayer", 42690); - } - } - catch (std::exception& e) - { - printf("Steam: %s\n", e.what()); - } - } - - void pre_destroy() override - { - if (this->steam_client_module_) - { - if (this->steam_pipe_) - { - if (this->global_user_) - { - this->steam_client_module_.invoke("Steam_ReleaseUser", this->steam_pipe_, this->global_user_); - } - - this->steam_client_module_.invoke("Steam_BReleaseSteamPipe", this->steam_pipe_); - } - } - } - -private: - utils::nt::module steam_client_module_; - - steam::interface client_engine_; - steam::interface client_user_; - steam::interface client_utils_; - - void* steam_pipe_ = nullptr; - void* global_user_ = nullptr; - - void run_mod() const - { - const auto command = "-proc "; - const char* parent_proc = strstr(GetCommandLineA(), command); - - if (parent_proc) - { - const auto pid = atoi(parent_proc + strlen(command)); - const auto process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid); - if (process_handle && process_handle != INVALID_HANDLE_VALUE) - { - WaitForSingleObject(process_handle, INFINITE); - CloseHandle(process_handle); - } - - module_loader::trigger_premature_shutdown(); - } - } - - void* load_client_engine() const - { - if (!this->steam_client_module_) return nullptr; - - for (auto i = 1; i > 0; ++i) - { - std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i); - const auto client_engine = this->steam_client_module_ - .invoke("CreateInterface", name.data(), nullptr); - if (client_engine) return client_engine; - } - - return nullptr; - } - - void load_client() - { - const auto steam_path = ::steam::get_steam_install_directory(); - if (steam_path.empty()) return; - - utils::nt::module::load(steam_path + "tier0_s.dll"); - utils::nt::module::load(steam_path + "vstdlib_s.dll"); - this->steam_client_module_ = utils::nt::module::load(steam_path + "steamclient.dll"); - if (!this->steam_client_module_) return; - - this->client_engine_ = load_client_engine(); - if (!this->client_engine_) return; - - this->steam_pipe_ = this->steam_client_module_.invoke("Steam_CreateSteamPipe"); - this->global_user_ = this->steam_client_module_.invoke("Steam_ConnectToGlobalUser", this->steam_pipe_); - this->client_user_ = this->client_engine_.invoke(8, this->steam_pipe_, this->global_user_, - "CLIENTUSER_INTERFACE_VERSION001"); // GetIClientUser - this->client_utils_ = this->client_engine_.invoke(13, this->steam_pipe_, - "CLIENTUTILS_INTERFACE_VERSION001"); // GetIClientUtils - } - - void start_mod(const std::string& title, size_t app_id) - { - if (!this->client_utils_ || !this->client_user_) return; - - if (!this->client_user_.invoke("BIsSubscribedApp", app_id)) - { - app_id = 480; // Spacewar - } - - this->client_utils_.invoke("SetAppIDForCurrentPipe", app_id, false); - - utils::nt::module self; - const auto path = self.get_path(); - - char our_directory[MAX_PATH] = {0}; - GetCurrentDirectoryA(sizeof(our_directory), our_directory); - - const std::string cmdline = utils::string::va("\"%s\" -proc %d", path.data(), GetCurrentProcessId()); - - game_id game_id; - game_id.raw.type = 1; // k_EGameIDTypeGameMod - game_id.raw.app_id = app_id & 0xFFFFFF; - - const auto mod_id = "OIW5"; - game_id.raw.mod_id = *reinterpret_cast(mod_id) | 0x80000000; - - this->client_user_.invoke("SpawnProcess", self.get_path().data(), cmdline.data(), 0, our_directory, - game_id.bits, title.data(), app_id, 0, 0); - } - - void clean_up_on_error() - { - if (this->steam_client_module_ - && this->steam_pipe_ - && this->global_user_ - && this->steam_client_module_.invoke("Steam_BConnected", this->global_user_, this->steam_pipe_) - && this->steam_client_module_.invoke("Steam_BLoggedOn", this->global_user_, this->steam_pipe_)) - { - scheduler::once(std::bind(&steam_proxy::clean_up_on_error, this)); - return; - } - - this->client_engine_ = nullptr; - this->client_user_ = nullptr; - this->client_utils_ = nullptr; - - this->steam_pipe_ = nullptr; - this->global_user_ = nullptr; - - this->steam_client_module_ = utils::nt::module{nullptr}; - } -}; - -//REGISTER_MODULE(steam_proxy) +#include +#include "loader/module_loader.hpp" +#include "game/game.hpp" +#include "utils/nt.hpp" +#include "steam/steam.hpp" +#include "steam/interface.hpp" +#include "utils/string.hpp" +#include "scheduler.hpp" + +class steam_proxy final : public module +{ +public: + void post_start() override + { + if (game::is_dedi()) return; + + this->run_mod(); + this->load_client(); + + this->clean_up_on_error(); + } + + void post_load() override + { + if (game::is_dedi()) return; + + try + { + if (game::is_sp()) + { + this->start_mod("Open-IW5 Singleplayer", 42680); + } + else if (game::is_mp()) + { + this->start_mod("Open-IW5 Multiplayer", 42690); + } + } + catch (std::exception& e) + { + printf("Steam: %s\n", e.what()); + } + } + + void pre_destroy() override + { + if (this->steam_client_module_) + { + if (this->steam_pipe_) + { + if (this->global_user_) + { + this->steam_client_module_.invoke("Steam_ReleaseUser", this->steam_pipe_, this->global_user_); + } + + this->steam_client_module_.invoke("Steam_BReleaseSteamPipe", this->steam_pipe_); + } + } + } + +private: + utils::nt::module steam_client_module_; + + steam::interface client_engine_; + steam::interface client_user_; + steam::interface client_utils_; + + void* steam_pipe_ = nullptr; + void* global_user_ = nullptr; + + void run_mod() const + { + const auto command = "-proc "; + const char* parent_proc = strstr(GetCommandLineA(), command); + + if (parent_proc) + { + const auto pid = atoi(parent_proc + strlen(command)); + const auto process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process_handle && process_handle != INVALID_HANDLE_VALUE) + { + WaitForSingleObject(process_handle, INFINITE); + CloseHandle(process_handle); + } + + module_loader::trigger_premature_shutdown(); + } + } + + void* load_client_engine() const + { + if (!this->steam_client_module_) return nullptr; + + for (auto i = 1; i > 0; ++i) + { + std::string name = utils::string::va("CLIENTENGINE_INTERFACE_VERSION%03i", i); + const auto client_engine = this->steam_client_module_ + .invoke("CreateInterface", name.data(), nullptr); + if (client_engine) return client_engine; + } + + return nullptr; + } + + void load_client() + { + const auto steam_path = ::steam::get_steam_install_directory(); + if (steam_path.empty()) return; + + utils::nt::module::load(steam_path + "tier0_s.dll"); + utils::nt::module::load(steam_path + "vstdlib_s.dll"); + this->steam_client_module_ = utils::nt::module::load(steam_path + "steamclient.dll"); + if (!this->steam_client_module_) return; + + this->client_engine_ = load_client_engine(); + if (!this->client_engine_) return; + + this->steam_pipe_ = this->steam_client_module_.invoke("Steam_CreateSteamPipe"); + this->global_user_ = this->steam_client_module_.invoke("Steam_ConnectToGlobalUser", this->steam_pipe_); + this->client_user_ = this->client_engine_.invoke(8, this->steam_pipe_, this->global_user_, + "CLIENTUSER_INTERFACE_VERSION001"); // GetIClientUser + this->client_utils_ = this->client_engine_.invoke(13, this->steam_pipe_, + "CLIENTUTILS_INTERFACE_VERSION001"); // GetIClientUtils + } + + void start_mod(const std::string& title, size_t app_id) + { + if (!this->client_utils_ || !this->client_user_) return; + + if (!this->client_user_.invoke("BIsSubscribedApp", app_id)) + { + app_id = 480; // Spacewar + } + + this->client_utils_.invoke("SetAppIDForCurrentPipe", app_id, false); + + utils::nt::module self; + const auto path = self.get_path(); + + char our_directory[MAX_PATH] = {0}; + GetCurrentDirectoryA(sizeof(our_directory), our_directory); + + const std::string cmdline = utils::string::va("\"%s\" -proc %d", path.data(), GetCurrentProcessId()); + + game_id game_id; + game_id.raw.type = 1; // k_EGameIDTypeGameMod + game_id.raw.app_id = app_id & 0xFFFFFF; + + const auto mod_id = "OIW5"; + game_id.raw.mod_id = *reinterpret_cast(mod_id) | 0x80000000; + + this->client_user_.invoke("SpawnProcess", self.get_path().data(), cmdline.data(), 0, our_directory, + game_id.bits, title.data(), app_id, 0, 0); + } + + void clean_up_on_error() + { + if (this->steam_client_module_ + && this->steam_pipe_ + && this->global_user_ + && this->steam_client_module_.invoke("Steam_BConnected", this->global_user_, this->steam_pipe_) + && this->steam_client_module_.invoke("Steam_BLoggedOn", this->global_user_, this->steam_pipe_)) + { + scheduler::once(std::bind(&steam_proxy::clean_up_on_error, this)); + return; + } + + this->client_engine_ = nullptr; + this->client_user_ = nullptr; + this->client_utils_ = nullptr; + + this->steam_pipe_ = nullptr; + this->global_user_ = nullptr; + + this->steam_client_module_ = utils::nt::module{nullptr}; + } +}; + +//REGISTER_MODULE(steam_proxy) diff --git a/src/resource.hpp b/src/resource.hpp index 9f35f65..fc1d4a0 100644 --- a/src/resource.hpp +++ b/src/resource.hpp @@ -1,16 +1,16 @@ -#pragma once - -#define BINARY_SP 300 -#define BINARY_MP 301 - -#define DW_HEATMAP 302 -#define DW_MOTD 303 -#define DW_IMG 304 -#define DW_WAD 305 -#define DW_PLAYLIST 306 -#define DW_CONFIG 307 -#define DW_IOTD_TXT 308 -#define DW_IOTD_IMG 309 - -#define MENU_MAIN 310 -#define MENU_SETTINGS 311 +#pragma once + +#define BINARY_SP 300 +#define BINARY_MP 301 + +#define DW_HEATMAP 302 +#define DW_MOTD 303 +#define DW_IMG 304 +#define DW_WAD 305 +#define DW_PLAYLIST 306 +#define DW_CONFIG 307 +#define DW_IOTD_TXT 308 +#define DW_IOTD_IMG 309 + +#define MENU_MAIN 310 +#define MENU_SETTINGS 311 diff --git a/src/resource.rc b/src/resource.rc index 72a3912..fab360c 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -1,117 +1,117 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "windows.h" -#include "resource.hpp" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "#include ""windows.h""\r\n" - "\0" -END - -2 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE VFT_DLL - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "momo5502" - VALUE "FileDescription", "Open-IW5" - VALUE "FileVersion", "1.0.0.0" - VALUE "InternalName", "Open-IW5" - VALUE "LegalCopyright", "All rights reserved." - VALUE "OriginalFilename", "open-iw5.exe" - VALUE "ProductName", "open-iw5" - VALUE "ProductVersion", "1.0.0.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -///////////////////////////////////////////////////////////////////////////// -// -// Binary Data -// - -102 ICON "resources/icon.ico" - -BINARY_SP RCDATA "resources/iw5sp.exe.diff" -BINARY_MP RCDATA "resources/iw5mp.exe.diff" - -DW_HEATMAP RCDATA "resources/dw/heatmap.raw" -DW_MOTD RCDATA "resources/dw/motd-english.txt" -DW_IMG RCDATA "resources/dw/online_mp.img" -DW_WAD RCDATA "resources/dw/online_tu14_mp_english.wad" -DW_PLAYLIST RCDATA "resources/dw/playlists.aggr" -DW_CONFIG RCDATA "resources/dw/social_tu1.cfg" -DW_IOTD_TXT RCDATA "resources/dw/iotd-english.txt" -DW_IOTD_IMG RCDATA "resources/dw/iotd-english.jpg" - -MENU_MAIN RCDATA "resources/main.html" -MENU_SETTINGS RCDATA "resources/settings.html" - - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +#include "resource.hpp" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\n" + "\0" +END + +2 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "momo5502" + VALUE "FileDescription", "Open-IW5" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "Open-IW5" + VALUE "LegalCopyright", "All rights reserved." + VALUE "OriginalFilename", "open-iw5.exe" + VALUE "ProductName", "open-iw5" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Binary Data +// + +102 ICON "resources/icon.ico" + +BINARY_SP RCDATA "resources/iw5sp.exe.diff" +BINARY_MP RCDATA "resources/iw5mp.exe.diff" + +DW_HEATMAP RCDATA "resources/dw/heatmap.raw" +DW_MOTD RCDATA "resources/dw/motd-english.txt" +DW_IMG RCDATA "resources/dw/online_mp.img" +DW_WAD RCDATA "resources/dw/online_tu14_mp_english.wad" +DW_PLAYLIST RCDATA "resources/dw/playlists.aggr" +DW_CONFIG RCDATA "resources/dw/social_tu1.cfg" +DW_IOTD_TXT RCDATA "resources/dw/iotd-english.txt" +DW_IOTD_IMG RCDATA "resources/dw/iotd-english.jpg" + +MENU_MAIN RCDATA "resources/main.html" +MENU_SETTINGS RCDATA "resources/settings.html" + + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/resources/main.html b/src/resources/main.html index 3b519ac..ad9c22b 100644 --- a/src/resources/main.html +++ b/src/resources/main.html @@ -1,131 +1,131 @@ - - - - - - - Open-IW5 - - - - - -
-
- Singleplayer - -
- -
- Multiplayer - -
-
- - - - - - + + + + + + + Open-IW5 + + + + + +
+
+ Singleplayer + +
+ +
+ Multiplayer + +
+
+ + + + + + \ No newline at end of file diff --git a/src/resources/settings.html b/src/resources/settings.html index 86093fa..cca18eb 100644 --- a/src/resources/settings.html +++ b/src/resources/settings.html @@ -1,44 +1,44 @@ - - - - - - - Open-IW5 Settings - - - - - -
-

No settings, yet!

-
- - + + + + + + + Open-IW5 Settings + + + + + +
+

No settings, yet!

+
+ + \ No newline at end of file diff --git a/src/steam/interfaces/user.cpp b/src/steam/interfaces/user.cpp index 25d310a..8240a19 100644 --- a/src/steam/interfaces/user.cpp +++ b/src/steam/interfaces/user.cpp @@ -1,143 +1,143 @@ -#include -#include "steam/steam.hpp" -#include "module/dw.hpp" - -namespace steam -{ - std::string auth_ticket; - - int user::GetHSteamUser() - { - return NULL; - } - - bool user::LoggedOn() - { - return true; - } - - steam_id user::GetSteamID() - { - steam_id id; - id.bits = 0x110000100000000 | (0x1377 & ~0x80000000); - return id; - } - - int user::InitiateGameConnection(void* pAuthBlob, int cbMaxAuthBlob, steam_id steamIDGameServer, - unsigned int unIPServer, unsigned short usPortServer, bool bSecure) - { - return 0; - } - - void user::TerminateGameConnection(unsigned int unIPServer, unsigned short usPortServer) - { - } - - void user::TrackAppUsageEvent(steam_id gameID, int eAppUsageEvent, const char* pchExtraInfo) - { - } - - bool user::GetUserDataFolder(char* pchBuffer, int cubBuffer) - { - return false; - } - - void user::StartVoiceRecording() - { - } - - void user::StopVoiceRecording() - { - } - - int user::GetAvailableVoice(unsigned int* pcbCompressed, unsigned int* pcbUncompressed, - unsigned int nUncompressedVoiceDesiredSampleRate) - { - return 0; - } - - int user::GetVoice(bool bWantCompressed, void* pDestBuffer, unsigned int cbDestBufferSize, - unsigned int* nBytesWritten, bool bWantUncompressed, void* pUncompressedDestBuffer, - unsigned int cbUncompressedDestBufferSize, unsigned int* nUncompressBytesWritten, - unsigned int nUncompressedVoiceDesiredSampleRate) - { - return 0; - } - - int user::DecompressVoice(void* pCompressed, unsigned int cbCompressed, void* pDestBuffer, - unsigned int cbDestBufferSize, unsigned int* nBytesWritten) - { - return 0; - } - - unsigned int user::GetVoiceOptimalSampleRate() - { - return 0; - } - - unsigned int user::GetAuthSessionTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket) - { - return 0; - } - - int user::BeginAuthSession(const void* pAuthTicket, int cbAuthTicket, steam_id steamID) - { - return 0; - } - - void user::EndAuthSession(steam_id steamID) - { - } - - void user::CancelAuthTicket(unsigned int hAuthTicket) - { - } - - unsigned int user::UserHasLicenseForApp(steam_id steamID, unsigned int appID) - { - return 0; - } - - bool user::BIsBehindNAT() - { - return false; - } - - void user::AdvertiseGame(steam_id steamIDGameServer, unsigned int unIPServer, unsigned short usPortServer) - { - } - - unsigned __int64 user::RequestEncryptedAppTicket(void* pUserData, int cbUserData) - { - // Generate the authentication ticket - const auto id = this->GetSteamID(); - - auth_ticket = "Open-IW5"; - auth_ticket.resize(32); - auth_ticket.append(reinterpret_cast(pUserData), cbUserData); - auth_ticket.append(reinterpret_cast(&id.bits), sizeof(id.bits)); - - // Create the call response - const auto result = callbacks::register_call(); - auto retvals = static_cast(calloc(1, sizeof(encrypted_app_ticket_response))); - //::Utils::Memory::AllocateArray(); - retvals->m_e_result = 1; - - // Return the call response - callbacks::return_call(retvals, sizeof(encrypted_app_ticket_response), - encrypted_app_ticket_response::callback_id, result); - - return result; - } - - bool user::GetEncryptedAppTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket) - { - if (cbMaxTicket < 0 || auth_ticket.empty()) return false; - - const auto size = std::min(size_t(cbMaxTicket), auth_ticket.size()); - std::memcpy(pTicket, auth_ticket.data(), size); - *pcbTicket = size; - - return true; - } -} +#include +#include "steam/steam.hpp" +#include "module/dw.hpp" + +namespace steam +{ + std::string auth_ticket; + + int user::GetHSteamUser() + { + return NULL; + } + + bool user::LoggedOn() + { + return true; + } + + steam_id user::GetSteamID() + { + steam_id id; + id.bits = 0x110000100000000 | (0x1377 & ~0x80000000); + return id; + } + + int user::InitiateGameConnection(void* pAuthBlob, int cbMaxAuthBlob, steam_id steamIDGameServer, + unsigned int unIPServer, unsigned short usPortServer, bool bSecure) + { + return 0; + } + + void user::TerminateGameConnection(unsigned int unIPServer, unsigned short usPortServer) + { + } + + void user::TrackAppUsageEvent(steam_id gameID, int eAppUsageEvent, const char* pchExtraInfo) + { + } + + bool user::GetUserDataFolder(char* pchBuffer, int cubBuffer) + { + return false; + } + + void user::StartVoiceRecording() + { + } + + void user::StopVoiceRecording() + { + } + + int user::GetAvailableVoice(unsigned int* pcbCompressed, unsigned int* pcbUncompressed, + unsigned int nUncompressedVoiceDesiredSampleRate) + { + return 0; + } + + int user::GetVoice(bool bWantCompressed, void* pDestBuffer, unsigned int cbDestBufferSize, + unsigned int* nBytesWritten, bool bWantUncompressed, void* pUncompressedDestBuffer, + unsigned int cbUncompressedDestBufferSize, unsigned int* nUncompressBytesWritten, + unsigned int nUncompressedVoiceDesiredSampleRate) + { + return 0; + } + + int user::DecompressVoice(void* pCompressed, unsigned int cbCompressed, void* pDestBuffer, + unsigned int cbDestBufferSize, unsigned int* nBytesWritten) + { + return 0; + } + + unsigned int user::GetVoiceOptimalSampleRate() + { + return 0; + } + + unsigned int user::GetAuthSessionTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket) + { + return 0; + } + + int user::BeginAuthSession(const void* pAuthTicket, int cbAuthTicket, steam_id steamID) + { + return 0; + } + + void user::EndAuthSession(steam_id steamID) + { + } + + void user::CancelAuthTicket(unsigned int hAuthTicket) + { + } + + unsigned int user::UserHasLicenseForApp(steam_id steamID, unsigned int appID) + { + return 0; + } + + bool user::BIsBehindNAT() + { + return false; + } + + void user::AdvertiseGame(steam_id steamIDGameServer, unsigned int unIPServer, unsigned short usPortServer) + { + } + + unsigned __int64 user::RequestEncryptedAppTicket(void* pUserData, int cbUserData) + { + // Generate the authentication ticket + const auto id = this->GetSteamID(); + + auth_ticket = "Open-IW5"; + auth_ticket.resize(32); + auth_ticket.append(reinterpret_cast(pUserData), cbUserData); + auth_ticket.append(reinterpret_cast(&id.bits), sizeof(id.bits)); + + // Create the call response + const auto result = callbacks::register_call(); + auto retvals = static_cast(calloc(1, sizeof(encrypted_app_ticket_response))); + //::Utils::Memory::AllocateArray(); + retvals->m_e_result = 1; + + // Return the call response + callbacks::return_call(retvals, sizeof(encrypted_app_ticket_response), + encrypted_app_ticket_response::callback_id, result); + + return result; + } + + bool user::GetEncryptedAppTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket) + { + if (cbMaxTicket < 0 || auth_ticket.empty()) return false; + + const auto size = std::min(size_t(cbMaxTicket), auth_ticket.size()); + std::memcpy(pTicket, auth_ticket.data(), size); + *pcbTicket = size; + + return true; + } +} diff --git a/tools/premake5.exe b/tools/premake5.exe index 225849981044d6c19aef04b8125c2c96e626ac57..9048d51e09da5bde02e6337dcfaa603a2cebf292 100644 GIT binary patch literal 1362432 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