diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0080c323 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs +.vscode +build/ +usergenerate.bat \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0e402752 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/minhook"] + path = deps/minhook + url = https://github.com/TsudaKageyu/minhook.git +[submodule "deps/asmjit"] + path = deps/asmjit + url = https://github.com/asmjit/asmjit.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..91dcd00f --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +All rights reserved. +Nobody may use anything from here. \ No newline at end of file diff --git a/deps/asmjit b/deps/asmjit new file mode 160000 index 00000000..a4cb51b5 --- /dev/null +++ b/deps/asmjit @@ -0,0 +1 @@ +Subproject commit a4cb51b532af0f8137c4182914244c3b05d7745f diff --git a/deps/minhook b/deps/minhook new file mode 160000 index 00000000..4a455528 --- /dev/null +++ b/deps/minhook @@ -0,0 +1 @@ +Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18 diff --git a/deps/premake/asmjit.lua b/deps/premake/asmjit.lua new file mode 100644 index 00000000..ee932594 --- /dev/null +++ b/deps/premake/asmjit.lua @@ -0,0 +1,34 @@ +asmjit = { + source = path.join(dependencies.basePath, "asmjit"), +} + +function asmjit.import() + links { "asmjit" } + asmjit.includes() +end + +function asmjit.includes() + includedirs { + path.join(asmjit.source, "src") + } + + defines { + "ASMJIT_STATIC" + } +end + +function asmjit.project() + project "asmjit" + language "C++" + + asmjit.includes() + + files { + path.join(asmjit.source, "src/**.cpp"), + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, asmjit) diff --git a/deps/premake/minhook.lua b/deps/premake/minhook.lua new file mode 100644 index 00000000..396d4d38 --- /dev/null +++ b/deps/premake/minhook.lua @@ -0,0 +1,31 @@ +minhook = { + source = path.join(dependencies.basePath, "minhook"), +} + +function minhook.import() + links { "minhook" } + minhook.includes() +end + +function minhook.includes() + includedirs { + path.join(minhook.source, "include") + } +end + +function minhook.project() + project "minhook" + language "C" + + minhook.includes() + + files { + path.join(minhook.source, "src/**.h"), + path.join(minhook.source, "src/**.c"), + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, minhook) diff --git a/generate.bat b/generate.bat new file mode 100644 index 00000000..06c74119 --- /dev/null +++ b/generate.bat @@ -0,0 +1,3 @@ +@echo off +git submodule update --init --recursive +tools\premake5 %* vs2022 \ No newline at end of file diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 00000000..9b0107e3 --- /dev/null +++ b/premake5.lua @@ -0,0 +1,310 @@ +gitVersioningCommand = "git describe --tags --dirty --always" +gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD" + +-- Quote the given string input as a C string +function cstrquote(value) + if value == nil then + return "\"\"" + end + result = value:gsub("\\", "\\\\") + result = result:gsub("\"", "\\\"") + result = result:gsub("\n", "\\n") + result = result:gsub("\t", "\\t") + result = result:gsub("\r", "\\r") + result = result:gsub("\a", "\\a") + result = result:gsub("\b", "\\b") + result = "\"" .. result .. "\"" + return result +end + +-- Converts tags in "vX.X.X" format and given revision number Y to an array of numbers {X,X,X,Y}. +-- In the case where the format does not work fall back to padding with zeroes and just ending with the revision number. +-- partscount can be either 3 or 4. +function vertonumarr(value, vernumber, partscount) + vernum = {} + for num in string.gmatch(value or "", "%d+") do + if #vernum < 3 then + table.insert(vernum, tonumber(num)) + end + end + while #vernum < 3 do + table.insert(vernum, 0) + end + if #vernum < partscount then + table.insert(vernum, tonumber(vernumber)) + end + return vernum +end + +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" +} + +newoption { + trigger = "dev-build", + description = "Enable development builds of the client." +} + +newaction { + trigger = "version", + description = "Returns the version string for the current commit of the source code.", + onWorkspace = function(wks) + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + local version = gitDescribeOutput + + proc = assert(io.popen(gitCurrentBranchCommand, "r")) + local gitCurrentBranchOutput = assert(proc:read('*a')):gsub("%s+", "") + local gitCurrentBranchSuccess = proc:close() + if gitCurrentBranchSuccess then + -- We got a branch name, check if it is a feature branch + if gitCurrentBranchOutput ~= "develop" and gitCurrentBranchOutput ~= "master" then + version = version .. "-" .. gitCurrentBranchOutput + end + end + + print(version) + os.exit(0) + end +} + +newaction { + trigger = "generate-buildinfo", + description = "Sets up build information file like version.h.", + onWorkspace = function(wks) + -- get old version number from version.hpp if any + local oldVersion = "(none)" + local oldVersionHeader = io.open(wks.location .. "/src/version.h", "r") + if oldVersionHeader ~= nil then + local oldVersionHeaderContent = assert(oldVersionHeader:read('*l')) + while oldVersionHeaderContent do + m = string.match(oldVersionHeaderContent, "#define GIT_DESCRIBE (.+)%s*$") + if m ~= nil then + oldVersion = m + end + + oldVersionHeaderContent = oldVersionHeader:read('*l') + end + end + + -- get current version via git + local proc = assert(io.popen(gitVersioningCommand, "r")) + local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + + -- generate version.hpp with a revision number if not equal + gitDescribeOutputQuoted = cstrquote(gitDescribeOutput) + if oldVersion ~= gitDescribeOutputQuoted then + -- get current git hash and write to version.txt (used by the preliminary updater) + -- TODO - remove once proper updater and release versioning exists + local proc = assert(io.popen("git rev-parse HEAD", "r")) + local gitCommitHash = assert(proc:read('*a')):gsub("%s+", "") + proc:close() + + -- get whether this is a clean revision (no uncommitted changes) + proc = assert(io.popen("git status --porcelain", "r")) + local revDirty = (assert(proc:read('*a')) ~= "") + if revDirty then revDirty = 1 else revDirty = 0 end + proc:close() + + -- get current tag name + proc = assert(io.popen("git describe --tags --abbrev=0")) + local tagName = proc:read('*l') + + -- get current branch name + proc = assert(io.popen("git branch --show-current")) + local branchName = proc:read('*l') + + -- branch for ci + if branchName == nil or branchName == '' then + proc = assert(io.popen("git show -s --pretty=%d HEAD")) + local branchInfo = proc:read('*l') + m = string.match(branchInfo, ".+,.+, ([^)]+)") + if m ~= nil then + branchName = m + end + end + + if branchName == nil then + branchName = "develop" + end + + print("Detected branch: " .. branchName) + + -- get revision number via git + local proc = assert(io.popen("git rev-list --count HEAD", "r")) + local revNumber = assert(proc:read('*a')):gsub("%s+", "") + + print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted) + + -- write to version.txt for preliminary updater + -- NOTE - remove this once we have a proper updater and proper release versioning + local versionFile = assert(io.open(wks.location .. "/version.txt", "w")) + versionFile:write(gitCommitHash) + versionFile:close() + + -- write version header + local versionHeader = assert(io.open(wks.location .. "/src/version.h", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch!\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n") + versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n") + versionHeader:write("#define GIT_HASH " .. cstrquote(gitCommitHash) .. "\n") + versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n") + versionHeader:write("#define GIT_BRANCH " .. cstrquote(branchName) .. "\n") + versionHeader:write("\n") + versionHeader:write("// Version transformed for RC files\n") + versionHeader:write("#define VERSION_PRODUCT_RC " .. table.concat(vertonumarr(tagName, revNumber, 3), ",") .. "\n") + versionHeader:write("#define VERSION_PRODUCT " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 3), ".")) .. "\n") + versionHeader:write("#define VERSION_FILE_RC " .. table.concat(vertonumarr(tagName, revNumber, 4), ",") .. "\n") + versionHeader:write("#define VERSION_FILE " .. cstrquote(table.concat(vertonumarr(tagName, revNumber, 4), ".")) .. "\n") + versionHeader:write("\n") + versionHeader:write("// Alias definitions\n") + versionHeader:write("#define VERSION GIT_DESCRIBE\n") + versionHeader:write("#define SHORTVERSION VERSION_PRODUCT\n") + versionHeader:close() + local versionHeader = assert(io.open(wks.location .. "/src/version.hpp", "w")) + versionHeader:write("/*\n") + versionHeader:write(" * Automatically generated by premake5.\n") + versionHeader:write(" * Do not touch!\n") + versionHeader:write(" *\n") + versionHeader:write(" * This file exists for reasons of complying with our coding standards.\n") + versionHeader:write(" *\n") + versionHeader:write(" * The Resource Compiler will ignore any content from C++ header files if they're not from STDInclude.hpp.\n") + versionHeader:write(" * That's the reason why we now place all version info in version.h instead.\n") + versionHeader:write(" */\n") + versionHeader:write("\n") + versionHeader:write("#include \".\\version.h\"\n") + versionHeader:close() + end + end +} + +dependencies.load() + +workspace "bo3" + startproject "client" + location "./build" + objdir "%{wks.location}/obj" + targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}" + + configurations {"Debug", "Release"} + + language "C++" + cppdialect "C++20" + + architecture "x86_64" + platforms "x64" + + systemversion "latest" + symbols "On" + staticruntime "On" + editandcontinue "Off" + warnings "Extra" + characterset "ASCII" + + if _OPTIONS["dev-build"] then + defines {"DEV_BUILD"} + end + + if os.getenv("CI") then + defines {"CI"} + end + + flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"} + + filter "platforms:x64" + defines {"_WINDOWS", "WIN32"} + filter {} + + filter "configurations:Release" + optimize "Size" + buildoptions {"/GL"} + linkoptions { "/IGNORE:4702", "/LTCG" } + defines {"NDEBUG"} + flags {"FatalCompileWarnings"} + filter {} + + filter "configurations:Debug" + optimize "Debug" + defines {"DEBUG", "_DEBUG"} + filter {} + +project "common" + kind "StaticLib" + language "C++" + + files {"./src/common/**.hpp", "./src/common/**.cpp"} + + includedirs {"./src/common", "%{prj.location}/src"} + + resincludedirs {"$(ProjectDir)src"} + + dependencies.imports() + +project "client" + kind "SharedLib" + language "C++" + + targetname "d3d11" + + pchheader "std_include.hpp" + pchsource "src/client/std_include.cpp" + + files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"} + + includedirs {"./src/client", "./src/common", "%{prj.location}/src"} + + resincludedirs {"$(ProjectDir)src"} + + dependson {"tlsdll", "runner"} + + links {"common"} + + prebuildcommands {"pushd %{_MAIN_SCRIPT_DIR}", "tools\\premake5 generate-buildinfo", "popd"} + + if _OPTIONS["copy-to"] then + postbuildcommands {"copy /y \"$(TargetPath)\" \"" .. _OPTIONS["copy-to"] .. "\""} + end + + dependencies.imports() + +group "Dependencies" + dependencies.projects() diff --git a/src/client/component/arxan.cpp b/src/client/component/arxan.cpp new file mode 100644 index 00000000..37228be4 --- /dev/null +++ b/src/client/component/arxan.cpp @@ -0,0 +1,122 @@ +#include +#include "loader/component_loader.hpp" +#include "scheduler.hpp" + +#include + +namespace arxan +{ + namespace + { + DWORD get_steam_pid() + { + static auto steam_pid = [] { + HKEY hRegKey; + DWORD pid{}; + + if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Valve\\Steam\\ActiveProcess", 0, KEY_QUERY_VALUE, + &hRegKey) != ERROR_SUCCESS) + return pid; + + DWORD dwLength = sizeof(pid); + RegQueryValueExA(hRegKey, "pid", nullptr, nullptr, reinterpret_cast(&pid), &dwLength); + RegCloseKey(hRegKey); + + return pid; + }(); + + return steam_pid; + } + + utils::hook::detour nt_close_hook; + utils::hook::detour nt_query_information_process_hook; + + NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class, + const PVOID info, + const ULONG info_length, const PULONG ret_length) + { + auto* orig = static_cast(nt_query_information_process_hook. + get_original()); + const auto status = orig(handle, info_class, info, info_length, ret_length); + + if (NT_SUCCESS(status)) + { + if (info_class == ProcessBasicInformation) + { + static DWORD explorer_pid = 0; + if (!explorer_pid) + { + auto* const shell_window = GetShellWindow(); + GetWindowThreadProcessId(shell_window, &explorer_pid); + } + + static_cast(info)->Reserved3 = PVOID(DWORD64(get_steam_pid())); + } + else if (info_class == 30) // ProcessDebugObjectHandle + { + *static_cast(info) = nullptr; + + return 0xC0000353; + } + else if (info_class == 7) // ProcessDebugPort + { + *static_cast(info) = nullptr; + } + else if (info_class == 31) + { + *static_cast(info) = 1; + } + } + + return status; + } + + NTSTATUS NTAPI nt_close_stub(const HANDLE handle) + { + char info[16]; + if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345) + { + auto* orig = static_cast(nt_close_hook.get_original()); + return orig(handle); + } + + return STATUS_INVALID_HANDLE; + } + + LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info) + { + if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE) + { + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; + } + + void hide_being_debugged() + { + auto* const peb = PPEB(__readgsqword(0x60)); + peb->BeingDebugged = false; + *reinterpret_cast(LPSTR(peb) + 0xBC) &= ~0x70; + } + } + + class component final : public component_interface + { + public: + void post_load() override + { + hide_being_debugged(); + scheduler::loop(hide_being_debugged, scheduler::pipeline::async); + + const utils::nt::library ntdll("ntdll.dll"); + nt_close_hook.create(ntdll.get_proc("NtClose"), nt_close_stub); + nt_query_information_process_hook.create(ntdll.get_proc("NtQueryInformationProcess"), + nt_query_information_process_stub); + + AddVectoredExceptionHandler(1, exception_filter); + } + }; +} + +REGISTER_COMPONENT(arxan::component) diff --git a/src/client/component/console.cpp b/src/client/component/console.cpp new file mode 100644 index 00000000..56015321 --- /dev/null +++ b/src/client/component/console.cpp @@ -0,0 +1,61 @@ +#include +#include "console.hpp" +#include "loader/component_loader.hpp" + +#include +#include + +namespace console +{ + namespace + { + void create_game_console() + { + reinterpret_cast(utils::nt::library{}.get_ptr() + 0x2333F80)(); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + this->terminate_runner_ = false; + + this->console_runner_ = utils::thread::create_named_thread("Console IO", [this] + { + create_game_console(); + + MSG msg{}; + while (!this->terminate_runner_) + { + if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + std::this_thread::sleep_for(1ms); + } + } + }); + } + + void pre_destroy() override + { + this->terminate_runner_ = true; + + if (this->console_runner_.joinable()) + { + this->console_runner_.join(); + } + } + + private: + std::atomic_bool terminate_runner_{false}; + std::thread console_runner_; + }; +} + +REGISTER_COMPONENT(console::component) diff --git a/src/client/component/console.hpp b/src/client/component/console.hpp new file mode 100644 index 00000000..28a19e30 --- /dev/null +++ b/src/client/component/console.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace console +{ + void print(int type, const char* fmt, ...); +} diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp new file mode 100644 index 00000000..e8875f8a --- /dev/null +++ b/src/client/component/scheduler.cpp @@ -0,0 +1,175 @@ +#include +#include "loader/component_loader.hpp" + +#include "scheduler.hpp" + +#include +#include +#include +#include + +namespace scheduler +{ + namespace + { + struct task + { + std::function handler{}; + std::chrono::milliseconds interval{}; + std::chrono::high_resolution_clock::time_point last_call{}; + }; + + using task_list = std::vector; + + class task_pipeline + { + public: + void add(task&& task) + { + new_callbacks_.access([&task](task_list& tasks) + { + tasks.emplace_back(std::move(task)); + }); + } + + void execute() + { + callbacks_.access([&](task_list& tasks) + { + this->merge_callbacks(); + + for (auto i = tasks.begin(); i != tasks.end();) + { + const auto now = std::chrono::high_resolution_clock::now(); + const auto diff = now - i->last_call; + + if (diff < i->interval) + { + ++i; + continue; + } + + i->last_call = now; + + const auto res = i->handler(); + if (res == cond_end) + { + i = tasks.erase(i); + } + else + { + ++i; + } + } + }); + } + + private: + utils::concurrency::container new_callbacks_; + utils::concurrency::container callbacks_; + + void merge_callbacks() + { + callbacks_.access([&](task_list& tasks) + { + new_callbacks_.access([&](task_list& new_tasks) + { + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_iterator(new_tasks.end())); + new_tasks = {}; + }); + }); + } + }; + + volatile bool kill = false; + std::thread thread; + task_pipeline pipelines[pipeline::count]; + utils::hook::detour r_end_frame_hook; + utils::hook::detour g_run_frame_hook; + utils::hook::detour main_frame_hook; + + void execute(const pipeline type) + { + assert(type >= 0 && type < pipeline::count); + pipelines[type].execute(); + } + + void r_end_frame_stub() + { + execute(pipeline::renderer); + r_end_frame_hook.invoke(); + } + + void server_frame_stub() + { + g_run_frame_hook.invoke(); + execute(pipeline::server); + } + + void main_frame_stub() + { + main_frame_hook.invoke(); + execute(pipeline::main); + } + } + + void schedule(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + assert(type >= 0 && type < pipeline::count); + + task task; + task.handler = callback; + task.interval = delay; + task.last_call = std::chrono::high_resolution_clock::now(); + + pipelines[type].add(std::move(task)); + } + + void loop(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_continue; + }, type, delay); + } + + void once(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) + { + schedule([callback]() + { + callback(); + return cond_end; + }, type, delay); + } + + class component final : public component_interface + { + public: + void post_load() override + { + thread = utils::thread::create_named_thread("Async Scheduler", []() + { + while (!kill) + { + execute(pipeline::async); + std::this_thread::sleep_for(10ms); + } + }); + } + + void pre_destroy() override + { + kill = true; + if (thread.joinable()) + { + thread.join(); + } + } + }; +} + +REGISTER_COMPONENT(scheduler::component) diff --git a/src/client/component/scheduler.hpp b/src/client/component/scheduler.hpp new file mode 100644 index 00000000..f26c60ce --- /dev/null +++ b/src/client/component/scheduler.hpp @@ -0,0 +1,33 @@ +#pragma once + +namespace scheduler +{ + enum pipeline + { + // Asynchronuous pipeline, disconnected from the game + async = 0, + + // The game's rendering pipeline + renderer, + + // The game's server thread + server, + + // The game's main thread + main, + + count, + }; + + static const bool cond_continue = false; + static const bool cond_end = true; + + void schedule(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void loop(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void once(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void on_game_initialized(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); +} \ No newline at end of file diff --git a/src/client/component/splash.cpp b/src/client/component/splash.cpp new file mode 100644 index 00000000..dec45419 --- /dev/null +++ b/src/client/component/splash.cpp @@ -0,0 +1,131 @@ +#include +#include "loader/component_loader.hpp" +#include "resource.hpp" + +#include + +namespace splash +{ +namespace +{ + HANDLE load_splash_image() + { + const auto self = utils::nt::library::get_by_address(load_splash_image); + return LoadImageA(self, MAKEINTRESOURCE(IMAGE_SPLASH), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + } +} + + class component final : public component_interface + { + public: + void post_load() override + { + image_ = load_splash_image(); + + this->show(); + } + + void pre_destroy() override + { + this->destroy(); + + MSG msg; + while (this->window_ && IsWindow(this->window_)) + { + if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + std::this_thread::sleep_for(1ms); + } + } + + this->window_ = nullptr; + } + + void post_unpack() override + { + this->destroy(); + } + + private: + HWND window_{}; + HANDLE image_{}; + + void destroy() const + { + if (this->window_ && IsWindow(this->window_)) + { + ShowWindow(this->window_, SW_HIDE); + DestroyWindow(this->window_); + UnregisterClassA("Black Ops III Splash Screen", utils::nt::library{}); + } + } + + void show() + { + WNDCLASSA wnd_class; + + const utils::nt::library host{}; + + wnd_class.style = CS_DROPSHADOW; + wnd_class.cbClsExtra = 0; + wnd_class.cbWndExtra = 0; + wnd_class.lpszMenuName = nullptr; + wnd_class.lpfnWndProc = DefWindowProcA; + wnd_class.hInstance = host; + wnd_class.hIcon = LoadIconA(host, reinterpret_cast(102)); + wnd_class.hCursor = LoadCursorA(nullptr, IDC_APPSTARTING); + wnd_class.hbrBackground = reinterpret_cast(6); + wnd_class.lpszClassName = "Black Ops III Splash Screen"; + + if (RegisterClassA(&wnd_class)) + { + const auto x_pixels = GetSystemMetrics(SM_CXFULLSCREEN); + const auto y_pixels = GetSystemMetrics(SM_CYFULLSCREEN); + + if (image_) + { + this->window_ = CreateWindowExA(WS_EX_APPWINDOW, "Black Ops III Splash Screen", "BOIII", + WS_POPUP | WS_SYSMENU, + (x_pixels - 320) / 2, (y_pixels - 100) / 2, 320, 100, nullptr, + nullptr, + host, nullptr); + + if (this->window_) + { + auto* const image_window = CreateWindowExA(0, "Static", nullptr, WS_CHILD | WS_VISIBLE | 0xEu, + 0, 0, + 320, 100, this->window_, nullptr, host, nullptr); + if (image_window) + { + RECT rect; + SendMessageA(image_window, 0x172u, 0, reinterpret_cast(image_)); + GetWindowRect(image_window, &rect); + + const int width = rect.right - rect.left; + rect.left = (x_pixels - width) / 2; + + const int height = rect.bottom - rect.top; + rect.top = (y_pixels - height) / 2; + + rect.right = rect.left + width; + rect.bottom = rect.top + height; + AdjustWindowRect(&rect, WS_CHILD | WS_VISIBLE | 0xEu, 0); + SetWindowPos(this->window_, nullptr, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOZORDER); + + ShowWindow(this->window_, SW_SHOW); + UpdateWindow(this->window_); + } + } + } + } + } + }; +} + +REGISTER_COMPONENT(splash::component) diff --git a/src/client/loader/component_interface.hpp b/src/client/loader/component_interface.hpp new file mode 100644 index 00000000..0478824e --- /dev/null +++ b/src/client/loader/component_interface.hpp @@ -0,0 +1,24 @@ +#pragma once + +class component_interface +{ +public: + virtual ~component_interface() = default; + + virtual void post_load() + { + } + + virtual void pre_destroy() + { + } + + virtual void post_unpack() + { + } + + virtual bool is_supported() + { + return true; + } +}; diff --git a/src/client/loader/component_loader.cpp b/src/client/loader/component_loader.cpp new file mode 100644 index 00000000..41abba14 --- /dev/null +++ b/src/client/loader/component_loader.cpp @@ -0,0 +1,90 @@ +#include +#include "component_loader.hpp" + +void component_loader::register_component(std::unique_ptr&& component_) +{ + get_components().push_back(std::move(component_)); +} + +bool component_loader::post_load() +{ + static auto handled = false; + if (handled) return true; + handled = true; + + clean(); + + try + { + for (const auto& component_ : get_components()) + { + component_->post_load(); + } + } + catch (premature_shutdown_trigger&) + { + return false; + } + + return true; +} + +void component_loader::post_unpack() +{ + static auto handled = false; + if (handled) return; + handled = true; + + for (const auto& component_ : get_components()) + { + component_->post_unpack(); + } +} + +void component_loader::pre_destroy() +{ + static auto handled = false; + if (handled) return; + handled = true; + + for (const auto& component_ : get_components()) + { + component_->pre_destroy(); + } +} + +void component_loader::clean() +{ + auto& components = get_components(); + for (auto i = components.begin(); i != components.end();) + { + if (!(*i)->is_supported()) + { + (*i)->pre_destroy(); + i = components.erase(i); + } + else + { + ++i; + } + } +} + +void component_loader::trigger_premature_shutdown() +{ + throw premature_shutdown_trigger(); +} + +std::vector>& component_loader::get_components() +{ + using component_vector = std::vector>; + using component_vector_container = std::unique_ptr>; + + static component_vector_container components(new component_vector, [](const component_vector* component_vector) + { + pre_destroy(); + delete component_vector; + }); + + return *components; +} diff --git a/src/client/loader/component_loader.hpp b/src/client/loader/component_loader.hpp new file mode 100644 index 00000000..2a21f724 --- /dev/null +++ b/src/client/loader/component_loader.hpp @@ -0,0 +1,58 @@ +#pragma once +#include "component_interface.hpp" + +class component_loader final +{ +public: + class premature_shutdown_trigger final : public std::exception + { + [[nodiscard]] const char* what() const noexcept override + { + return "Premature shutdown requested"; + } + }; + + template + class installer final + { + static_assert(std::is_base_of_v, "component has invalid base class"); + + public: + installer() + { + register_component(std::make_unique()); + } + }; + + template + static T* get() + { + for (const auto& component_ : get_components()) + { + if (typeid(*component_.get()) == typeid(T)) + { + return reinterpret_cast(component_.get()); + } + } + + return nullptr; + } + + static void register_component(std::unique_ptr&& component); + + static bool post_load(); + static void post_unpack(); + static void pre_destroy(); + static void clean(); + + static void trigger_premature_shutdown(); + +private: + static std::vector>& get_components(); +}; + +#define REGISTER_COMPONENT(name) \ +namespace \ +{ \ + static component_loader::installer __component; \ +} diff --git a/src/client/main.cpp b/src/client/main.cpp new file mode 100644 index 00000000..e63a77e3 --- /dev/null +++ b/src/client/main.cpp @@ -0,0 +1,131 @@ +#include + +#include "loader/component_loader.hpp" + +#include +#include +#include + +namespace +{ +DECLSPEC_NORETURN void WINAPI exit_hook(const int code) +{ + component_loader::pre_destroy(); + exit(code); +} + +VOID WINAPI initialize_critical_section(const LPCRITICAL_SECTION lpCriticalSection) +{ + component_loader::post_unpack(); + InitializeCriticalSection(lpCriticalSection); +} + +void patch_imports() +{ + const utils::nt::library game{}; + const auto self = utils::nt::library::get_by_address(patch_imports); + + auto patch_steam_import = [&](const std::string& func) { + const auto game_entry = game.get_iat_entry("steam_api64.dll", func); + if (!game_entry) { + throw std::runtime_error("Import '" + func + "' not found!"); + } + + const auto self_proc = self.get_proc(func); + if (!self_proc) { + throw std::runtime_error(func + " export not found"); + } + utils::hook::set(game_entry, self_proc); + }; + + patch_steam_import("SteamAPI_RegisterCallback"); + patch_steam_import("SteamAPI_RegisterCallResult"); + patch_steam_import("SteamGameServer_Shutdown"); + patch_steam_import("SteamGameServer_RunCallbacks"); + patch_steam_import("SteamGameServer_GetHSteamPipe"); + patch_steam_import("SteamGameServer_GetHSteamUser"); + patch_steam_import("SteamInternal_GameServer_Init"); + patch_steam_import("SteamAPI_UnregisterCallResult"); + patch_steam_import("SteamAPI_UnregisterCallback"); + patch_steam_import("SteamAPI_RunCallbacks"); + //patch_steam_import("SteamAPI_Shutdown"); + patch_steam_import("SteamInternal_CreateInterface"); + patch_steam_import("SteamAPI_GetHSteamUser"); + patch_steam_import("SteamAPI_GetHSteamPipe"); + patch_steam_import("SteamAPI_Init"); + patch_steam_import("SteamAPI_RestartAppIfNecessary"); + + utils::hook::set(game.get_iat_entry("kernel32.dll", "InitializeCriticalSection"), initialize_critical_section); + utils::hook::set(game.get_iat_entry("kernel32.dll", "ExitProcess"), exit_hook); +} + +bool run() +{ + srand(uint32_t(time(nullptr)) ^ (~GetTickCount())); + + { + auto premature_shutdown = true; + const auto _ = utils::finally([&premature_shutdown]() + { + if (premature_shutdown) + { + component_loader::pre_destroy(); + } + }); + + try + { + patch_imports(); + + if (!component_loader::post_load()) + { + return false; + } + + premature_shutdown = false; + } + catch (std::exception& e) + { + MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); + return false; + } + } + + return true; +} +} + +BOOL WINAPI DllMain(HINSTANCE, const DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) { + if(!run()) { + return FALSE; + } + } + return TRUE; +} + +extern "C" __declspec(dllexport) +HRESULT D3D11CreateDevice( + void* pAdapter, + uint64_t DriverType, + HMODULE Software, + UINT Flags, + const void* pFeatureLevels, + UINT FeatureLevels, + UINT SDKVersion, + void** ppDevice, + void* pFeatureLevel, + void** ppImmediateContext +) +{ + static auto func = [] { + char dir[MAX_PATH]{ 0 }; + GetSystemDirectoryA(dir, sizeof(dir)); + + const auto d3d11 = utils::nt::library::load(dir + "/d3d11.dll"s); + return d3d11.get_proc("D3D11CreateDevice"); + }(); + + return func(pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext); +} diff --git a/src/client/resource.hpp b/src/client/resource.hpp new file mode 100644 index 00000000..d546dabf --- /dev/null +++ b/src/client/resource.hpp @@ -0,0 +1,4 @@ +#pragma once + +#define ID_ICON 102 +#define IMAGE_SPLASH 300 diff --git a/src/client/resource.rc b/src/client/resource.rc new file mode 100644 index 00000000..68145b75 --- /dev/null +++ b/src/client/resource.rc @@ -0,0 +1,111 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +#include "version.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 VERSION_FILE_RC + PRODUCTVERSION VERSION_PRODUCT_RC + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else +#ifdef PRERELEASE + FILEFLAGS VS_FF_PRERELEASE +#else +#ifndef CI + FILEFLAGS VS_FF_PRIVATEBUILD +#else + FILEFLAGS 0x0L +#endif +#endif +#endif + FILEOS 0x40004L + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "" + VALUE "FileDescription", "bo3" + VALUE "FileVersion", VERSION_FILE + VALUE "InternalName", "something" + VALUE "LegalCopyright", "Copyright (C) 2022. All rights reserved." + VALUE "OriginalFilename", "d3d11.dll" + VALUE "ProductName", "bo3" + VALUE "ProductVersion", VERSION_PRODUCT + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Binary Data +// + +//ID_ICON ICON "resources/icon.ico" +IMAGE_SPLASH BITMAP "resources/splash.bmp" + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/client/resources/icon.ico b/src/client/resources/icon.ico new file mode 100644 index 00000000..f46ad35e Binary files /dev/null and b/src/client/resources/icon.ico differ diff --git a/src/client/resources/splash.bmp b/src/client/resources/splash.bmp new file mode 100644 index 00000000..f580ad70 Binary files /dev/null and b/src/client/resources/splash.bmp differ diff --git a/src/client/std_include.cpp b/src/client/std_include.cpp new file mode 100644 index 00000000..5622d708 --- /dev/null +++ b/src/client/std_include.cpp @@ -0,0 +1,50 @@ +#include + +#pragma comment(linker, "/stack:0x1000000") + +#ifdef INJECT_HOST_AS_LIB +//#pragma comment(linker, "/base:0x160000000") +#else +#pragma comment(linker, "/base:0x140000000") +#pragma comment(linker, "/merge:.data=.cld") +#pragma comment(linker, "/merge:.rdata=.clr") +#pragma comment(linker, "/merge:.cl=.main") +#pragma comment(linker, "/merge:.text=.main") +#endif + +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language=''\"") + +#ifndef INJECT_HOST_AS_LIB +#pragma bss_seg(".payload") +char payload_data[BINARY_PAYLOAD_SIZE]; +#endif + +extern "C" +{ + __declspec(dllexport) DWORD NvOptimusEnablement = 1; + __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; +}; + +extern "C" +{ + int s_read_arc4random(void*, size_t) + { + return -1; + } + + int s_read_getrandom(void*, size_t) + { + return -1; + } + + int s_read_urandom(void*, size_t) + { + return -1; + } + + int s_read_ltm_rng(void*, size_t) + { + return -1; + } +} + diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp new file mode 100644 index 00000000..78673d8d --- /dev/null +++ b/src/client/std_include.hpp @@ -0,0 +1,93 @@ +#pragma once + +#define BINARY_PAYLOAD_SIZE 0x14000000 + +// Decide whether to load the game as lib or to inject it +#define INJECT_HOST_AS_LIB + +#pragma warning(push) +#pragma warning(disable: 4100) +#pragma warning(disable: 4127) +#pragma warning(disable: 4244) +#pragma warning(disable: 4458) +#pragma warning(disable: 4702) +#pragma warning(disable: 4996) +#pragma warning(disable: 5054) +#pragma warning(disable: 5056) +#pragma warning(disable: 6011) +#pragma warning(disable: 6297) +#pragma warning(disable: 6385) +#pragma warning(disable: 6386) +#pragma warning(disable: 6387) +#pragma warning(disable: 26110) +#pragma warning(disable: 26451) +#pragma warning(disable: 26444) +#pragma warning(disable: 26451) +#pragma warning(disable: 26489) +#pragma warning(disable: 26495) +#pragma warning(disable: 26498) +#pragma warning(disable: 26812) +#pragma warning(disable: 28020) + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// min and max is required by gdi, therefore NOMINMAX won't work +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#pragma warning(pop) +#pragma warning(disable: 4100) + +#pragma comment(lib, "ntdll.lib") +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "urlmon.lib" ) +#pragma comment(lib, "iphlpapi.lib") +#pragma comment(lib, "Crypt32.lib") + +using namespace std::literals; diff --git a/src/client/steam/interfaces/apps.cpp b/src/client/steam/interfaces/apps.cpp new file mode 100644 index 00000000..f3de1074 --- /dev/null +++ b/src/client/steam/interfaces/apps.cpp @@ -0,0 +1,140 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool apps::BIsSubscribed() + { + return true; + } + + bool apps::BIsLowViolence() + { + return false; + } + + bool apps::BIsCybercafe() + { + return false; + } + + bool apps::BIsVACBanned() + { + return false; + } + + const char* apps::GetCurrentGameLanguage() + { + return "english"; + } + + const char* apps::GetAvailableGameLanguages() + { + return "english"; + } + + bool apps::BIsSubscribedApp(unsigned int appID) + { + return true; + } + + bool apps::BIsDlcInstalled(unsigned int appID) + { + return true; + } + + unsigned int apps::GetEarliestPurchaseUnixTime(unsigned int nAppID) + { + return 0; + } + + bool apps::BIsSubscribedFromFreeWeekend() + { + return false; + } + + int apps::GetDLCCount() + { + return 0; + } + + bool apps::BGetDLCDataByIndex(int iDLC, unsigned int* pAppID, bool* pbAvailable, char* pchName, + int cchNameBufferSize) + { + return false; + } + + void apps::InstallDLC(unsigned int nAppID) + { + } + + void apps::UninstallDLC(unsigned int nAppID) + { + } + + void apps::RequestAppProofOfPurchaseKey(unsigned int nAppID) + { + } + + bool apps::GetCurrentBetaName(char* pchName, int cchNameBufferSize) + { + strncpy_s(pchName, cchNameBufferSize, "public", cchNameBufferSize); + return true; + } + + bool apps::MarkContentCorrupt(bool bMissingFilesOnly) + { + return false; + } + + unsigned int apps::GetInstalledDepots(int* pvecDepots, unsigned int cMaxDepots) + { + return 0; + } + + unsigned int apps::GetAppInstallDir(unsigned int appID, char* pchFolder, unsigned int cchFolderBufferSize) + { + return 0; + } + + bool apps::BIsAppInstalled(unsigned int appID) + { + return false; + } + + steam_id apps::GetAppOwner() + { + return SteamUser()->GetSteamID(); + } + + const char* apps::GetLaunchQueryParam(const char* pchKey) + { + return ""; + } + bool apps::GetDlcDownloadProgress(uint32_t nAppID, uint64_t* punBytesDownloaded, uint64_t* punBytesTotal) + { + *punBytesTotal = 0; + *punBytesDownloaded = 0; + return false; + } + int apps::GetAppBuildId() + { + return 0; + } + void apps::RequestAllProofOfPurchaseKeys() + { + + } + uint64_t apps::GetFileDetails(const char* pszFileName) + { + return 0; + } + int apps::GetLaunchCommandLine(char* pszCommandLine, int cubCommandLine) + { + return 0; + } + bool apps::BIsSubscribedFromFamilySharing() + { + return false; + } +} diff --git a/src/client/steam/interfaces/apps.hpp b/src/client/steam/interfaces/apps.hpp new file mode 100644 index 00000000..a529ed68 --- /dev/null +++ b/src/client/steam/interfaces/apps.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace steam +{ + class apps + { + public: + ~apps() = default; + + virtual bool BIsSubscribed(); + virtual bool BIsLowViolence(); + virtual bool BIsCybercafe(); + virtual bool BIsVACBanned(); + virtual const char* GetCurrentGameLanguage(); + virtual const char* GetAvailableGameLanguages(); + virtual bool BIsSubscribedApp(unsigned int appID); + virtual bool BIsDlcInstalled(unsigned int appID); + virtual unsigned int GetEarliestPurchaseUnixTime(unsigned int nAppID); + virtual bool BIsSubscribedFromFreeWeekend(); + virtual int GetDLCCount(); + virtual bool BGetDLCDataByIndex(int iDLC, unsigned int* pAppID, bool* pbAvailable, char* pchName, + int cchNameBufferSize); + virtual void InstallDLC(unsigned int nAppID); + virtual void UninstallDLC(unsigned int nAppID); + virtual void RequestAppProofOfPurchaseKey(unsigned int nAppID); + virtual bool GetCurrentBetaName(char* pchName, int cchNameBufferSize); + virtual bool MarkContentCorrupt(bool bMissingFilesOnly); + virtual unsigned int GetInstalledDepots(int* pvecDepots, unsigned int cMaxDepots); + virtual unsigned int GetAppInstallDir(unsigned int appID, char* pchFolder, unsigned int cchFolderBufferSize); + virtual bool BIsAppInstalled(unsigned int appID); + + virtual steam_id GetAppOwner(); + virtual const char* GetLaunchQueryParam(const char* pchKey); + virtual bool GetDlcDownloadProgress(uint32_t nAppID, uint64_t* punBytesDownloaded, uint64_t* punBytesTotal); + virtual int GetAppBuildId(); + virtual void RequestAllProofOfPurchaseKeys(); + virtual uint64_t GetFileDetails(const char* pszFileName); + virtual int GetLaunchCommandLine(char* pszCommandLine, int cubCommandLine); + virtual bool BIsSubscribedFromFamilySharing(); + }; +} diff --git a/src/client/steam/interfaces/client.cpp b/src/client/steam/interfaces/client.cpp new file mode 100644 index 00000000..e0eda062 --- /dev/null +++ b/src/client/steam/interfaces/client.cpp @@ -0,0 +1,356 @@ +#include +#include "../steam.hpp" + +namespace steam { +namespace { + +void* get_dummy() +{ + static class blub { + public: + virtual uint64_t m0() { return 0; } + virtual uint64_t m1() { return 0; } + virtual uint64_t m2() { return 0; } + virtual uint64_t m3() { return 0; } + virtual uint64_t m4() { return 0; } + virtual uint64_t m5() { return 0; } + virtual uint64_t m6() { return 0; } + virtual uint64_t m7() { return 0; } + virtual uint64_t m8() { return 0; } + virtual uint64_t m9() { return 0; } + + virtual uint64_t m10() { return 0; } + virtual uint64_t m11() { return 0; } + virtual uint64_t m12() { return 0; } + virtual uint64_t m13() { return 0; } + virtual uint64_t m14() { return 0; } + virtual uint64_t m15() { return 0; } + virtual uint64_t m16() { return 0; } + virtual uint64_t m17() { return 0; } + virtual uint64_t m18() { return 0; } + virtual uint64_t m19() { return 0; } + + virtual uint64_t m20() { return 0; } + virtual uint64_t m21() { return 0; } + virtual uint64_t m22() { return 0; } + virtual uint64_t m23() { return 0; } + virtual uint64_t m24() { return 0; } + virtual uint64_t m25() { return 0; } + virtual uint64_t m26() { return 0; } + virtual uint64_t m27() { return 0; } + virtual uint64_t m28() { return 0; } + virtual uint64_t m29() { return 0; } + + virtual uint64_t m30() { return 0; } + virtual uint64_t m31() { return 0; } + virtual uint64_t m32() { return 0; } + virtual uint64_t m33() { return 0; } + virtual uint64_t m34() { return 0; } + virtual uint64_t m35() { return 0; } + virtual uint64_t m36() { return 0; } + virtual uint64_t m37() { return 0; } + virtual uint64_t m38() { return 0; } + virtual uint64_t m39() { return 0; } + + virtual uint64_t m40() { return 0; } + virtual uint64_t m41() { return 0; } + virtual uint64_t m42() { return 0; } + virtual uint64_t m43() { return 0; } + virtual uint64_t m44() { return 0; } + virtual uint64_t m45() { return 0; } + virtual uint64_t m46() { return 0; } + virtual uint64_t m47() { return 0; } + virtual uint64_t m48() { return 0; } + virtual uint64_t m49() { return 0; } + + virtual uint64_t m50() { return 0; } + virtual uint64_t m51() { return 0; } + virtual uint64_t m52() { return 0; } + virtual uint64_t m53() { return 0; } + virtual uint64_t m54() { return 0; } + virtual uint64_t m55() { return 0; } + virtual uint64_t m56() { return 0; } + virtual uint64_t m57() { return 0; } + virtual uint64_t m58() { return 0; } + virtual uint64_t m59() { return 0; } + + virtual uint64_t m60() { return 0; } + virtual uint64_t m61() { return 0; } + virtual uint64_t m62() { return 0; } + virtual uint64_t m63() { return 0; } + virtual uint64_t m64() { return 0; } + virtual uint64_t m65() { return 0; } + virtual uint64_t m66() { return 0; } + virtual uint64_t m67() { return 0; } + virtual uint64_t m68() { return 0; } + virtual uint64_t m69() { return 0; } + + } x; + return &x; +} + +} + +HSteamPipe client::CreateSteamPipe() +{ + OutputDebugStringA(__FUNCTION__); + return 1; +} + +bool client::BReleaseSteamPipe(HSteamPipe hSteamPipe) +{ + OutputDebugStringA(__FUNCTION__); + return true; +} + +HSteamUser client::ConnectToGlobalUser(HSteamPipe hSteamPipe) +{ + OutputDebugStringA(__FUNCTION__); + return 1; +} + +HSteamUser client::CreateLocalUser(HSteamPipe* phSteamPipe, uint32_t eAccountType) +{ + OutputDebugStringA(__FUNCTION__); + return 1; +} + +void client::ReleaseUser(HSteamPipe hSteamPipe, HSteamUser hUser) +{ + OutputDebugStringA(__FUNCTION__); +} + +void* client::GetISteamUser(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + return SteamUser(); +} + +void* client::GetISteamGameServer(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamGameServer(); +} + +void client::SetLocalIPBinding(uint32_t unIP, uint16_t usPort) +{ + OutputDebugStringA(__FUNCTION__); +} + +void* client::GetISteamFriends(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + return SteamFriends(); +} + +void* client::GetISteamUtils(HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamUtils(); +} + +void* client::GetISteamMatchmaking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamMatchmaking(); +} + +void* client::GetISteamMatchmakingServers(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + static matchmaking_servers u; + return &u; +} + +void* client::GetISteamGenericInterface(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return nullptr; +} + +void* client::GetISteamUserStats(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamUserStats(); +} + +void* client::GetISteamGameServerStats(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + static game_server_stats u; + return &u; +} + +void* client::GetISteamApps(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamApps(); +} + +void* client::GetISteamNetworking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamNetworking(); +} + +void* client::GetISteamRemoteStorage(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + return SteamRemoteStorage(); +} + +void* client::GetISteamScreenshots(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + static screenshots s; + return &s; +} + +void client::RunFrame() +{ + OutputDebugStringA(__FUNCTION__); +} + +uint32_t client::GetIPCCallCount() +{ + OutputDebugStringA(__FUNCTION__); + return 0; +} + +void client::SetWarningMessageHook(void* pFunction) +{ + OutputDebugStringA(__FUNCTION__); +} + +bool client::BShutdownIfAllPipesClosed() +{ + OutputDebugStringA(__FUNCTION__); + return true; +} + +void* client::GetISteamHTTP(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + + static http h; + return &h; +} + +void* client::GetISteamUnifiedMessages(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + static unified_messages u; + return &u; // +} + +void* client::GetISteamController(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + + static controller c; + return &c; // +} + +void* client::GetISteamUGC(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); // +} + +void* client::GetISteamAppList(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); // +} + +void* client::GetISteamMusic(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); // +} + +void* client::GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); // +} + +void* client::GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); // +} + +void client::Set_SteamAPI_CPostAPIResultInProcess(void* func) +{ + OutputDebugStringA(__FUNCTION__); +} + +void client::Remove_SteamAPI_CPostAPIResultInProcess(void* func) +{ + OutputDebugStringA(__FUNCTION__); +} + +void client::Set_SteamAPI_CCheckCallbackRegisteredInProcess(void* func) +{ + OutputDebugStringA(__FUNCTION__); +} + +void* client::GetISteamInventory(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); +} + +void* client::GetISteamVideo(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy();// +} + +void* client::GetISteamParentalSettings(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion) +{ + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(pchVersion); + //MessageBoxA(0, pchVersion, 0, 0); + return get_dummy(); +} +} diff --git a/src/client/steam/interfaces/client.hpp b/src/client/steam/interfaces/client.hpp new file mode 100644 index 00000000..85c0a6a9 --- /dev/null +++ b/src/client/steam/interfaces/client.hpp @@ -0,0 +1,52 @@ +#pragma once +#include + +namespace steam +{ + using HSteamPipe = uint64_t; +using HSteamUser = uint64_t; + + class client + { + public: + ~client() = default; + + virtual HSteamPipe CreateSteamPipe(); + virtual bool BReleaseSteamPipe(HSteamPipe hSteamPipe); + virtual HSteamUser ConnectToGlobalUser(HSteamPipe hSteamPipe); + virtual HSteamUser CreateLocalUser(HSteamPipe* phSteamPipe, uint32_t eAccountType); + virtual void ReleaseUser(HSteamPipe hSteamPipe, HSteamUser hUser); + virtual void* GetISteamUser(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamGameServer(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void SetLocalIPBinding(uint32_t unIP, uint16_t usPort); + virtual void* GetISteamFriends(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamUtils(HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamMatchmaking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamMatchmakingServers(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamGenericInterface(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamUserStats(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamGameServerStats(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamApps(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamNetworking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamRemoteStorage(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamScreenshots(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void RunFrame(); + virtual uint32_t GetIPCCallCount(); + virtual void SetWarningMessageHook(void* pFunction); + virtual bool BShutdownIfAllPipesClosed(); + virtual void* GetISteamHTTP(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamUnifiedMessages(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamController(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamUGC(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamAppList(HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamMusic(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void Set_SteamAPI_CPostAPIResultInProcess(void* func); + virtual void Remove_SteamAPI_CPostAPIResultInProcess(void* func); + virtual void Set_SteamAPI_CCheckCallbackRegisteredInProcess(void* func); + virtual void* GetISteamInventory(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamVideo(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + virtual void* GetISteamParentalSettings(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char* pchVersion); + }; +} diff --git a/src/client/steam/interfaces/controller.cpp b/src/client/steam/interfaces/controller.cpp new file mode 100644 index 00000000..6772babe --- /dev/null +++ b/src/client/steam/interfaces/controller.cpp @@ -0,0 +1,74 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool controller::Init() + { + return true; + } + bool controller::Shutdown() + { + return true; + } + + void controller::RunFrame() + { + + } + int controller::GetConnectedControllers(ControllerHandle_t* handlesOut) + { + return 0; + } + + bool controller::ShowBindingPanel(ControllerHandle_t controllerHandle) + { + return false; + } + uint64_t controller::GetActionSetHandle(const char* pszActionSetName) + { + return 0; + } + void controller::ActivateActionSet(ControllerHandle_t controllerHandle, uint64_t actionSetHandle) + { + + } + uint64_t controller::GetCurrentActionSet(ControllerHandle_t controllerHandle) + { + return 0; + } + uint64_t controller::GetDigitalActionHandle(const char* pszActionName) + { + return 0; + } + + uint64_t controller::GetDigitalActionData(ControllerHandle_t controllerHandle, uint64_t digitalActionHandle) + { + return 0; + } + int controller::GetDigitalActionOrigins(ControllerHandle_t controllerHandle, uint64_t actionSetHandle, uint64_t digitalActionHandle, uint64_t* originsOut) + { + return 0; + } + uint64_t controller::GetAnalogActionHandle(const char* pszActionName) + { + return 0; + } + uint64_t controller::GetAnalogActionData(ControllerHandle_t controllerHandle, uint64_t analogActionHandle) + { + return 0; + } + int controller::GetAnalogActionOrigins(ControllerHandle_t controllerHandle, uint64_t actionSetHandle, uint64_t analogActionHandle, uint64_t* originsOut) + { + return 0; + } + void controller::StopAnalogActionMomentum(ControllerHandle_t controllerHandle, uint64_t eAction) + { + + } + + void controller::TriggerHapticPulse(ControllerHandle_t controllerHandle, uint64_t eTargetPad, unsigned short usDurationMicroSec) + { + + } +} diff --git a/src/client/steam/interfaces/controller.hpp b/src/client/steam/interfaces/controller.hpp new file mode 100644 index 00000000..1d2d27f7 --- /dev/null +++ b/src/client/steam/interfaces/controller.hpp @@ -0,0 +1,28 @@ +#pragma once + +namespace steam +{ + using ControllerHandle_t = uint64_t; + class controller + { + public: + ~controller() = default; + + virtual bool Init(); + virtual bool Shutdown(); + virtual void RunFrame(); + virtual int GetConnectedControllers(ControllerHandle_t* handlesOut); + virtual bool ShowBindingPanel(ControllerHandle_t controllerHandle); + virtual uint64_t GetActionSetHandle(const char* pszActionSetName); + virtual void ActivateActionSet(ControllerHandle_t controllerHandle, uint64_t actionSetHandle); + virtual uint64_t GetCurrentActionSet(ControllerHandle_t controllerHandle); + virtual uint64_t GetDigitalActionHandle(const char* pszActionName); + virtual uint64_t GetDigitalActionData(ControllerHandle_t controllerHandle, uint64_t digitalActionHandle); + virtual int GetDigitalActionOrigins(ControllerHandle_t controllerHandle, uint64_t actionSetHandle, uint64_t digitalActionHandle, uint64_t* originsOut); + virtual uint64_t GetAnalogActionHandle(const char* pszActionName); + virtual uint64_t GetAnalogActionData(ControllerHandle_t controllerHandle, uint64_t analogActionHandle); + virtual int GetAnalogActionOrigins(ControllerHandle_t controllerHandle, uint64_t actionSetHandle, uint64_t analogActionHandle, uint64_t* originsOut); + virtual void StopAnalogActionMomentum(ControllerHandle_t controllerHandle, uint64_t eAction); + virtual void TriggerHapticPulse(ControllerHandle_t controllerHandle, uint64_t eTargetPad, unsigned short usDurationMicroSec); + }; +} diff --git a/src/client/steam/interfaces/friends.cpp b/src/client/steam/interfaces/friends.cpp new file mode 100644 index 00000000..328c7146 --- /dev/null +++ b/src/client/steam/interfaces/friends.cpp @@ -0,0 +1,315 @@ +#include +#include "../steam.hpp" + +//#include + +namespace steam +{ + const char* friends::GetPersonaName() + { + return "T7x User"; + } + + unsigned long long friends::SetPersonaName(const char* pchPersonaName) + { + return 0; + } + + int friends::GetPersonaState() + { + return 1; + } + + int friends::GetFriendCount(int eFriendFlags) + { + return 0; + } + + steam_id friends::GetFriendByIndex(int iFriend, int iFriendFlags) + { + return steam_id(); + } + + int friends::GetFriendRelationship(steam_id steamIDFriend) + { + return 0; + } + + int friends::GetFriendPersonaState(steam_id steamIDFriend) + { + return 0; + } + + const char* friends::GetFriendPersonaName(steam_id steamIDFriend) + { + return ""; + } + + bool friends::GetFriendGamePlayed(steam_id steamIDFriend, void* pFriendGameInfo) + { + return false; + } + + const char* friends::GetFriendPersonaNameHistory(steam_id steamIDFriend, int iPersonaName) + { + return ""; + } + + bool friends::HasFriend(steam_id steamIDFriend, int eFriendFlags) + { + return false; + } + + int friends::GetClanCount() + { + return 0; + } + + steam_id friends::GetClanByIndex(int iClan) + { + return steam_id(); + } + + const char* friends::GetClanName(steam_id steamIDClan) + { + return "3arc"; + } + + const char* friends::GetClanTag(steam_id steamIDClan) + { + return this->GetClanName(steamIDClan); + } + + bool friends::GetClanActivityCounts(steam_id steamID, int* pnOnline, int* pnInGame, int* pnChatting) + { + return false; + } + + unsigned long long friends::DownloadClanActivityCounts(steam_id groupIDs[], int nIds) + { + return 0; + } + + int friends::GetFriendCountFromSource(steam_id steamIDSource) + { + return 0; + } + + steam_id friends::GetFriendFromSourceByIndex(steam_id steamIDSource, int iFriend) + { + return steam_id(); + } + + bool friends::IsUserInSource(steam_id steamIDUser, steam_id steamIDSource) + { + return false; + } + + void friends::SetInGameVoiceSpeaking(steam_id steamIDUser, bool bSpeaking) + { + } + + void friends::ActivateGameOverlay(const char* pchDialog) + { + } + + void friends::ActivateGameOverlayToUser(const char* pchDialog, steam_id steamID) + { + } + + void friends::ActivateGameOverlayToWebPage(const char* pchURL) + { + } + + void friends::ActivateGameOverlayToStore(unsigned int nAppID, unsigned int eFlag) + { + } + + void friends::SetPlayedWith(steam_id steamIDUserPlayedWith) + { + } + + void friends::ActivateGameOverlayInviteDialog(steam_id steamIDLobby) + { + } + + int friends::GetSmallFriendAvatar(steam_id steamIDFriend) + { + return 0; + } + + int friends::GetMediumFriendAvatar(steam_id steamIDFriend) + { + return 0; + } + + int friends::GetLargeFriendAvatar(steam_id steamIDFriend) + { + return 0; + } + + bool friends::RequestUserInformation(steam_id steamIDUser, bool bRequireNameOnly) + { + return false; + } + + unsigned long long friends::RequestClanOfficerList(steam_id steamIDClan) + { + return 0; + } + + steam_id friends::GetClanOwner(steam_id steamIDClan) + { + return steam_id(); + } + + int friends::GetClanOfficerCount(steam_id steamIDClan) + { + return 0; + } + + steam_id friends::GetClanOfficerByIndex(steam_id steamIDClan, int iOfficer) + { + return steam_id(); + } + + int friends::GetUserRestrictions() + { + return 0; + } + + bool friends::SetRichPresence(const char* pchKey, const char* pchValue) + { + return true; + } + + void friends::ClearRichPresence() + { + } + + const char* friends::GetFriendRichPresence(steam_id steamIDFriend, const char* pchKey) + { + return ""; + } + + int friends::GetFriendRichPresenceKeyCount(steam_id steamIDFriend) + { + return 0; + } + + const char* friends::GetFriendRichPresenceKeyByIndex(steam_id steamIDFriend, int iKey) + { + return "a"; + } + + void friends::RequestFriendRichPresence(steam_id steamIDFriend) + { + } + + bool friends::InviteUserToGame(steam_id steamIDFriend, const char* pchConnectString) + { + return false; + } + + int friends::GetCoplayFriendCount() + { + return 0; + } + + steam_id friends::GetCoplayFriend(int iCoplayFriend) + { + return steam_id(); + } + + int friends::GetFriendCoplayTime(steam_id steamIDFriend) + { + return 0; + } + + unsigned int friends::GetFriendCoplayGame(steam_id steamIDFriend) + { + return 0; + } + + unsigned long long friends::JoinClanChatRoom(steam_id steamIDClan) + { + return 0; + } + + bool friends::LeaveClanChatRoom(steam_id steamIDClan) + { + return false; + } + + int friends::GetClanChatMemberCount(steam_id steamIDClan) + { + return 0; + } + + steam_id friends::GetChatMemberByIndex(steam_id steamIDClan, int iUser) + { + return steam_id(); + } + + bool friends::SendClanChatMessage(steam_id steamIDClanChat, const char* pchText) + { + return false; + } + + int friends::GetClanChatMessage(steam_id steamIDClanChat, int iMessage, void* prgchText, int cchTextMax, + unsigned int* peChatEntryType, steam_id* pSteamIDChatter) + { + return 0; + } + + bool friends::IsClanChatAdmin(steam_id steamIDClanChat, steam_id steamIDUser) + { + return false; + } + + bool friends::IsClanChatWindowOpenInSteam(steam_id steamIDClanChat) + { + return false; + } + + bool friends::OpenClanChatWindowInSteam(steam_id steamIDClanChat) + { + return false; + } + + bool friends::CloseClanChatWindowInSteam(steam_id steamIDClanChat) + { + return false; + } + + bool friends::SetListenForFriendsMessages(bool bInterceptEnabled) + { + return false; + } + + bool friends::ReplyToFriendMessage(steam_id steamIDFriend, const char* pchMsgToSend) + { + return false; + } + + int friends::GetFriendMessage(steam_id steamIDFriend, int iMessageID, void* pvData, int cubData, + unsigned int* peChatEntryType) + { + return 0; + } + + unsigned long long friends::GetFollowerCount(steam_id steamID) + { + return 0; + } + + unsigned long long friends::IsFollowing(steam_id steamID) + { + return 0; + } + + unsigned long long friends::EnumerateFollowingList(unsigned int unStartIndex) + { + return 0; + } +} diff --git a/src/client/steam/interfaces/friends.hpp b/src/client/steam/interfaces/friends.hpp new file mode 100644 index 00000000..f7cb2a2e --- /dev/null +++ b/src/client/steam/interfaces/friends.hpp @@ -0,0 +1,76 @@ +#pragma once + +namespace steam +{ + class friends + { + public: + ~friends() = default; + + virtual const char* GetPersonaName(); + virtual unsigned long long SetPersonaName(const char* pchPersonaName); + virtual int GetPersonaState(); + virtual int GetFriendCount(int eFriendFlags); + virtual steam_id GetFriendByIndex(int iFriend, int iFriendFlags); + virtual int GetFriendRelationship(steam_id steamIDFriend); + virtual int GetFriendPersonaState(steam_id steamIDFriend); + virtual const char* GetFriendPersonaName(steam_id steamIDFriend); + virtual bool GetFriendGamePlayed(steam_id steamIDFriend, void* pFriendGameInfo); + virtual const char* GetFriendPersonaNameHistory(steam_id steamIDFriend, int iPersonaName); + virtual bool HasFriend(steam_id steamIDFriend, int eFriendFlags); + virtual int GetClanCount(); + virtual steam_id GetClanByIndex(int iClan); + virtual const char* GetClanName(steam_id steamIDClan); + virtual const char* GetClanTag(steam_id steamIDClan); + virtual bool GetClanActivityCounts(steam_id steamID, int* pnOnline, int* pnInGame, int* pnChatting); + virtual unsigned long long DownloadClanActivityCounts(steam_id groupIDs[], int nIds); + virtual int GetFriendCountFromSource(steam_id steamIDSource); + virtual steam_id GetFriendFromSourceByIndex(steam_id steamIDSource, int iFriend); + virtual bool IsUserInSource(steam_id steamIDUser, steam_id steamIDSource); + virtual void SetInGameVoiceSpeaking(steam_id steamIDUser, bool bSpeaking); + virtual void ActivateGameOverlay(const char* pchDialog); + virtual void ActivateGameOverlayToUser(const char* pchDialog, steam_id steamID); + virtual void ActivateGameOverlayToWebPage(const char* pchURL); + virtual void ActivateGameOverlayToStore(unsigned int nAppID, unsigned int eFlag); + virtual void SetPlayedWith(steam_id steamIDUserPlayedWith); + virtual void ActivateGameOverlayInviteDialog(steam_id steamIDLobby); + virtual int GetSmallFriendAvatar(steam_id steamIDFriend); + virtual int GetMediumFriendAvatar(steam_id steamIDFriend); + virtual int GetLargeFriendAvatar(steam_id steamIDFriend); + virtual bool RequestUserInformation(steam_id steamIDUser, bool bRequireNameOnly); + virtual unsigned long long RequestClanOfficerList(steam_id steamIDClan); + virtual steam_id GetClanOwner(steam_id steamIDClan); + virtual int GetClanOfficerCount(steam_id steamIDClan); + virtual steam_id GetClanOfficerByIndex(steam_id steamIDClan, int iOfficer); + virtual int GetUserRestrictions(); + virtual bool SetRichPresence(const char* pchKey, const char* pchValue); + virtual void ClearRichPresence(); + virtual const char* GetFriendRichPresence(steam_id steamIDFriend, const char* pchKey); + virtual int GetFriendRichPresenceKeyCount(steam_id steamIDFriend); + virtual const char* GetFriendRichPresenceKeyByIndex(steam_id steamIDFriend, int iKey); + virtual void RequestFriendRichPresence(steam_id steamIDFriend); + virtual bool InviteUserToGame(steam_id steamIDFriend, const char* pchConnectString); + virtual int GetCoplayFriendCount(); + virtual steam_id GetCoplayFriend(int iCoplayFriend); + virtual int GetFriendCoplayTime(steam_id steamIDFriend); + virtual unsigned int GetFriendCoplayGame(steam_id steamIDFriend); + virtual unsigned long long JoinClanChatRoom(steam_id steamIDClan); + virtual bool LeaveClanChatRoom(steam_id steamIDClan); + virtual int GetClanChatMemberCount(steam_id steamIDClan); + virtual steam_id GetChatMemberByIndex(steam_id steamIDClan, int iUser); + virtual bool SendClanChatMessage(steam_id steamIDClanChat, const char* pchText); + virtual int GetClanChatMessage(steam_id steamIDClanChat, int iMessage, void* prgchText, int cchTextMax, + unsigned int* peChatEntryType, steam_id* pSteamIDChatter); + virtual bool IsClanChatAdmin(steam_id steamIDClanChat, steam_id steamIDUser); + virtual bool IsClanChatWindowOpenInSteam(steam_id steamIDClanChat); + virtual bool OpenClanChatWindowInSteam(steam_id steamIDClanChat); + virtual bool CloseClanChatWindowInSteam(steam_id steamIDClanChat); + virtual bool SetListenForFriendsMessages(bool bInterceptEnabled); + virtual bool ReplyToFriendMessage(steam_id steamIDFriend, const char* pchMsgToSend); + virtual int GetFriendMessage(steam_id steamIDFriend, int iMessageID, void* pvData, int cubData, + unsigned int* peChatEntryType); + virtual unsigned long long GetFollowerCount(steam_id steamID); + virtual unsigned long long IsFollowing(steam_id steamID); + virtual unsigned long long EnumerateFollowingList(unsigned int unStartIndex); + }; +} diff --git a/src/client/steam/interfaces/game_server.cpp b/src/client/steam/interfaces/game_server.cpp new file mode 100644 index 00000000..11e7a79d --- /dev/null +++ b/src/client/steam/interfaces/game_server.cpp @@ -0,0 +1,204 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool game_server::InitGameServer(unsigned int unGameIP, unsigned short unGamePort, unsigned short usQueryPort, + unsigned int unServerFlags, unsigned int nAppID, const char* pchVersion) + { + return true; + } + + void game_server::SetProduct(const char* pchProductName) + { + } + + void game_server::SetGameDescription(const char* pchGameDescription) + { + } + + void game_server::SetModDir(const char* pchModDir) + { + } + + void game_server::SetDedicatedServer(bool bDedicatedServer) + { + } + + void game_server::LogOn(const char* pszAccountName, const char* pszPassword) + { + } + + void game_server::LogOnAnonymous() + { + auto* const retvals = calloc(1, 1); + const auto result = callbacks::register_call(); + callbacks::return_call(retvals, 0, 101, result); + } + + void game_server::LogOff() + { + } + + bool game_server::BLoggedOn() + { + return true; + } + + bool game_server::BSecure() + { + return false; + } + + steam_id game_server::GetSteamID() + { + return SteamUser()->GetSteamID(); + } + + bool game_server::WasRestartRequested() + { + return false; + } + + void game_server::SetMaxPlayerCount(int cPlayersMax) + { + } + + void game_server::SetBotPlayerCount(int cBotPlayers) + { + } + + void game_server::SetServerName(const char* pszServerName) + { + } + + void game_server::SetMapName(const char* pszMapName) + { + } + + void game_server::SetPasswordProtected(bool bPasswordProtected) + { + } + + void game_server::SetSpectatorPort(unsigned short unSpectatorPort) + { + } + + void game_server::SetSpectatorServerName(const char* pszSpectatorServerName) + { + } + + void game_server::ClearAllKeyValues() + { + } + + void game_server::SetKeyValue(const char* pKey, const char* pValue) + { + } + + void game_server::SetGameTags(const char* pchGameTags) + { + } + + void game_server::SetGameData(const char* pchGameData) + { + } + + void game_server::SetRegion(const char* pchRegionName) + { + } + + int game_server::SendUserConnectAndAuthenticate(unsigned int unIPClient, const void* pvAuthBlob, + unsigned int cubAuthBlobSize, steam_id* pSteamIDUser) + { + return 0; + } + + steam_id game_server::CreateUnauthenticatedUserConnection() + { + return SteamUser()->GetSteamID(); + } + + void game_server::SendUserDisconnect(steam_id steamIDUser) + { + } + + bool game_server::BUpdateUserData(steam_id steamIDUser, const char* pchPlayerName, unsigned int uScore) + { + return false; + } + + int game_server::GetAuthSessionTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket) + { + return 0; + } + + int game_server::BeginAuthSession(const void* pAuthTicket, int cbAuthTicket, steam_id steamID) + { + return 0; + } + + void game_server::EndAuthSession(steam_id steamID) + { + } + + void game_server::CancelAuthTicket(int hAuthTicket) + { + } + + int game_server::UserHasLicenseForApp(steam_id steamID, unsigned int appID) + { + return 0; + } + + bool game_server::RequestUserGroupStatus(steam_id steamIDUser, steam_id steamIDGroup) + { + return false; + } + + void game_server::GetGameplayStats() + { + } + + unsigned long long game_server::GetServerReputation() + { + return 0; + } + + unsigned int game_server::GetPublicIP() + { + return 0; + } + + bool game_server::HandleIncomingPacket(const void* pData, int cbData, unsigned int srcIP, unsigned short srcPort) + { + return false; + } + + int game_server::GetNextOutgoingPacket(void* pOut, int cbMaxOut, unsigned int* pNetAdr, unsigned short* pPort) + { + return 0; + } + + void game_server::EnableHeartbeats(bool bActive) + { + } + + void game_server::SetHeartbeatInterval(int iHeartbeatInterval) + { + } + + void game_server::ForceHeartbeat() + { + } + + unsigned long long game_server::AssociateWithClan(steam_id clanID) + { + return 0; + } + + unsigned long long game_server::ComputeNewPlayerCompatibility(steam_id steamID) + { + return 0; + } +} diff --git a/src/client/steam/interfaces/game_server.hpp b/src/client/steam/interfaces/game_server.hpp new file mode 100644 index 00000000..3f7f7c71 --- /dev/null +++ b/src/client/steam/interfaces/game_server.hpp @@ -0,0 +1,57 @@ +#pragma once + +namespace steam +{ + class game_server + { + public: + ~game_server() = default; + + virtual bool InitGameServer(unsigned int unGameIP, unsigned short unGamePort, unsigned short usQueryPort, + unsigned int unServerFlags, unsigned int nAppID, const char* pchVersion); + virtual void SetProduct(const char* pchProductName); + virtual void SetGameDescription(const char* pchGameDescription); + virtual void SetModDir(const char* pchModDir); + virtual void SetDedicatedServer(bool bDedicatedServer); + virtual void LogOn(const char* pszAccountName, const char* pszPassword); + virtual void LogOnAnonymous(); + virtual void LogOff(); + virtual bool BLoggedOn(); + virtual bool BSecure(); + virtual steam_id GetSteamID(); + virtual bool WasRestartRequested(); + virtual void SetMaxPlayerCount(int cPlayersMax); + virtual void SetBotPlayerCount(int cBotPlayers); + virtual void SetServerName(const char* pszServerName); + virtual void SetMapName(const char* pszMapName); + virtual void SetPasswordProtected(bool bPasswordProtected); + virtual void SetSpectatorPort(unsigned short unSpectatorPort); + virtual void SetSpectatorServerName(const char* pszSpectatorServerName); + virtual void ClearAllKeyValues(); + virtual void SetKeyValue(const char* pKey, const char* pValue); + virtual void SetGameTags(const char* pchGameTags); + virtual void SetGameData(const char* pchGameData); + virtual void SetRegion(const char* pchRegionName); + virtual int SendUserConnectAndAuthenticate(unsigned int unIPClient, const void* pvAuthBlob, + unsigned int cubAuthBlobSize, steam_id* pSteamIDUser); + virtual steam_id CreateUnauthenticatedUserConnection(); + virtual void SendUserDisconnect(steam_id steamIDUser); + virtual bool BUpdateUserData(steam_id steamIDUser, const char* pchPlayerName, unsigned int uScore); + virtual int GetAuthSessionTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket); + virtual int BeginAuthSession(const void* pAuthTicket, int cbAuthTicket, steam_id steamID); + virtual void EndAuthSession(steam_id steamID); + virtual void CancelAuthTicket(int hAuthTicket); + virtual int UserHasLicenseForApp(steam_id steamID, unsigned int appID); + virtual bool RequestUserGroupStatus(steam_id steamIDUser, steam_id steamIDGroup); + virtual void GetGameplayStats(); + virtual unsigned long long GetServerReputation(); + virtual unsigned int GetPublicIP(); + virtual bool HandleIncomingPacket(const void* pData, int cbData, unsigned int srcIP, unsigned short srcPort); + virtual int GetNextOutgoingPacket(void* pOut, int cbMaxOut, unsigned int* pNetAdr, unsigned short* pPort); + virtual void EnableHeartbeats(bool bActive); + virtual void SetHeartbeatInterval(int iHeartbeatInterval); + virtual void ForceHeartbeat(); + virtual unsigned long long AssociateWithClan(steam_id clanID); + virtual unsigned long long ComputeNewPlayerCompatibility(steam_id steamID); + }; +} diff --git a/src/client/steam/interfaces/game_server_stats.cpp b/src/client/steam/interfaces/game_server_stats.cpp new file mode 100644 index 00000000..96198098 --- /dev/null +++ b/src/client/steam/interfaces/game_server_stats.cpp @@ -0,0 +1,56 @@ +#include +#include "../steam.hpp" + +namespace steam { +uint64_t game_server_stats::RequestUserStats(steam_id steamIDUser) +{ + return 0; +} + +bool game_server_stats::GetUserStat(steam_id steamIDUser, const char* pchName, int32_t* pData) +{ + return false; +} + +bool game_server_stats::GetUserStat(steam_id steamIDUser, const char* pchName, float* pData) +{ + return false; +} + +bool game_server_stats::GetUserAchievement(steam_id steamIDUser, const char* pchName, bool* pbAchieved) +{ + return false; +} + +bool game_server_stats::SetUserStat(steam_id steamIDUser, const char* pchName, int32_t nData) +{ + return false; +} + +bool game_server_stats::SetUserStat(steam_id steamIDUser, const char* pchName, float fData) +{ + return false; +} + +bool game_server_stats::UpdateUserAvgRateStat(steam_id steamIDUser, const char* pchName, float flCountThisSession, + double dSessionLength) +{ + return false; +} + +bool game_server_stats::SetUserAchievement(steam_id steamIDUser, const char* pchName) +{ + return false; +} + +bool game_server_stats::ClearUserAchievement(steam_id steamIDUser, const char* pchName) +{ + return false; +} + +uint64_t game_server_stats::StoreUserStats(steam_id steamIDUser) +{ + return 0; +} + +} diff --git a/src/client/steam/interfaces/game_server_stats.hpp b/src/client/steam/interfaces/game_server_stats.hpp new file mode 100644 index 00000000..07151ed9 --- /dev/null +++ b/src/client/steam/interfaces/game_server_stats.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace steam +{ + class game_server_stats + { + public: + ~game_server_stats() = default; + + virtual uint64_t RequestUserStats(steam_id steamIDUser); + virtual bool GetUserStat(steam_id steamIDUser, const char* pchName, int32_t* pData); + virtual bool GetUserStat(steam_id steamIDUser, const char* pchName, float* pData); + virtual bool GetUserAchievement(steam_id steamIDUser, const char* pchName, bool* pbAchieved); + virtual bool SetUserStat(steam_id steamIDUser, const char* pchName, int32_t nData); + virtual bool SetUserStat(steam_id steamIDUser, const char* pchName, float fData); + virtual bool UpdateUserAvgRateStat(steam_id steamIDUser, const char* pchName, float flCountThisSession, double dSessionLength); + virtual bool SetUserAchievement(steam_id steamIDUser, const char* pchName); + virtual bool ClearUserAchievement(steam_id steamIDUser, const char* pchName); + virtual uint64_t StoreUserStats(steam_id steamIDUser); + }; +} diff --git a/src/client/steam/interfaces/http.cpp b/src/client/steam/interfaces/http.cpp new file mode 100644 index 00000000..c9f701de --- /dev/null +++ b/src/client/steam/interfaces/http.cpp @@ -0,0 +1,131 @@ +#include +#include "../steam.hpp" + + +namespace steam +{ + HTTPRequestHandle http::http::CreateHTTPRequest(uint32_t eHTTPRequestMethod, const char* pchAbsoluteURL) + { + return 0; + } + + bool http::SetHTTPRequestContextValue(HTTPRequestHandle hRequest, uint64_t ulContextValue) + { + return false; + } + + bool http::SetHTTPRequestNetworkActivityTimeout(HTTPRequestHandle hRequest, uint32_t unTimeoutSeconds) + { + return false; + } + + bool http::SetHTTPRequestHeaderValue(HTTPRequestHandle hRequest, const char* pchHeaderName, const char* pchHeaderValue) + { + return false; + } + + bool http::SetHTTPRequestGetOrPostParameter(HTTPRequestHandle hRequest, const char* pchParamName, const char* pchParamValue) + { + return false; + } + + bool http::SendHTTPRequest(HTTPRequestHandle hRequest, uint64_t* pCallHandle) + { + return false; + } + + bool http::SendHTTPRequestAndStreamResponse(HTTPRequestHandle hRequest, uint64_t* pCallHandle) + { + return false; + } + + bool http::DeferHTTPRequest(HTTPRequestHandle hRequest) + { + return false; + } + + bool http::PrioritizeHTTPRequest(HTTPRequestHandle hRequest) + { + return false; + } + + bool http::GetHTTPResponseHeaderSize(HTTPRequestHandle hRequest, const char* pchHeaderName, uint32_t* unResponseHeaderSize) + { + return false; + } + + bool http::GetHTTPResponseHeaderValue(HTTPRequestHandle hRequest, const char* pchHeaderName, uint8_t* pHeaderValueBuffer, uint32_t unBufferSize) + { + return false; + } + + bool http::GetHTTPResponseBodySize(HTTPRequestHandle hRequest, uint32_t* unBodySize) + { + return false; + } + + bool http::GetHTTPResponseBodyData(HTTPRequestHandle hRequest, uint8_t* pBodyDataBuffer, uint32_t unBufferSize) + { + return false; + } + + bool http::GetHTTPStreamingResponseBodyData(HTTPRequestHandle hRequest, uint32_t cOffset, uint8_t* pBodyDataBuffer, uint32_t unBufferSize) + { + return false; + } + + bool http::ReleaseHTTPRequest(HTTPRequestHandle hRequest) + { + return false; + } + + bool http::GetHTTPDownloadProgressPct(HTTPRequestHandle hRequest, float* pflPercentOut) + { + return false; + } + + bool http::SetHTTPRequestRawPostBody(HTTPRequestHandle hRequest, const char* pchContentType, uint8_t* pubBody, uint32_t unBodyLen) + { + return false; + } + + HTTPCookieContainerHandle http::CreateCookieContainer(bool bAllowResponsesToModify) + { + return 0; + } + + bool http::ReleaseCookieContainer(HTTPCookieContainerHandle hCookieContainer) + { + return false; + } + + bool http::SetCookie(HTTPCookieContainerHandle hCookieContainer, const char* pchHost, const char* pchUrl, const char* pchCookie) + { + return false; + } + + bool http::SetHTTPRequestCookieContainer(HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer) + { + return false; + } + + bool http::SetHTTPRequestUserAgentInfo(HTTPRequestHandle hRequest, const char* pchUserAgentInfo) + { + return false; + } + + bool http::SetHTTPRequestRequiresVerifiedCertificate(HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate) + { + return false; + } + + bool http::SetHTTPRequestAbsoluteTimeoutMS(HTTPRequestHandle hRequest, uint32_t unMilliseconds) + { + return false; + } + + bool http::GetHTTPRequestWasTimedOut(HTTPRequestHandle hRequest, bool* pbWasTimedOut) + { + return false; + } +} diff --git a/src/client/steam/interfaces/http.hpp b/src/client/steam/interfaces/http.hpp new file mode 100644 index 00000000..8f2eb0fe --- /dev/null +++ b/src/client/steam/interfaces/http.hpp @@ -0,0 +1,39 @@ +#pragma once + +namespace steam +{ + using HTTPRequestHandle = uint64_t; + using HTTPCookieContainerHandle = uint64_t; + + class http + { + public: + ~http() = default; + + virtual HTTPRequestHandle CreateHTTPRequest(uint32_t eHTTPRequestMethod, const char* pchAbsoluteURL); + virtual bool SetHTTPRequestContextValue(HTTPRequestHandle hRequest, uint64_t ulContextValue); + virtual bool SetHTTPRequestNetworkActivityTimeout(HTTPRequestHandle hRequest, uint32_t unTimeoutSeconds); + virtual bool SetHTTPRequestHeaderValue(HTTPRequestHandle hRequest, const char* pchHeaderName, const char* pchHeaderValue); + virtual bool SetHTTPRequestGetOrPostParameter(HTTPRequestHandle hRequest, const char* pchParamName, const char* pchParamValue); + virtual bool SendHTTPRequest(HTTPRequestHandle hRequest, uint64_t* pCallHandle); + virtual bool SendHTTPRequestAndStreamResponse(HTTPRequestHandle hRequest, uint64_t* pCallHandle); + virtual bool DeferHTTPRequest(HTTPRequestHandle hRequest); + virtual bool PrioritizeHTTPRequest(HTTPRequestHandle hRequest); + virtual bool GetHTTPResponseHeaderSize(HTTPRequestHandle hRequest, const char* pchHeaderName, uint32_t* unResponseHeaderSize); + virtual bool GetHTTPResponseHeaderValue(HTTPRequestHandle hRequest, const char* pchHeaderName, uint8_t* pHeaderValueBuffer, uint32_t unBufferSize); + virtual bool GetHTTPResponseBodySize(HTTPRequestHandle hRequest, uint32_t* unBodySize); + virtual bool GetHTTPResponseBodyData(HTTPRequestHandle hRequest, uint8_t* pBodyDataBuffer, uint32_t unBufferSize); + virtual bool GetHTTPStreamingResponseBodyData(HTTPRequestHandle hRequest, uint32_t cOffset, uint8_t* pBodyDataBuffer, uint32_t unBufferSize); + virtual bool ReleaseHTTPRequest(HTTPRequestHandle hRequest); + virtual bool GetHTTPDownloadProgressPct(HTTPRequestHandle hRequest, float* pflPercentOut); + virtual bool SetHTTPRequestRawPostBody(HTTPRequestHandle hRequest, const char* pchContentType, uint8_t* pubBody, uint32_t unBodyLen); + virtual HTTPCookieContainerHandle CreateCookieContainer(bool bAllowResponsesToModify); + virtual bool ReleaseCookieContainer(HTTPCookieContainerHandle hCookieContainer); + virtual bool SetCookie(HTTPCookieContainerHandle hCookieContainer, const char* pchHost, const char* pchUrl, const char* pchCookie); + virtual bool SetHTTPRequestCookieContainer(HTTPRequestHandle hRequest, HTTPCookieContainerHandle hCookieContainer); + virtual bool SetHTTPRequestUserAgentInfo(HTTPRequestHandle hRequest, const char* pchUserAgentInfo); + virtual bool SetHTTPRequestRequiresVerifiedCertificate(HTTPRequestHandle hRequest, bool bRequireVerifiedCertificate); + virtual bool SetHTTPRequestAbsoluteTimeoutMS(HTTPRequestHandle hRequest, uint32_t unMilliseconds); + virtual bool GetHTTPRequestWasTimedOut(HTTPRequestHandle hRequest, bool* pbWasTimedOut); + }; +} diff --git a/src/client/steam/interfaces/matchmaking.cpp b/src/client/steam/interfaces/matchmaking.cpp new file mode 100644 index 00000000..8b8dec7b --- /dev/null +++ b/src/client/steam/interfaces/matchmaking.cpp @@ -0,0 +1,230 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + int matchmaking::GetFavoriteGameCount() + { + return 0; + } + + bool matchmaking::GetFavoriteGame(int iGame, unsigned int* pnAppID, unsigned int* pnIP, unsigned short* pnConnPort, + unsigned short* pnQueryPort, unsigned int* punFlags, + unsigned int* pRTime32LastPlayedOnServer) + { + return false; + } + + int matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, + unsigned short nQueryPort, unsigned int unFlags, + unsigned int rTime32LastPlayedOnServer) + { + return 0; + } + + bool matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, + unsigned short nQueryPort, unsigned int unFlags) + { + return false; + } + + unsigned long long matchmaking::RequestLobbyList() + { + return 0; + } + + void matchmaking::AddRequestLobbyListStringFilter(const char* pchKeyToMatch, const char* pchValueToMatch, + int eComparisonType) + { + } + + void matchmaking::AddRequestLobbyListNumericalFilter(const char* pchKeyToMatch, int nValueToMatch, + int eComparisonType) + { + } + + void matchmaking::AddRequestLobbyListNearValueFilter(const char* pchKeyToMatch, int nValueToBeCloseTo) + { + } + + void matchmaking::AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable) + { + } + + void matchmaking::AddRequestLobbyListDistanceFilter(int eLobbyDistanceFilter) + { + } + + void matchmaking::AddRequestLobbyListResultCountFilter(int cMaxResults) + { + } + + void matchmaking::AddRequestLobbyListCompatibleMembersFilter(steam_id steamID) + { + } + + steam_id matchmaking::GetLobbyByIndex(int iLobby) + { + steam_id id; + + id.raw.account_id = SteamUser()->GetSteamID().raw.account_id; + id.raw.universe = 1; + id.raw.account_type = 8; + id.raw.account_instance = 0x40000; + + return id; + } + + unsigned long long matchmaking::CreateLobby(int eLobbyType, int cMaxMembers) + { + const auto result = callbacks::register_call(); + auto retvals = static_cast(calloc(1, sizeof(lobby_created))); + //::Utils::Memory::AllocateArray(); + steam_id id; + + id.raw.account_id = SteamUser()->GetSteamID().raw.account_id; + id.raw.universe = 1; + id.raw.account_type = 8; + id.raw.account_instance = 0x40000; + + retvals->m_e_result = 1; + retvals->m_ul_steam_id_lobby = id; + + callbacks::return_call(retvals, sizeof(lobby_created), lobby_created::callback_id, result); + + matchmaking::JoinLobby(id); + + return result; + } + + unsigned long long matchmaking::JoinLobby(steam_id steamIDLobby) + { + const auto result = callbacks::register_call(); + auto* retvals = static_cast(calloc(1, sizeof(lobby_enter))); + //::Utils::Memory::AllocateArray(); + retvals->m_b_locked = false; + retvals->m_e_chat_room_enter_response = 1; + retvals->m_rgf_chat_permissions = 0xFFFFFFFF; + retvals->m_ul_steam_id_lobby = steamIDLobby; + + callbacks::return_call(retvals, sizeof(lobby_enter), lobby_enter::callback_id, result); + + return result; + } + + void matchmaking::LeaveLobby(steam_id steamIDLobby) + { + } + + bool matchmaking::InviteUserToLobby(steam_id steamIDLobby, steam_id steamIDInvitee) + { + return true; + } + + int matchmaking::GetNumLobbyMembers(steam_id steamIDLobby) + { + return 1; + } + + steam_id matchmaking::GetLobbyMemberByIndex(steam_id steamIDLobby, int iMember) + { + return SteamUser()->GetSteamID(); + } + + const char* matchmaking::GetLobbyData(steam_id steamIDLobby, const char* pchKey) + { + return ""; + } + + bool matchmaking::SetLobbyData(steam_id steamIDLobby, const char* pchKey, const char* pchValue) + { + return true; + } + + int matchmaking::GetLobbyDataCount(steam_id steamIDLobby) + { + return 0; + } + + bool matchmaking::GetLobbyDataByIndex(steam_id steamIDLobby, int iLobbyData, char* pchKey, int cchKeyBufferSize, + char* pchValue, int cchValueBufferSize) + { + return true; + } + + bool matchmaking::DeleteLobbyData(steam_id steamIDLobby, const char* pchKey) + { + return true; + } + + const char* matchmaking::GetLobbyMemberData(steam_id steamIDLobby, steam_id steamIDUser, const char* pchKey) + { + return ""; + } + + void matchmaking::SetLobbyMemberData(steam_id steamIDLobby, const char* pchKey, const char* pchValue) + { + } + + bool matchmaking::SendLobbyChatMsg(steam_id steamIDLobby, const void* pvMsgBody, int cubMsgBody) + { + return true; + } + + int matchmaking::GetLobbyChatEntry(steam_id steamIDLobby, int iChatID, steam_id* pSteamIDUser, void* pvData, + int cubData, int* peChatEntryType) + { + return 0; + } + + bool matchmaking::RequestLobbyData(steam_id steamIDLobby) + { + return true; + } + + void matchmaking::SetLobbyGameServer(steam_id steamIDLobby, unsigned int unGameServerIP, + unsigned short unGameServerPort, steam_id steamIDGameServer) + { + } + + bool matchmaking::GetLobbyGameServer(steam_id steamIDLobby, unsigned int* punGameServerIP, + unsigned short* punGameServerPort, steam_id* psteamIDGameServer) + { + return true; + } + + bool matchmaking::SetLobbyMemberLimit(steam_id steamIDLobby, int cMaxMembers) + { + return true; + } + + int matchmaking::GetLobbyMemberLimit(steam_id steamIDLobby) + { + return 0; + } + + bool matchmaking::SetLobbyType(steam_id steamIDLobby, int eLobbyType) + { + return true; + } + + bool matchmaking::SetLobbyJoinable(steam_id steamIDLobby, bool bLobbyJoinable) + { + return true; + } + + steam_id matchmaking::GetLobbyOwner(steam_id steamIDLobby) + { + return SteamUser()->GetSteamID(); + } + + bool matchmaking::SetLobbyOwner(steam_id steamIDLobby, steam_id steamIDNewOwner) + { + return true; + } + + bool matchmaking::SetLinkedLobby(steam_id steamIDLobby, steam_id steamIDLobby2) + { + return true; + } +} diff --git a/src/client/steam/interfaces/matchmaking.hpp b/src/client/steam/interfaces/matchmaking.hpp new file mode 100644 index 00000000..88923212 --- /dev/null +++ b/src/client/steam/interfaces/matchmaking.hpp @@ -0,0 +1,79 @@ +#pragma once + +namespace steam +{ + struct lobby_created final + { + enum { callback_id = 513 }; + + int m_e_result; + int m_pad; + steam_id m_ul_steam_id_lobby; + }; + + struct lobby_enter final + { + enum { callback_id = 504 }; + + steam_id m_ul_steam_id_lobby; + int m_rgf_chat_permissions; + bool m_b_locked; + int m_e_chat_room_enter_response; + }; + + class matchmaking + { + public: + ~matchmaking() = default; + + virtual int GetFavoriteGameCount(); + virtual bool GetFavoriteGame(int iGame, unsigned int* pnAppID, unsigned int* pnIP, unsigned short* pnConnPort, + unsigned short* pnQueryPort, unsigned int* punFlags, + unsigned int* pRTime32LastPlayedOnServer); + virtual int AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, + unsigned short nQueryPort, unsigned int unFlags, + unsigned int rTime32LastPlayedOnServer); + virtual bool RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, + unsigned short nQueryPort, unsigned int unFlags); + virtual unsigned long long RequestLobbyList(); + virtual void AddRequestLobbyListStringFilter(const char* pchKeyToMatch, const char* pchValueToMatch, + int eComparisonType); + virtual void AddRequestLobbyListNumericalFilter(const char* pchKeyToMatch, int nValueToMatch, + int eComparisonType); + virtual void AddRequestLobbyListNearValueFilter(const char* pchKeyToMatch, int nValueToBeCloseTo); + virtual void AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable); + virtual void AddRequestLobbyListDistanceFilter(int eLobbyDistanceFilter); + virtual void AddRequestLobbyListResultCountFilter(int cMaxResults); + virtual void AddRequestLobbyListCompatibleMembersFilter(steam_id steamID); + virtual steam_id GetLobbyByIndex(int iLobby); + virtual unsigned long long CreateLobby(int eLobbyType, int cMaxMembers); + virtual unsigned long long JoinLobby(steam_id steamIDLobby); + virtual void LeaveLobby(steam_id steamIDLobby); + virtual bool InviteUserToLobby(steam_id steamIDLobby, steam_id steamIDInvitee); + virtual int GetNumLobbyMembers(steam_id steamIDLobby); + virtual steam_id GetLobbyMemberByIndex(steam_id steamIDLobby, int iMember); + virtual const char* GetLobbyData(steam_id steamIDLobby, const char* pchKey); + virtual bool SetLobbyData(steam_id steamIDLobby, const char* pchKey, const char* pchValue); + virtual int GetLobbyDataCount(steam_id steamIDLobby); + virtual bool GetLobbyDataByIndex(steam_id steamIDLobby, int iLobbyData, char* pchKey, int cchKeyBufferSize, + char* pchValue, int cchValueBufferSize); + virtual bool DeleteLobbyData(steam_id steamIDLobby, const char* pchKey); + virtual const char* GetLobbyMemberData(steam_id steamIDLobby, steam_id steamIDUser, const char* pchKey); + virtual void SetLobbyMemberData(steam_id steamIDLobby, const char* pchKey, const char* pchValue); + virtual bool SendLobbyChatMsg(steam_id steamIDLobby, const void* pvMsgBody, int cubMsgBody); + virtual int GetLobbyChatEntry(steam_id steamIDLobby, int iChatID, steam_id* pSteamIDUser, void* pvData, + int cubData, int* peChatEntryType); + virtual bool RequestLobbyData(steam_id steamIDLobby); + virtual void SetLobbyGameServer(steam_id steamIDLobby, unsigned int unGameServerIP, + unsigned short unGameServerPort, steam_id steamIDGameServer); + virtual bool GetLobbyGameServer(steam_id steamIDLobby, unsigned int* punGameServerIP, + unsigned short* punGameServerPort, steam_id* psteamIDGameServer); + virtual bool SetLobbyMemberLimit(steam_id steamIDLobby, int cMaxMembers); + virtual int GetLobbyMemberLimit(steam_id steamIDLobby); + virtual bool SetLobbyType(steam_id steamIDLobby, int eLobbyType); + virtual bool SetLobbyJoinable(steam_id steamIDLobby, bool bLobbyJoinable); + virtual steam_id GetLobbyOwner(steam_id steamIDLobby); + virtual bool SetLobbyOwner(steam_id steamIDLobby, steam_id steamIDNewOwner); + virtual bool SetLinkedLobby(steam_id steamIDLobby, steam_id steamIDLobby2); + }; +} diff --git a/src/client/steam/interfaces/matchmaking_servers.cpp b/src/client/steam/interfaces/matchmaking_servers.cpp new file mode 100644 index 00000000..d327238a --- /dev/null +++ b/src/client/steam/interfaces/matchmaking_servers.cpp @@ -0,0 +1,90 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + void* matchmaking_servers::RequestInternetServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse) + { + return nullptr; + } + + void* matchmaking_servers::RequestLANServerList(unsigned int iApp, void* pRequestServersResponse) + { + return nullptr; + } + + void* matchmaking_servers::RequestFriendsServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse) + { + return nullptr; + } + + void* matchmaking_servers::RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse) + { + return nullptr; + } + + void* matchmaking_servers::RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse) + { + return nullptr; + } + + void* matchmaking_servers::RequestSpectatorServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse) + { + return nullptr; + } + + void matchmaking_servers::ReleaseRequest(void* hServerListRequest) + { + } + + void* matchmaking_servers::GetServerDetails(void* hRequest, int iServer) + { + return nullptr; + } + + void matchmaking_servers::CancelQuery(void* hRequest) + { + } + + void matchmaking_servers::RefreshQuery(void* hRequest) + { + } + + bool matchmaking_servers::IsRefreshing(void* hRequest) + { + return false; + } + + int matchmaking_servers::GetServerCount(void* hRequest) + { + return 0; + } + + void matchmaking_servers::RefreshServer(void* hRequest, int iServer) + { + } + + int matchmaking_servers::PingServer(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) + { + return 0; + } + + int matchmaking_servers::PlayerDetails(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) + { + return 0; + } + + int matchmaking_servers::ServerRules(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse) + { + return 0; + } + + void matchmaking_servers::CancelServerQuery(int hServerQuery) + { + } +} diff --git a/src/client/steam/interfaces/matchmaking_servers.hpp b/src/client/steam/interfaces/matchmaking_servers.hpp new file mode 100644 index 00000000..5d6b10d9 --- /dev/null +++ b/src/client/steam/interfaces/matchmaking_servers.hpp @@ -0,0 +1,33 @@ +#pragma once + +namespace steam +{ + class matchmaking_servers + { + public: + ~matchmaking_servers() = default; + + virtual void* RequestInternetServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse); + virtual void* RequestLANServerList(unsigned int iApp, void* pRequestServersResponse); + virtual void* RequestFriendsServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse); + virtual void* RequestFavoritesServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse); + virtual void* RequestHistoryServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse); + virtual void* RequestSpectatorServerList(unsigned int iApp, void** ppchFilters, unsigned int nFilters, + void* pRequestServersResponse); + virtual void ReleaseRequest(void* hServerListRequest); + virtual void* GetServerDetails(void* hRequest, int iServer); + virtual void CancelQuery(void* hRequest); + virtual void RefreshQuery(void* hRequest); + virtual bool IsRefreshing(void* hRequest); + virtual int GetServerCount(void* hRequest); + virtual void RefreshServer(void* hRequest, int iServer); + virtual int PingServer(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse); + virtual int PlayerDetails(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse); + virtual int ServerRules(unsigned int unIP, unsigned short usPort, void* pRequestServersResponse); + virtual void CancelServerQuery(int hServerQuery); + }; +} diff --git a/src/client/steam/interfaces/networking.cpp b/src/client/steam/interfaces/networking.cpp new file mode 100644 index 00000000..d0ee6e0e --- /dev/null +++ b/src/client/steam/interfaces/networking.cpp @@ -0,0 +1,121 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool networking::SendP2PPacket(steam_id steamIDRemote, const void* pubData, unsigned int cubData, int eP2PSendType) + { + return false; + } + + bool networking::IsP2PPacketAvailable(unsigned int* pcubMsgSize, int idk) + { + return false; + } + + bool networking::ReadP2PPacket(void* pubDest, unsigned int cubDest, unsigned int* pcubMsgSize, + steam_id* psteamIDRemote) + { + return false; + } + + bool networking::AcceptP2PSessionWithUser(steam_id steamIDRemote) + { + return false; + } + + bool networking::CloseP2PSessionWithUser(steam_id steamIDRemote) + { + return false; + } + + bool networking::CloseP2PChannelWithUser(steam_id steamIDRemote, int iVirtualPort) + { + return false; + } + + bool networking::GetP2PSessionState(steam_id steamIDRemote, void* pConnectionState) + { + return false; + } + + bool networking::AllowP2PPacketRelay(bool bAllow) + { + return false; + } + + unsigned int networking::CreateListenSocket(int nVirtualP2PPort, unsigned int nIP, unsigned short nPort, + bool bAllowUseOfPacketRelay) + { + return NULL; + } + + unsigned int networking::CreateP2PConnectionSocket(steam_id steamIDTarget, int nVirtualPort, int nTimeoutSec, + bool bAllowUseOfPacketRelay) + { + return NULL; + } + + unsigned int networking::CreateConnectionSocket(unsigned int nIP, unsigned short nPort, int nTimeoutSec) + { + return NULL; + } + + bool networking::DestroySocket(unsigned int hSocket, bool bNotifyRemoteEnd) + { + return false; + } + + bool networking::DestroyListenSocket(unsigned int hSocket, bool bNotifyRemoteEnd) + { + return false; + } + + bool networking::SendDataOnSocket(unsigned int hSocket, void* pubData, unsigned int cubData, bool bReliable) + { + return false; + } + + bool networking::IsDataAvailableOnSocket(unsigned int hSocket, unsigned int* pcubMsgSize) + { + return false; + } + + bool networking::RetrieveDataFromSocket(unsigned int hSocket, void* pubDest, unsigned int cubDest, + unsigned int* pcubMsgSize) + { + return false; + } + + bool networking::IsDataAvailable(unsigned int hListenSocket, unsigned int* pcubMsgSize, unsigned int* phSocket) + { + return false; + } + + bool networking::RetrieveData(unsigned int hListenSocket, void* pubDest, unsigned int cubDest, + unsigned int* pcubMsgSize, unsigned int* phSocket) + { + return false; + } + + bool networking::GetSocketInfo(unsigned int hSocket, steam_id* pSteamIDRemote, int* peSocketStatus, + unsigned int* punIPRemote, unsigned short* punPortRemote) + { + return false; + } + + bool networking::GetListenSocketInfo(unsigned int hListenSocket, unsigned int* pnIP, unsigned short* pnPort) + { + return false; + } + + int networking::GetSocketConnectionType(unsigned int hSocket) + { + return 0; + } + + int networking::GetMaxPacketSize(unsigned int hSocket) + { + return 0; + } +} diff --git a/src/client/steam/interfaces/networking.hpp b/src/client/steam/interfaces/networking.hpp new file mode 100644 index 00000000..bb1b29dd --- /dev/null +++ b/src/client/steam/interfaces/networking.hpp @@ -0,0 +1,39 @@ +#pragma once + +namespace steam +{ + class networking + { + public: + ~networking() = default; + + virtual bool SendP2PPacket(steam_id steamIDRemote, const void* pubData, unsigned int cubData, int eP2PSendType); + virtual bool IsP2PPacketAvailable(unsigned int* pcubMsgSize, int idk); + virtual bool ReadP2PPacket(void* pubDest, unsigned int cubDest, unsigned int* pcubMsgSize, + steam_id* psteamIDRemote); + virtual bool AcceptP2PSessionWithUser(steam_id steamIDRemote); + virtual bool CloseP2PSessionWithUser(steam_id steamIDRemote); + virtual bool CloseP2PChannelWithUser(steam_id steamIDRemote, int iVirtualPort); + virtual bool GetP2PSessionState(steam_id steamIDRemote, void* pConnectionState); + virtual bool AllowP2PPacketRelay(bool bAllow); + virtual unsigned int CreateListenSocket(int nVirtualP2PPort, unsigned int nIP, unsigned short nPort, + bool bAllowUseOfPacketRelay); + virtual unsigned int CreateP2PConnectionSocket(steam_id steamIDTarget, int nVirtualPort, int nTimeoutSec, + bool bAllowUseOfPacketRelay); + virtual unsigned int CreateConnectionSocket(unsigned int nIP, unsigned short nPort, int nTimeoutSec); + virtual bool DestroySocket(unsigned int hSocket, bool bNotifyRemoteEnd); + virtual bool DestroyListenSocket(unsigned int hSocket, bool bNotifyRemoteEnd); + virtual bool SendDataOnSocket(unsigned int hSocket, void* pubData, unsigned int cubData, bool bReliable); + virtual bool IsDataAvailableOnSocket(unsigned int hSocket, unsigned int* pcubMsgSize); + virtual bool RetrieveDataFromSocket(unsigned int hSocket, void* pubDest, unsigned int cubDest, + unsigned int* pcubMsgSize); + virtual bool IsDataAvailable(unsigned int hListenSocket, unsigned int* pcubMsgSize, unsigned int* phSocket); + virtual bool RetrieveData(unsigned int hListenSocket, void* pubDest, unsigned int cubDest, + unsigned int* pcubMsgSize, unsigned int* phSocket); + virtual bool GetSocketInfo(unsigned int hSocket, steam_id* pSteamIDRemote, int* peSocketStatus, + unsigned int* punIPRemote, unsigned short* punPortRemote); + virtual bool GetListenSocketInfo(unsigned int hListenSocket, unsigned int* pnIP, unsigned short* pnPort); + virtual int GetSocketConnectionType(unsigned int hSocket); + virtual int GetMaxPacketSize(unsigned int hSocket); + }; +} diff --git a/src/client/steam/interfaces/remote_storage.cpp b/src/client/steam/interfaces/remote_storage.cpp new file mode 100644 index 00000000..02bc968f --- /dev/null +++ b/src/client/steam/interfaces/remote_storage.cpp @@ -0,0 +1,283 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool remote_storage::FileWrite(const char* pchFile, const void* pvData, int cubData) + { + return true; + } + + int remote_storage::FileRead(const char* pchFile, void* pvData, int cubDataToRead) + { + return 0; + } + + bool remote_storage::FileForget(const char* pchFile) + { + return true; + } + + bool remote_storage::FileDelete(const char* pchFile) + { + return true; + } + + unsigned long long remote_storage::FileShare(const char* pchFile) + { + return 0; + } + + bool remote_storage::SetSyncPlatforms(const char* pchFile, unsigned int eRemoteStoragePlatform) + { + return true; + } + + unsigned long long remote_storage::FileWriteStreamOpen(const char* pchFile) + { + return 0; + } + + int remote_storage::FileWriteStreamWriteChunk(unsigned long long hStream, const void* pvData, int cubData) + { + return 1; + } + + int remote_storage::FileWriteStreamClose(unsigned long long hStream) + { + return 1; + } + + int remote_storage::FileWriteStreamCancel(unsigned long long hStream) + { + return 1; + } + + bool remote_storage::FileExists(const char* pchFile) + { + return true; + } + + bool remote_storage::FilePersisted(const char* pchFile) + { + return true; + } + + int remote_storage::GetFileSize(const char* pchFile) + { + return 0; + } + + long long remote_storage::GetFileTimestamp(const char* pchFile) + { + return 0; + } + + unsigned remote_storage::GetSyncPlatforms(const char* pchFile) + { + return 0; + } + + int remote_storage::GetFileCount() + { + return 0; + } + + const char* remote_storage::GetFileNameAndSize(int iFile, int* pnFileSizeInBytes) + { + *pnFileSizeInBytes = 0; + return ""; + } + + bool remote_storage::GetQuota(int* pnTotalBytes, int* puAvailableBytes) + { + *pnTotalBytes = 0x10000000; + *puAvailableBytes = 0x10000000; + return false; + } + + bool remote_storage::IsCloudEnabledForAccount() + { + return false; + } + + bool remote_storage::IsCloudEnabledForApp() + { + return false; + } + + void remote_storage::SetCloudEnabledForApp(bool bEnabled) + { + } + + unsigned long long remote_storage::UGCDownload(unsigned long long hContent, unsigned int uUnk) + { + return 0; + } + + bool remote_storage::GetUGCDownloadProgress(unsigned long long hContent, unsigned int* puDownloadedBytes, + unsigned int* puTotalBytes) + { + return false; + } + + bool remote_storage::GetUGCDetails(unsigned long long hContent, unsigned int* pnAppID, char** ppchName, + int* pnFileSizeInBytes, steam_id* pSteamIDOwner) + { + return false; + } + + int remote_storage::UGCRead(unsigned long long hContent, void* pvData, int cubDataToRead, unsigned int uOffset) + { + return 0; + } + + int remote_storage::GetCachedUGCCount() + { + return 0; + } + + unsigned long long remote_storage::GetCachedUGCHandle(int iCachedContent) + { + return 0; + } + + unsigned long long remote_storage::PublishWorkshopFile(const char* pchFile, const char* pchPreviewFile, + unsigned int nConsumerAppId, const char* pchTitle, + const char* pchDescription, unsigned int eVisibility, + int* pTags, unsigned int eWorkshopFileType) + { + return 0; + } + + unsigned long long remote_storage::CreatePublishedFileUpdateRequest(unsigned long long unPublishedFileId) + { + return 0; + } + + bool remote_storage::UpdatePublishedFileFile(unsigned long long hUpdateRequest, const char* pchFile) + { + return false; + } + + bool remote_storage::UpdatePublishedFilePreviewFile(unsigned long long hUpdateRequest, const char* pchPreviewFile) + { + return false; + } + + bool remote_storage::UpdatePublishedFileTitle(unsigned long long hUpdateRequest, const char* pchTitle) + { + return false; + } + + bool remote_storage::UpdatePublishedFileDescription(unsigned long long hUpdateRequest, const char* pchDescription) + { + return false; + } + + bool remote_storage::UpdatePublishedFileVisibility(unsigned long long hUpdateRequest, unsigned int eVisibility) + { + return false; + } + + bool remote_storage::UpdatePublishedFileTags(unsigned long long hUpdateRequest, int* pTags) + { + return false; + } + + unsigned long long remote_storage::CommitPublishedFileUpdate(unsigned long long hUpdateRequest) + { + return 0; + } + + unsigned long long remote_storage::GetPublishedFileDetails(unsigned long long unPublishedFileId) + { + return 0; + } + + unsigned long long remote_storage::DeletePublishedFile(unsigned long long unPublishedFileId) + { + return 0; + } + + unsigned long long remote_storage::EnumerateUserPublishedFiles(unsigned int uStartIndex) + { + return 0; + } + + unsigned long long remote_storage::SubscribePublishedFile(unsigned long long unPublishedFileId) + { + return 0; + } + + unsigned long long remote_storage::EnumerateUserSubscribedFiles(unsigned int uStartIndex) + { + return 0; + } + + unsigned long long remote_storage::UnsubscribePublishedFile(unsigned long long unPublishedFileId) + { + return 0; + } + + bool remote_storage::UpdatePublishedFileSetChangeDescription(unsigned long long hUpdateRequest, + const char* cszDescription) + { + return false; + } + + unsigned long long remote_storage::GetPublishedItemVoteDetails(unsigned long long unPublishedFileId) + { + return 0; + } + + unsigned long long remote_storage::UpdateUserPublishedItemVote(unsigned long long unPublishedFileId, bool bVoteUp) + { + return 0; + } + + unsigned long long remote_storage::GetUserPublishedItemVoteDetails(unsigned long long unPublishedFileId) + { + return 0; + } + + unsigned long long remote_storage::EnumerateUserSharedWorkshopFiles(unsigned int nAppId, steam_id creatorSteamID, + unsigned int uStartIndex, int* pRequiredTags, + int* pExcludedTags) + { + return 0; + } + + unsigned long long remote_storage::PublishVideo(unsigned int eVideoProvider, const char* cszVideoAccountName, + const char* cszVideoIdentifier, const char* cszFileName, + unsigned int nConsumerAppId, const char* cszTitle, + const char* cszDescription, unsigned int eVisibility, int* pTags) + { + return 0; + } + + unsigned long long remote_storage::SetUserPublishedFileAction(unsigned long long unPublishedFileId, + unsigned int eAction) + { + return 0; + } + + unsigned long long remote_storage::EnumeratePublishedFilesByUserAction( + unsigned int eAction, unsigned int uStartIndex) + { + return 0; + } + + unsigned long long remote_storage::EnumeratePublishedWorkshopFiles(unsigned int eType, unsigned int uStartIndex, + unsigned int cDays, unsigned int cCount, + int* pTags, int* pUserTags) + { + return 0; + } + + unsigned long long remote_storage::UGCDownloadToLocation(unsigned long long hContent, const char* cszLocation, + unsigned int uUnk) + { + return 0; + } +} diff --git a/src/client/steam/interfaces/remote_storage.hpp b/src/client/steam/interfaces/remote_storage.hpp new file mode 100644 index 00000000..ec0d64ea --- /dev/null +++ b/src/client/steam/interfaces/remote_storage.hpp @@ -0,0 +1,78 @@ +#pragma once + +namespace steam +{ + class remote_storage + { + public: + ~remote_storage() = default; + + virtual bool FileWrite(const char* pchFile, const void* pvData, int cubData); + virtual int FileRead(const char* pchFile, void* pvData, int cubDataToRead); + virtual bool FileForget(const char* pchFile); + virtual bool FileDelete(const char* pchFile); + virtual unsigned long long FileShare(const char* pchFile); + virtual bool SetSyncPlatforms(const char* pchFile, unsigned int eRemoteStoragePlatform); + virtual unsigned long long FileWriteStreamOpen(const char* pchFile); + virtual int FileWriteStreamWriteChunk(unsigned long long hStream, const void* pvData, int cubData); + virtual int FileWriteStreamClose(unsigned long long hStream); + virtual int FileWriteStreamCancel(unsigned long long hStream); + virtual bool FileExists(const char* pchFile); + virtual bool FilePersisted(const char* pchFile); + virtual int GetFileSize(const char* pchFile); + virtual long long GetFileTimestamp(const char* pchFile); + virtual unsigned int GetSyncPlatforms(const char* pchFile); + virtual int GetFileCount(); + virtual const char* GetFileNameAndSize(int iFile, int* pnFileSizeInBytes); + virtual bool GetQuota(int* pnTotalBytes, int* puAvailableBytes); + virtual bool IsCloudEnabledForAccount(); + virtual bool IsCloudEnabledForApp(); + virtual void SetCloudEnabledForApp(bool bEnabled); + virtual unsigned long long UGCDownload(unsigned long long hContent, unsigned int uUnk); + virtual bool GetUGCDownloadProgress(unsigned long long hContent, unsigned int* puDownloadedBytes, + unsigned int* puTotalBytes); + virtual bool GetUGCDetails(unsigned long long hContent, unsigned int* pnAppID, char** ppchName, + int* pnFileSizeInBytes, steam_id* pSteamIDOwner); + virtual int UGCRead(unsigned long long hContent, void* pvData, int cubDataToRead, unsigned int uOffset); + virtual int GetCachedUGCCount(); + virtual unsigned long long GetCachedUGCHandle(int iCachedContent); + virtual unsigned long long PublishWorkshopFile(const char* pchFile, const char* pchPreviewFile, + unsigned int nConsumerAppId, const char* pchTitle, + const char* pchDescription, unsigned int eVisibility, int* pTags, + unsigned int eWorkshopFileType); + virtual unsigned long long CreatePublishedFileUpdateRequest(unsigned long long unPublishedFileId); + virtual bool UpdatePublishedFileFile(unsigned long long hUpdateRequest, const char* pchFile); + virtual bool UpdatePublishedFilePreviewFile(unsigned long long hUpdateRequest, const char* pchPreviewFile); + virtual bool UpdatePublishedFileTitle(unsigned long long hUpdateRequest, const char* pchTitle); + virtual bool UpdatePublishedFileDescription(unsigned long long hUpdateRequest, const char* pchDescription); + virtual bool UpdatePublishedFileVisibility(unsigned long long hUpdateRequest, unsigned int eVisibility); + virtual bool UpdatePublishedFileTags(unsigned long long hUpdateRequest, int* pTags); + virtual unsigned long long CommitPublishedFileUpdate(unsigned long long hUpdateRequest); + virtual unsigned long long GetPublishedFileDetails(unsigned long long unPublishedFileId); + virtual unsigned long long DeletePublishedFile(unsigned long long unPublishedFileId); + virtual unsigned long long EnumerateUserPublishedFiles(unsigned int uStartIndex); + virtual unsigned long long SubscribePublishedFile(unsigned long long unPublishedFileId); + virtual unsigned long long EnumerateUserSubscribedFiles(unsigned int uStartIndex); + virtual unsigned long long UnsubscribePublishedFile(unsigned long long unPublishedFileId); + virtual bool UpdatePublishedFileSetChangeDescription(unsigned long long hUpdateRequest, + const char* cszDescription); + virtual unsigned long long GetPublishedItemVoteDetails(unsigned long long unPublishedFileId); + virtual unsigned long long UpdateUserPublishedItemVote(unsigned long long unPublishedFileId, bool bVoteUp); + virtual unsigned long long GetUserPublishedItemVoteDetails(unsigned long long unPublishedFileId); + virtual unsigned long long EnumerateUserSharedWorkshopFiles(unsigned int nAppId, steam_id creatorSteamID, + unsigned int uStartIndex, int* pRequiredTags, + int* pExcludedTags); + virtual unsigned long long PublishVideo(unsigned int eVideoProvider, const char* cszVideoAccountName, + const char* cszVideoIdentifier, const char* cszFileName, + unsigned int nConsumerAppId, const char* cszTitle, + const char* cszDescription, unsigned int eVisibility, int* pTags); + virtual unsigned long long SetUserPublishedFileAction(unsigned long long unPublishedFileId, + unsigned int eAction); + virtual unsigned long long EnumeratePublishedFilesByUserAction(unsigned int eAction, unsigned int uStartIndex); + virtual unsigned long long EnumeratePublishedWorkshopFiles(unsigned int eType, unsigned int uStartIndex, + unsigned int cDays, unsigned int cCount, int* pTags, + int* pUserTags); + virtual unsigned long long UGCDownloadToLocation(unsigned long long hContent, const char* cszLocation, + unsigned int uUnk); + }; +} diff --git a/src/client/steam/interfaces/screenshots.cpp b/src/client/steam/interfaces/screenshots.cpp new file mode 100644 index 00000000..cd34b47e --- /dev/null +++ b/src/client/steam/interfaces/screenshots.cpp @@ -0,0 +1,40 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + uint64_t screenshots::WriteScreenshot(void* pubRGB, uint32_t cubRGB, int nWidth, int nHeight) + { + return 0; + } + + uint64_t screenshots::AddScreenshotToLibrary(const char* pchFilename, const char* pchThumbnailFilename, int nWidth, int nHeight) + { + return 0; + } + + void screenshots::TriggerScreenshot() + { + + } + + void screenshots::HookScreenshots(bool bHook) + { + + } + + bool screenshots::SetLocation(uint64_t hScreenshot, const char* pchLocation) + { + return false; + } + + bool screenshots::TagUser(uint64_t hScreenshot, steam_id steamID) + { + return false; + } + + bool screenshots::TagPublishedFile(uint64_t hScreenshot, uint64_t unPublishedFileID) + { + return false; + } +} diff --git a/src/client/steam/interfaces/screenshots.hpp b/src/client/steam/interfaces/screenshots.hpp new file mode 100644 index 00000000..0a6e9db3 --- /dev/null +++ b/src/client/steam/interfaces/screenshots.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace steam +{ + class screenshots + { + public: + ~screenshots() = default; + + virtual uint64_t WriteScreenshot(void* pubRGB, uint32_t cubRGB, int nWidth, int nHeight); + virtual uint64_t AddScreenshotToLibrary(const char* pchFilename, const char* pchThumbnailFilename, int nWidth, int nHeight); + virtual void TriggerScreenshot(); + virtual void HookScreenshots(bool bHook); + virtual bool SetLocation(uint64_t hScreenshot, const char* pchLocation); + virtual bool TagUser(uint64_t hScreenshot, steam_id steamID); + virtual bool TagPublishedFile(uint64_t hScreenshot, uint64_t unPublishedFileID); + }; +} diff --git a/src/client/steam/interfaces/unified_messages.cpp b/src/client/steam/interfaces/unified_messages.cpp new file mode 100644 index 00000000..8e9462ae --- /dev/null +++ b/src/client/steam/interfaces/unified_messages.cpp @@ -0,0 +1,30 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + ClientUnifiedMessageHandle unified_messages::SendMethod(const char* pchServiceMethod, const void* pRequestBuffer, uint32_t unRequestBufferSize, uint64_t unContext) + { + return 0; + } + + bool unified_messages::GetMethodResponseInfo(ClientUnifiedMessageHandle hHandle, uint32_t* punResponseSize, uint32_t* peResult) + { + return false; + } + + bool unified_messages::GetMethodResponseData(ClientUnifiedMessageHandle hHandle, void* pResponseBuffer, uint32_t unResponseBufferSize, bool bAutoRelease) + { + return false; + } + + bool unified_messages::ReleaseMethod(ClientUnifiedMessageHandle hHandle) + { + return false; + } + + bool unified_messages::SendNotification(const char* pchServiceNotification, const void* pNotificationBuffer, uint32_t unNotificationBufferSize) + { + return false; + } +} diff --git a/src/client/steam/interfaces/unified_messages.hpp b/src/client/steam/interfaces/unified_messages.hpp new file mode 100644 index 00000000..22722555 --- /dev/null +++ b/src/client/steam/interfaces/unified_messages.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace steam +{ + using ClientUnifiedMessageHandle = uint64_t; + + class unified_messages + { + public: + ~unified_messages() = default; + + virtual ClientUnifiedMessageHandle SendMethod(const char* pchServiceMethod, const void* pRequestBuffer, uint32_t unRequestBufferSize, uint64_t unContext); + virtual bool GetMethodResponseInfo(ClientUnifiedMessageHandle hHandle, uint32_t* punResponseSize, uint32_t* peResult); + virtual bool GetMethodResponseData(ClientUnifiedMessageHandle hHandle, void* pResponseBuffer, uint32_t unResponseBufferSize, bool bAutoRelease); + virtual bool ReleaseMethod(ClientUnifiedMessageHandle hHandle); + virtual bool SendNotification(const char* pchServiceNotification, const void* pNotificationBuffer, uint32_t unNotificationBufferSize); + }; +} diff --git a/src/client/steam/interfaces/user.cpp b/src/client/steam/interfaces/user.cpp new file mode 100644 index 00000000..e52bf004 --- /dev/null +++ b/src/client/steam/interfaces/user.cpp @@ -0,0 +1,194 @@ +#include +#include "../steam.hpp" + + +namespace steam +{ + namespace + { + std::string auth_ticket; + + steam_id generate_steam_id() + { + steam_id id{}; + id.bits = 76561197960287930; + return id; + } + } + + int user::GetHSteamUser() + { + return NULL; + } + + bool user::LoggedOn() + { + return true; + } + + steam_id user::GetSteamID() + { + static auto id = generate_steam_id(); + 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) + { + static uint32_t ticket = 0; + *pcbTicket = 1; + + const auto result = callbacks::register_call(); + auto* response = static_cast(calloc( + 1, sizeof(get_auth_session_ticket_response))); + response->m_h_auth_ticket = ++ticket; + response->m_e_result = 1; // k_EResultOK; + + callbacks::return_call(response, sizeof(get_auth_session_ticket_response), + get_auth_session_ticket_response::callback_id, result); + return response->m_h_auth_ticket; + } + + 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 long long user::RequestEncryptedAppTicket(void* pUserData, int cbUserData) + { + const auto id = this->GetSteamID(); + + auth_ticket = "S1"; + auth_ticket.resize(32); + auth_ticket.append(static_cast(pUserData), 24); // key + auth_ticket.append(reinterpret_cast(&id.bits), sizeof(id.bits)); // user id + auth_ticket.append(&static_cast(pUserData)[24], 64); // user name + + // Create the call response + const auto result = callbacks::register_call(); + const 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 = static_cast(size); + + return true; + } + + int user::GetGameBadgeLevel(int nSeries, bool bFoil) + { + return 1; + } + int user::GetPlayerSteamLevel() + { + return 0; + } + uint64_t user::RequestStoreAuthURL(const char* pchRedirectURL) + { + return 0; + } + bool user::BIsPhoneVerified() + { + return true; + } + + bool user::BIsTwoFactorEnabled() + { + return true; + } + + bool user::BIsPhoneIdentifying() + { + return false; + } + bool user::BIsPhoneRequiringVerification() + { + return false; + } +} diff --git a/src/client/steam/interfaces/user.hpp b/src/client/steam/interfaces/user.hpp new file mode 100644 index 00000000..81659174 --- /dev/null +++ b/src/client/steam/interfaces/user.hpp @@ -0,0 +1,63 @@ +#pragma once + +namespace steam +{ + struct encrypted_app_ticket_response final + { + enum { callback_id = 154 }; + + int m_e_result; + }; + + struct get_auth_session_ticket_response + { + enum { callback_id = 163 }; + + unsigned int m_h_auth_ticket; + int m_e_result; + }; + + class user + { + public: + ~user() = default; + + virtual int GetHSteamUser(); + virtual bool LoggedOn(); + virtual steam_id GetSteamID(); + + virtual int InitiateGameConnection(void* pAuthBlob, int cbMaxAuthBlob, steam_id steamIDGameServer, + unsigned int unIPServer, unsigned short usPortServer, bool bSecure); + virtual void TerminateGameConnection(unsigned int unIPServer, unsigned short usPortServer); + virtual void TrackAppUsageEvent(steam_id gameID, int eAppUsageEvent, const char* pchExtraInfo = ""); + virtual bool GetUserDataFolder(char* pchBuffer, int cubBuffer); + virtual void StartVoiceRecording(); + virtual void StopVoiceRecording(); + virtual int GetAvailableVoice(unsigned int* pcbCompressed, unsigned int* pcbUncompressed, + unsigned int nUncompressedVoiceDesiredSampleRate); + virtual int GetVoice(bool bWantCompressed, void* pDestBuffer, unsigned int cbDestBufferSize, + unsigned int* nBytesWritten, bool bWantUncompressed, void* pUncompressedDestBuffer, + unsigned int cbUncompressedDestBufferSize, unsigned int* nUncompressBytesWritten, + unsigned int nUncompressedVoiceDesiredSampleRate); + virtual int DecompressVoice(void* pCompressed, unsigned int cbCompressed, void* pDestBuffer, + unsigned int cbDestBufferSize, unsigned int* nBytesWritten); + virtual unsigned int GetVoiceOptimalSampleRate(); + virtual unsigned int GetAuthSessionTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket); + virtual int BeginAuthSession(const void* pAuthTicket, int cbAuthTicket, steam_id steamID); + virtual void EndAuthSession(steam_id steamID); + virtual void CancelAuthTicket(unsigned int hAuthTicket); + virtual unsigned int UserHasLicenseForApp(steam_id steamID, unsigned int appID); + virtual bool BIsBehindNAT(); + virtual void AdvertiseGame(steam_id steamIDGameServer, unsigned int unIPServer, unsigned short usPortServer); + virtual unsigned long long RequestEncryptedAppTicket(void* pUserData, int cbUserData); + virtual bool GetEncryptedAppTicket(void* pTicket, int cbMaxTicket, unsigned int* pcbTicket); + + virtual int GetGameBadgeLevel(int nSeries, bool bFoil); + virtual int GetPlayerSteamLevel(); + virtual uint64_t RequestStoreAuthURL(const char* pchRedirectURL); + virtual bool BIsPhoneVerified(); + virtual bool BIsTwoFactorEnabled(); + virtual bool BIsPhoneIdentifying(); + virtual bool BIsPhoneRequiringVerification(); + }; +} diff --git a/src/client/steam/interfaces/user_stats.cpp b/src/client/steam/interfaces/user_stats.cpp new file mode 100644 index 00000000..13d4ccc0 --- /dev/null +++ b/src/client/steam/interfaces/user_stats.cpp @@ -0,0 +1,231 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + bool user_stats::RequestCurrentStats() + { + return true; + } + + bool user_stats::GetStat(const char* pchName, int* pData) + { + return false; + } + + bool user_stats::GetStat(const char* pchName, float* pData) + { + return false; + } + + bool user_stats::SetStat(const char* pchName, int nData) + { + return false; + } + + bool user_stats::SetStat(const char* pchName, float fData) + { + return false; + } + + bool user_stats::UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength) + { + return false; + } + + bool user_stats::GetAchievement(const char* pchName, bool* pbAchieved) + { + return true; + } + + bool user_stats::SetAchievement(const char* pchName) + { + return true; + } + + bool user_stats::ClearAchievement(const char* pchName) + { + return true; + } + + bool user_stats::GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, unsigned int* punUnlockTime) + { + return true; + } + + bool user_stats::StoreStats() + { + return true; + } + + int user_stats::GetAchievementIcon(const char* pchName) + { + return 0; + } + + const char* user_stats::GetAchievementDisplayAttribute(const char* pchName, const char* pchKey) + { + return ""; + } + + bool user_stats::IndicateAchievementProgress(const char* pchName, unsigned int nCurProgress, + unsigned int nMaxProgress) + { + return true; + } + + unsigned int user_stats::GetNumAchievements() + { + return 0; + } + + const char* user_stats::GetAchievementName(unsigned int iAchievement) + { + return ""; + } + + unsigned long long user_stats::RequestUserStats(steam_id steamIDUser) + { + return 0; + } + + bool user_stats::GetUserStat(steam_id steamIDUser, const char* pchName, int* pData) + { + return false; + } + + bool user_stats::GetUserStat(steam_id steamIDUser, const char* pchName, float* pData) + { + return false; + } + + bool user_stats::GetUserAchievement(steam_id steamIDUser, const char* pchName, bool* pbAchieved) + { + return true; + } + + bool user_stats::GetUserAchievementAndUnlockTime(steam_id steamIDUser, const char* pchName, bool* pbAchieved, + unsigned int* punUnlockTime) + { + return true; + } + + bool user_stats::ResetAllStats(bool bAchievementsToo) + { + return false; + } + + unsigned long long user_stats::FindOrCreateLeaderboard(const char* pchLeaderboardName, int eLeaderboardSortMethod, + int eLeaderboardDisplayType) + { + return 0; + } + + unsigned long long user_stats::FindLeaderboard(const char* pchLeaderboardName) + { + return 0; + } + + const char* user_stats::GetLeaderboardName(unsigned long long hSteamLeaderboard) + { + return ""; + } + + int user_stats::GetLeaderboardEntryCount(unsigned long long hSteamLeaderboard) + { + return 0; + } + + int user_stats::GetLeaderboardSortMethod(unsigned long long hSteamLeaderboard) + { + return 0; + } + + int user_stats::GetLeaderboardDisplayType(unsigned long long hSteamLeaderboard) + { + return 0; + } + + unsigned long long user_stats::DownloadLeaderboardEntries(unsigned long long hSteamLeaderboard, + int eLeaderboardDataRequest, int nRangeStart, + int nRangeEnd) + { + return 0; + } + + unsigned long long user_stats::DownloadLeaderboardEntriesForUsers(unsigned long long hSteamLeaderboard, + steam_id* prgUsers, int cUsers) + { + return 0; + } + + bool user_stats::GetDownloadedLeaderboardEntry(unsigned long long hSteamLeaderboardEntries, int index, + int* pLeaderboardEntry, int* pDetails, int cDetailsMax) + { + return false; + } + + unsigned long long user_stats::UploadLeaderboardScore(unsigned long long hSteamLeaderboard, + int eLeaderboardUploadScoreMethod, int nScore, + const int* pScoreDetails, int cScoreDetailsCount) + { + return 0; + } + + unsigned long long user_stats::AttachLeaderboardUGC(unsigned long long hSteamLeaderboard, unsigned long long hUGC) + { + return 0; + } + + unsigned long long user_stats::GetNumberOfCurrentPlayers() + { + return 0; + } + + unsigned long long user_stats::RequestGlobalAchievementPercentages() + { + return 0; + } + + int user_stats::GetMostAchievedAchievementInfo(char* pchName, unsigned int unNameBufLen, float* pflPercent, + bool* pbAchieved) + { + return 0; + } + + int user_stats::GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, unsigned int unNameBufLen, + float* pflPercent, bool* pbAchieved) + { + return 0; + } + + bool user_stats::GetAchievementAchievedPercent(const char* pchName, float* pflPercent) + { + return true; + } + + unsigned long long user_stats::RequestGlobalStats(int nHistoryDays) + { + return 0; + } + + bool user_stats::GetGlobalStat(const char* pchStatName, long long* pData) + { + return false; + } + + bool user_stats::GetGlobalStat(const char* pchStatName, double* pData) + { + return false; + } + + int user_stats::GetGlobalStatHistory(const char* pchStatName, long long* pData, unsigned int cubData) + { + return 0; + } + + int user_stats::GetGlobalStatHistory(const char* pchStatName, double* pData, unsigned int cubData) + { + return 0; + } +} diff --git a/src/client/steam/interfaces/user_stats.hpp b/src/client/steam/interfaces/user_stats.hpp new file mode 100644 index 00000000..c49c79d4 --- /dev/null +++ b/src/client/steam/interfaces/user_stats.hpp @@ -0,0 +1,65 @@ +#pragma once + +namespace steam +{ + class user_stats + { + public: + ~user_stats() = default; + + virtual bool RequestCurrentStats(); + virtual bool GetStat(const char* pchName, int* pData); + virtual bool GetStat(const char* pchName, float* pData); + virtual bool SetStat(const char* pchName, int nData); + virtual bool SetStat(const char* pchName, float fData); + virtual bool UpdateAvgRateStat(const char* pchName, float flCountThisSession, double dSessionLength); + virtual bool GetAchievement(const char* pchName, bool* pbAchieved); + virtual bool SetAchievement(const char* pchName); + virtual bool ClearAchievement(const char* pchName); + virtual bool GetAchievementAndUnlockTime(const char* pchName, bool* pbAchieved, unsigned int* punUnlockTime); + virtual bool StoreStats(); + virtual int GetAchievementIcon(const char* pchName); + virtual const char* GetAchievementDisplayAttribute(const char* pchName, const char* pchKey); + virtual bool IndicateAchievementProgress(const char* pchName, unsigned int nCurProgress, + unsigned int nMaxProgress); + virtual unsigned int GetNumAchievements(); + virtual const char* GetAchievementName(unsigned int iAchievement); + virtual unsigned long long RequestUserStats(steam_id steamIDUser); + virtual bool GetUserStat(steam_id steamIDUser, const char* pchName, int* pData); + virtual bool GetUserStat(steam_id steamIDUser, const char* pchName, float* pData); + virtual bool GetUserAchievement(steam_id steamIDUser, const char* pchName, bool* pbAchieved); + virtual bool GetUserAchievementAndUnlockTime(steam_id steamIDUser, const char* pchName, bool* pbAchieved, + unsigned int* punUnlockTime); + virtual bool ResetAllStats(bool bAchievementsToo); + virtual unsigned long long FindOrCreateLeaderboard(const char* pchLeaderboardName, int eLeaderboardSortMethod, + int eLeaderboardDisplayType); + virtual unsigned long long FindLeaderboard(const char* pchLeaderboardName); + virtual const char* GetLeaderboardName(unsigned long long hSteamLeaderboard); + virtual int GetLeaderboardEntryCount(unsigned long long hSteamLeaderboard); + virtual int GetLeaderboardSortMethod(unsigned long long hSteamLeaderboard); + virtual int GetLeaderboardDisplayType(unsigned long long hSteamLeaderboard); + virtual unsigned long long DownloadLeaderboardEntries(unsigned long long hSteamLeaderboard, + int eLeaderboardDataRequest, int nRangeStart, + int nRangeEnd); + virtual unsigned long long DownloadLeaderboardEntriesForUsers(unsigned long long hSteamLeaderboard, + steam_id* prgUsers, int cUsers); + virtual bool GetDownloadedLeaderboardEntry(unsigned long long hSteamLeaderboardEntries, int index, + int* pLeaderboardEntry, int* pDetails, int cDetailsMax); + virtual unsigned long long UploadLeaderboardScore(unsigned long long hSteamLeaderboard, + int eLeaderboardUploadScoreMethod, int nScore, + const int* pScoreDetails, int cScoreDetailsCount); + virtual unsigned long long AttachLeaderboardUGC(unsigned long long hSteamLeaderboard, unsigned long long hUGC); + virtual unsigned long long GetNumberOfCurrentPlayers(); + virtual unsigned long long RequestGlobalAchievementPercentages(); + virtual int GetMostAchievedAchievementInfo(char* pchName, unsigned int unNameBufLen, float* pflPercent, + bool* pbAchieved); + virtual int GetNextMostAchievedAchievementInfo(int iIteratorPrevious, char* pchName, unsigned int unNameBufLen, + float* pflPercent, bool* pbAchieved); + virtual bool GetAchievementAchievedPercent(const char* pchName, float* pflPercent); + virtual unsigned long long RequestGlobalStats(int nHistoryDays); + virtual bool GetGlobalStat(const char* pchStatName, long long* pData); + virtual bool GetGlobalStat(const char* pchStatName, double* pData); + virtual int GetGlobalStatHistory(const char* pchStatName, long long* pData, unsigned int cubData); + virtual int GetGlobalStatHistory(const char* pchStatName, double* pData, unsigned int cubData); + }; +} diff --git a/src/client/steam/interfaces/utils.cpp b/src/client/steam/interfaces/utils.cpp new file mode 100644 index 00000000..69f3f823 --- /dev/null +++ b/src/client/steam/interfaces/utils.cpp @@ -0,0 +1,146 @@ +#include +#include "../steam.hpp" + +namespace steam +{ + unsigned int utils::GetSecondsSinceAppActive() + { + return 0; + } + + unsigned int utils::GetSecondsSinceComputerActive() + { + return (uint32_t)GetTickCount64() / 1000; + } + + int utils::GetConnectedUniverse() + { + return 1; + } + + unsigned int utils::GetServerRealTime() + { + return (uint32_t)time(NULL); + } + + const char* utils::GetIPCountry() + { + return "US"; + } + + bool utils::GetImageSize(int iImage, unsigned int* pnWidth, unsigned int* pnHeight) + { + return false; + } + + bool utils::GetImageRGBA(int iImage, unsigned char* pubDest, int nDestBufferSize) + { + return false; + } + + bool utils::GetCSERIPPort(unsigned int* unIP, unsigned short* usPort) + { + return false; + } + + unsigned char utils::GetCurrentBatteryPower() + { + return 255; + } + + unsigned int utils::GetAppID() + { + return 209660; + } + + void utils::SetOverlayNotificationPosition(int eNotificationPosition) + { + //const auto& overlay = steam_proxy::get_overlay_module(); + //if (overlay) + //{ + // overlay.invoke("SetNotificationPosition", eNotificationPosition); + //} + } + + bool utils::IsAPICallCompleted(unsigned long long hSteamAPICall, bool* pbFailed) + { + return false; + } + + int utils::GetAPICallFailureReason(unsigned long long hSteamAPICall) + { + return -1; + } + + bool utils::GetAPICallResult(unsigned long long hSteamAPICall, void* pCallback, int cubCallback, + int iCallbackExpected, bool* pbFailed) + { + return false; + } + + void utils::RunFrame() + { + } + + unsigned int utils::GetIPCCallCount() + { + return 0; + } + + void utils::SetWarningMessageHook(void (*pFunction)(int hpipe, const char* message)) + { + } + + bool utils::IsOverlayEnabled() + { + return false; + } + + bool utils::BOverlayNeedsPresent() + { + return false; + } + + unsigned long long utils::CheckFileSignature(const char* szFileName) + { + return 0; + } + + bool utils::ShowGamepadTextInput(int eInputMode, int eInputLineMode, const char* szText, unsigned int uMaxLength) + { + return false; + } + + unsigned int utils::GetEnteredGamepadTextLength() + { + return 0; + } + + bool utils::GetEnteredGamepadTextInput(char* pchValue, unsigned int cchValueMax) + { + return false; + } + + const char* utils::GetSteamUILanguage() + { + return "english"; + } + bool utils::IsSteamRunningInVR() + { + return false; + } + + void utils::SetOverlayNotificationInset(int nHorizontalInset, int nVerticalInset) + { + + } + bool utils::IsSteamInBigPictureMode() + { + return false; + } + + void utils::StartVRDashboard() + { + + } +} diff --git a/src/client/steam/interfaces/utils.hpp b/src/client/steam/interfaces/utils.hpp new file mode 100644 index 00000000..7956e331 --- /dev/null +++ b/src/client/steam/interfaces/utils.hpp @@ -0,0 +1,43 @@ +#pragma once + +namespace steam +{ + class utils + { + public: + ~utils() = default; + + virtual unsigned int GetSecondsSinceAppActive(); + virtual unsigned int GetSecondsSinceComputerActive(); + virtual int GetConnectedUniverse(); + virtual unsigned int GetServerRealTime(); + virtual const char* GetIPCountry(); + virtual bool GetImageSize(int iImage, unsigned int* pnWidth, unsigned int* pnHeight); + virtual bool GetImageRGBA(int iImage, unsigned char* pubDest, int nDestBufferSize); + virtual bool GetCSERIPPort(unsigned int* unIP, unsigned short* usPort); + virtual unsigned char GetCurrentBatteryPower(); + virtual unsigned int GetAppID(); + virtual void SetOverlayNotificationPosition(int eNotificationPosition); + virtual bool IsAPICallCompleted(unsigned long long hSteamAPICall, bool* pbFailed); + virtual int GetAPICallFailureReason(unsigned long long hSteamAPICall); + virtual bool GetAPICallResult(unsigned long long hSteamAPICall, void* pCallback, int cubCallback, + int iCallbackExpected, bool* pbFailed); + virtual void RunFrame(); + virtual unsigned int GetIPCCallCount(); + virtual void SetWarningMessageHook(void (*pFunction)(int hpipe, const char* message)); + virtual bool IsOverlayEnabled(); + virtual bool BOverlayNeedsPresent(); + virtual unsigned long long CheckFileSignature(const char* szFileName); + + virtual bool ShowGamepadTextInput(int eInputMode, int eInputLineMode, const char* szText, + unsigned int uMaxLength); + virtual unsigned int GetEnteredGamepadTextLength(); + virtual bool GetEnteredGamepadTextInput(char* pchValue, unsigned int cchValueMax); + + virtual const char* GetSteamUILanguage(); + virtual bool IsSteamRunningInVR(); + virtual void SetOverlayNotificationInset(int nHorizontalInset, int nVerticalInset); + virtual bool IsSteamInBigPictureMode(); + virtual void StartVRDashboard(); + }; +} diff --git a/src/client/steam/steam.cpp b/src/client/steam/steam.cpp new file mode 100644 index 00000000..7c99955e --- /dev/null +++ b/src/client/steam/steam.cpp @@ -0,0 +1,310 @@ +#include +#include "steam.hpp" + +#include + +namespace steam +{ + uint64_t callbacks::call_id_ = 0; + std::recursive_mutex callbacks::mutex_; + std::map callbacks::calls_; + std::map callbacks::result_handlers_; + std::vector callbacks::results_; + std::vector callbacks::callback_list_; + + uint64_t callbacks::register_call() + { + std::lock_guard _(mutex_); + calls_[++call_id_] = false; + return call_id_; + } + + void callbacks::register_callback(base* handler, const int callback) + { + std::lock_guard _(mutex_); + handler->set_i_callback(callback); + callback_list_.push_back(handler); + } + + void callbacks::unregister_callback(base* handler) + { + std::lock_guard _(mutex_); + for (auto i = callback_list_.begin(); i != callback_list_.end();) + { + if (*i == handler) + { + i = callback_list_.erase(i); + } + else + { + ++i; + } + } + } + + void callbacks::register_call_result(const uint64_t call, base* result) + { + std::lock_guard _(mutex_); + result_handlers_[call] = result; + } + + void callbacks::unregister_call_result(const uint64_t call, base* /*result*/) + { + std::lock_guard _(mutex_); + const auto i = result_handlers_.find(call); + if (i != result_handlers_.end()) + { + result_handlers_.erase(i); + } + } + + void callbacks::return_call(void* data, const int size, const int type, const uint64_t call) + { + std::lock_guard _(mutex_); + + result result{}; + result.call = call; + result.data = data; + result.size = size; + result.type = type; + + calls_[call] = true; + + results_.emplace_back(result); + } + + void callbacks::run_callbacks() + { + std::lock_guard _(mutex_); + + for (const auto& result : results_) + { + if (result_handlers_.find(result.call) != result_handlers_.end()) + { + result_handlers_[result.call]->run(result.data, false, result.call); + } + + for (const auto& callback : callback_list_) + { + if (callback && callback->get_i_callback() == result.type) + { + callback->run(result.data, false, 0); + } + } + + if (result.data) + { + free(result.data); + } + } + + results_.clear(); + } + + extern "C" { + + bool SteamAPI_RestartAppIfNecessary() + { + OutputDebugStringA(__FUNCTION__); + return false; + } + + bool SteamAPI_Init() + { + OutputDebugStringA(__FUNCTION__); + const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath(); + if (steam_path.empty()) return true; + + ::utils::nt::library::load(steam_path / "tier0_s64.dll"); + ::utils::nt::library::load(steam_path / "vstdlib_s64.dll"); + ::utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll"); + ::utils::nt::library::load(steam_path / "steamclient64.dll"); + return true; + } + + void SteamAPI_RegisterCallResult(callbacks::base* result, const uint64_t call) + { + OutputDebugStringA(__FUNCTION__); + callbacks::register_call_result(call, result); + } + + void SteamAPI_RegisterCallback(callbacks::base* handler, const int callback) + { + OutputDebugStringA(__FUNCTION__); + callbacks::register_callback(handler, callback); + } + + void SteamAPI_RunCallbacks() + { + OutputDebugStringA(__FUNCTION__); + callbacks::run_callbacks(); + } + + void SteamAPI_Shutdown() + { + OutputDebugStringA(__FUNCTION__); + } + + void SteamAPI_UnregisterCallResult(callbacks::base* result, const uint64_t call) + { + OutputDebugStringA(__FUNCTION__); + callbacks::unregister_call_result(call, result); + } + + void SteamAPI_UnregisterCallback(callbacks::base* handler) + { + OutputDebugStringA(__FUNCTION__); + callbacks::unregister_callback(handler); + } + + const char* SteamAPI_GetSteamInstallPath() + { + OutputDebugStringA(__FUNCTION__); + static std::string install_path{}; + if (!install_path.empty()) + { + return install_path.data(); + } + + HKEY reg_key; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\WOW6432Node\\Valve\\Steam", 0, KEY_QUERY_VALUE, + ®_key) == + ERROR_SUCCESS) + { + char path[MAX_PATH] = {0}; + DWORD length = sizeof(path); + RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast(path), + &length); + RegCloseKey(reg_key); + + install_path = path; + } + + return install_path.data(); + } + void* SteamGameServer_GetHSteamPipe() + { + OutputDebugStringA(__FUNCTION__); + return (void*)1; + } + +void* SteamGameServer_GetHSteamUser() + { + OutputDebugStringA(__FUNCTION__); + return (void*)1; + } + +void* SteamAPI_GetHSteamUser() +{ + OutputDebugStringA(__FUNCTION__); + return (void*)1; +} + +void* SteamAPI_GetHSteamPipe() +{ + OutputDebugStringA(__FUNCTION__); + return (void*)1; +} + +void* SteamInternal_CreateInterface(const char* interfacename) + { + OutputDebugStringA(__FUNCTION__); + OutputDebugStringA(interfacename); + if(std::string(interfacename) == "SteamClient017") { + static client c; + return &c; + } + + + MessageBoxA(0, interfacename, __FUNCTION__, 0); + return nullptr;//::utils::nt::library("steam_api64.dll").invoke("SteamInternal_CreateInterface", interfacename); + } + +bool SteamInternal_GameServer_Init() + { + OutputDebugStringA(__FUNCTION__); + return true; + } + + bool SteamGameServer_Init() + { + OutputDebugStringA(__FUNCTION__); + return true; + } + + void SteamGameServer_RunCallbacks() + { + OutputDebugStringA(__FUNCTION__); + } + + void SteamGameServer_Shutdown() + { + OutputDebugStringA(__FUNCTION__); + } + + + friends* SteamFriends() + { + OutputDebugStringA(__FUNCTION__); + static friends friends; + return &friends; + } + + matchmaking* SteamMatchmaking() + { + OutputDebugStringA(__FUNCTION__); + static matchmaking matchmaking; + return &matchmaking; + } + + game_server* SteamGameServer() + { + OutputDebugStringA(__FUNCTION__); + static game_server game_server; + return &game_server; + } + + networking* SteamNetworking() + { + OutputDebugStringA(__FUNCTION__); + static networking networking; + return &networking; + } + + remote_storage* SteamRemoteStorage() + { + OutputDebugStringA(__FUNCTION__); + static remote_storage remote_storage; + return &remote_storage; + } + + user* SteamUser() + { + OutputDebugStringA(__FUNCTION__); + static user user; + return &user; + } + + utils* SteamUtils() + { + OutputDebugStringA(__FUNCTION__); + static utils utils; + return &utils; + } + + apps* SteamApps() + { + OutputDebugStringA(__FUNCTION__); + static apps apps; + return &apps; + } + + user_stats* SteamUserStats() + { + OutputDebugStringA(__FUNCTION__); + static user_stats user_stats; + return &user_stats; + } + + } +} diff --git a/src/client/steam/steam.hpp b/src/client/steam/steam.hpp new file mode 100644 index 00000000..8a0f8958 --- /dev/null +++ b/src/client/steam/steam.hpp @@ -0,0 +1,137 @@ +#pragma once + +#define STEAM_EXPORT extern "C" __declspec(dllexport) + +struct raw_steam_id final +{ + unsigned int account_id : 32; + unsigned int account_instance : 20; + unsigned int account_type : 4; + int universe : 8; +}; + +typedef union +{ + raw_steam_id raw; + unsigned long long bits; +} steam_id; + +#pragma pack( push, 1 ) +struct raw_game_id final +{ + unsigned int app_id : 24; + unsigned int type : 8; + unsigned int mod_id : 32; +}; + +typedef union +{ + raw_game_id raw; + unsigned long long bits; +} game_id; +#pragma pack( pop ) + +#include "interfaces/apps.hpp" +#include "interfaces/user.hpp" +#include "interfaces/utils.hpp" +#include "interfaces/friends.hpp" +#include "interfaces/user_stats.hpp" +#include "interfaces/game_server.hpp" +#include "interfaces/networking.hpp" +#include "interfaces/matchmaking.hpp" +#include "interfaces/matchmaking_servers.hpp" +#include "interfaces/remote_storage.hpp" +#include "interfaces/screenshots.hpp" +#include "interfaces/game_server_stats.hpp" +#include "interfaces/http.hpp" +#include "interfaces/unified_messages.hpp" +#include "interfaces/controller.hpp" +#include "interfaces/client.hpp" + +namespace steam +{ + class callbacks + { + public: + class base + { + public: + base() : flags_(0), callback_(0) + { + } + + virtual void run(void* pv_param) = 0; + virtual void run(void* pv_param, bool failure, uint64_t handle) = 0; + virtual int get_callback_size_bytes() = 0; + + int get_i_callback() const { return callback_; } + void set_i_callback(const int i_callback) { callback_ = i_callback; } + + protected: + ~base() = default; + + unsigned char flags_; + int callback_; + }; + + struct result final + { + void* data{}; + int size{}; + int type{}; + uint64_t call{}; + }; + + static uint64_t register_call(); + + static void register_callback(base* handler, int callback); + static void unregister_callback(base* handler); + + static void register_call_result(uint64_t call, base* result); + static void unregister_call_result(uint64_t call, base* result); + + static void return_call(void* data, int size, int type, uint64_t call); + static void run_callbacks(); + + private: + static uint64_t call_id_; + static std::recursive_mutex mutex_; + static std::map calls_; + static std::map result_handlers_; + static std::vector results_; + static std::vector callback_list_; + }; + + STEAM_EXPORT bool SteamAPI_RestartAppIfNecessary(); + STEAM_EXPORT bool SteamAPI_Init(); + STEAM_EXPORT void SteamAPI_RegisterCallResult(callbacks::base* result, uint64_t call); + STEAM_EXPORT void SteamAPI_RegisterCallback(callbacks::base* handler, int callback); + STEAM_EXPORT void SteamAPI_RunCallbacks(); + STEAM_EXPORT void SteamAPI_Shutdown(); + STEAM_EXPORT void SteamAPI_UnregisterCallResult(callbacks::base* result, const uint64_t call); + STEAM_EXPORT void SteamAPI_UnregisterCallback(callbacks::base* handler); + STEAM_EXPORT const char* SteamAPI_GetSteamInstallPath(); + + STEAM_EXPORT void* SteamGameServer_GetHSteamPipe(); + STEAM_EXPORT void* SteamGameServer_GetHSteamUser(); + + STEAM_EXPORT void* SteamAPI_GetHSteamUser(); + STEAM_EXPORT void* SteamAPI_GetHSteamPipe(); + + STEAM_EXPORT void* SteamInternal_CreateInterface(const char* interfacename); + + STEAM_EXPORT bool SteamInternal_GameServer_Init(); + STEAM_EXPORT bool SteamGameServer_Init(); + STEAM_EXPORT void SteamGameServer_RunCallbacks(); + STEAM_EXPORT void SteamGameServer_Shutdown(); + + STEAM_EXPORT friends* SteamFriends(); + STEAM_EXPORT matchmaking* SteamMatchmaking(); + STEAM_EXPORT game_server* SteamGameServer(); + STEAM_EXPORT networking* SteamNetworking(); + STEAM_EXPORT remote_storage* SteamRemoteStorage(); + STEAM_EXPORT user* SteamUser(); + STEAM_EXPORT utils* SteamUtils(); + STEAM_EXPORT apps* SteamApps(); + STEAM_EXPORT user_stats* SteamUserStats(); +} diff --git a/src/common/utils/binary_resource.cpp b/src/common/utils/binary_resource.cpp new file mode 100644 index 00000000..eed83b0d --- /dev/null +++ b/src/common/utils/binary_resource.cpp @@ -0,0 +1,75 @@ +#include "binary_resource.hpp" + +#include +#include "nt.hpp" +#include "io.hpp" + +namespace utils +{ + namespace + { + std::string get_temp_folder() + { + char path[MAX_PATH] = {0}; + if (!GetTempPathA(sizeof(path), path)) + { + throw std::runtime_error("Unable to get temp path"); + } + + return path; + } + + std::string write_existing_temp_file(const std::string& file, const std::string& data, + const bool fatal_if_overwrite_fails) + { + const auto temp = get_temp_folder(); + auto file_path = temp + file; + + std::string current_data; + if (!io::read_file(file_path, ¤t_data)) + { + if (!io::write_file(file_path, data)) + { + throw std::runtime_error("Failed to write file: " + file_path); + } + + return file_path; + } + + if (current_data == data || io::write_file(file_path, data) || !fatal_if_overwrite_fails) + { + return file_path; + } + + throw std::runtime_error( + "Temporary file was already written, but differs. It can't be overwritten as it's still in use: " + + file_path); + } + } + + binary_resource::binary_resource(const int id, std::string file) + : filename_(std::move(file)) + { + this->resource_ = nt::load_resource(id); + + if (this->resource_.empty()) + { + throw std::runtime_error("Unable to load resource: " + std::to_string(id)); + } + } + + std::string binary_resource::get_extracted_file(const bool fatal_if_overwrite_fails) + { + if (this->path_.empty()) + { + this->path_ = write_existing_temp_file(this->filename_, this->resource_, fatal_if_overwrite_fails); + } + + return this->path_; + } + + const std::string& binary_resource::get_data() const + { + return this->resource_; + } +} diff --git a/src/common/utils/binary_resource.hpp b/src/common/utils/binary_resource.hpp new file mode 100644 index 00000000..da19af1a --- /dev/null +++ b/src/common/utils/binary_resource.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace utils +{ + class binary_resource + { + public: + binary_resource(int id, std::string file); + + std::string get_extracted_file(bool fatal_if_overwrite_fails = false); + const std::string& get_data() const; + + private: + std::string resource_; + std::string filename_; + std::string path_; + }; +} diff --git a/src/common/utils/concurrency.hpp b/src/common/utils/concurrency.hpp new file mode 100644 index 00000000..05c5d3ad --- /dev/null +++ b/src/common/utils/concurrency.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace utils::concurrency +{ + template + class container + { + public: + template + R access(F&& accessor) const + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access(F&& accessor) + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access_with_lock(F&& accessor) const + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + template + R access_with_lock(F&& accessor) + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + T& get_raw() { return object_; } + const T& get_raw() const { return object_; } + + private: + mutable MutexType mutex_{}; + T object_{}; + }; +} diff --git a/src/common/utils/finally.hpp b/src/common/utils/finally.hpp new file mode 100644 index 00000000..2e4b6bad --- /dev/null +++ b/src/common/utils/finally.hpp @@ -0,0 +1,54 @@ +#pragma once +#include + +namespace utils +{ + /* + * Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57 + */ + + template + class final_action + { + public: + static_assert(!std::is_reference::value && !std::is_const::value && + !std::is_volatile::value, + "Final_action should store its callable by value"); + + explicit final_action(F f) noexcept : f_(std::move(f)) + { + } + + final_action(final_action&& other) noexcept + : f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false)) + { + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; + final_action& operator=(final_action&&) = delete; + + ~final_action() noexcept + { + if (invoke_) f_(); + } + + // Added by momo5502 + void cancel() + { + invoke_ = false; + } + + private: + F f_; + bool invoke_{true}; + }; + + template + final_action::type>::type> + finally(F&& f) noexcept + { + return final_action::type>::type>( + std::forward(f)); + } +} \ No newline at end of file diff --git a/src/common/utils/flags.cpp b/src/common/utils/flags.cpp new file mode 100644 index 00000000..09f13114 --- /dev/null +++ b/src/common/utils/flags.cpp @@ -0,0 +1,53 @@ +#include "flags.hpp" +#include "string.hpp" +#include "nt.hpp" + +#include + +namespace utils::flags +{ + void parse_flags(std::vector& flags) + { + int num_args; + auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args); + + flags.clear(); + + if (argv) + { + for (auto i = 0; i < num_args; ++i) + { + std::wstring wide_flag(argv[i]); + if (wide_flag[0] == L'-') + { + wide_flag.erase(wide_flag.begin()); + flags.emplace_back(string::convert(wide_flag)); + } + } + + LocalFree(argv); + } + } + + bool has_flag(const std::string& flag) + { + static auto parsed = false; + static std::vector enabled_flags; + + if (!parsed) + { + parse_flags(enabled_flags); + parsed = true; + } + + for (const auto& entry : enabled_flags) + { + if (string::to_lower(entry) == string::to_lower(flag)) + { + return true; + } + } + + return false; + } +} diff --git a/src/common/utils/flags.hpp b/src/common/utils/flags.hpp new file mode 100644 index 00000000..cf304b20 --- /dev/null +++ b/src/common/utils/flags.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace utils::flags +{ + bool has_flag(const std::string& flag); +} diff --git a/src/common/utils/hook.cpp b/src/common/utils/hook.cpp new file mode 100644 index 00000000..be05cb96 --- /dev/null +++ b/src/common/utils/hook.cpp @@ -0,0 +1,310 @@ +#include "hook.hpp" +#include "string.hpp" + +#include + +namespace utils::hook +{ + namespace + { + [[maybe_unused]] class _ + { + public: + _() + { + if (MH_Initialize() != MH_OK) + { + throw std::runtime_error("Failed to initialize MinHook"); + } + } + + ~_() + { + MH_Uninitialize(); + } + } __; + } + + void assembler::pushad64() + { + this->push(rax); + this->push(rcx); + this->push(rdx); + this->push(rbx); + this->push(rsp); + this->push(rbp); + this->push(rsi); + this->push(rdi); + + this->sub(rsp, 0x40); + } + + void assembler::popad64() + { + this->add(rsp, 0x40); + + this->pop(rdi); + this->pop(rsi); + this->pop(rbp); + this->pop(rsp); + this->pop(rbx); + this->pop(rdx); + this->pop(rcx); + this->pop(rax); + } + + void assembler::prepare_stack_for_call() + { + const auto reserve_callee_space = this->newLabel(); + const auto stack_unaligned = this->newLabel(); + + this->test(rsp, 0xF); + this->jnz(stack_unaligned); + + this->sub(rsp, 0x8); + this->push(rsp); + + this->push(rax); + this->mov(rax, ptr(rsp, 8, 8)); + this->add(rax, 0x8); + this->mov(ptr(rsp, 8, 8), rax); + this->pop(rax); + + this->jmp(reserve_callee_space); + + this->bind(stack_unaligned); + this->push(rsp); + + this->bind(reserve_callee_space); + this->sub(rsp, 0x40); + } + + void assembler::restore_stack_after_call() + { + this->lea(rsp, ptr(rsp, 0x40)); + this->pop(rsp); + } + + asmjit::Error assembler::call(void* target) + { + return Assembler::call(size_t(target)); + } + + asmjit::Error assembler::jmp(void* target) + { + return Assembler::jmp(size_t(target)); + } + + detour::detour(const size_t place, void* target) : detour(reinterpret_cast(place), target) + { + } + + detour::detour(void* place, void* target) + { + this->create(place, target); + } + + detour::~detour() + { + this->clear(); + } + + void detour::enable() const + { + MH_EnableHook(this->place_); + } + + void detour::disable() const + { + MH_DisableHook(this->place_); + } + + void detour::create(void* place, void* target) + { + this->clear(); + this->place_ = place; + + if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) + { + throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_)); + } + + this->enable(); + } + + void detour::create(const size_t place, void* target) + { + this->create(reinterpret_cast(place), target); + } + + void detour::clear() + { + if (this->place_) + { + MH_RemoveHook(this->place_); + } + + this->place_ = nullptr; + this->original_ = nullptr; + } + + void* detour::get_original() const + { + return this->original_; + } + + bool iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub) + { + if (!library.is_valid()) return false; + + auto* const ptr = library.get_iat_entry(target_library, process); + if (!ptr) return false; + + DWORD protect; + VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect); + + *ptr = stub; + + VirtualProtect(ptr, sizeof(*ptr), protect, &protect); + return true; + } + + void nop(void* place, const size_t length) + { + DWORD old_protect{}; + VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); + + std::memset(place, 0x90, length); + + VirtualProtect(place, length, old_protect, &old_protect); + FlushInstructionCache(GetCurrentProcess(), place, length); + } + + void nop(const size_t place, const size_t length) + { + nop(reinterpret_cast(place), length); + } + + void copy(void* place, const void* data, const size_t length) + { + DWORD old_protect{}; + VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect); + + std::memmove(place, data, length); + + VirtualProtect(place, length, old_protect, &old_protect); + FlushInstructionCache(GetCurrentProcess(), place, length); + } + + void copy(const size_t place, const void* data, const size_t length) + { + copy(reinterpret_cast(place), data, length); + } + + bool is_relatively_far(const void* pointer, const void* data, const int offset) + { + const int64_t diff = size_t(data) - (size_t(pointer) + offset); + const auto small_diff = int32_t(diff); + return diff != int64_t(small_diff); + } + + void call(void* pointer, void* data) + { + if (is_relatively_far(pointer, data)) + { + throw std::runtime_error("Too far away to create 32bit relative branch"); + } + + auto* patch_pointer = PBYTE(pointer); + set(patch_pointer, 0xE8); + set(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); + } + + void call(const size_t pointer, void* data) + { + return call(reinterpret_cast(pointer), data); + } + + void call(const size_t pointer, const size_t data) + { + return call(pointer, reinterpret_cast(data)); + } + + void jump(void* pointer, void* data, const bool use_far) + { + static const unsigned char jump_data[] = { + 0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xe0 + }; + + if (!use_far && is_relatively_far(pointer, data)) + { + throw std::runtime_error("Too far away to create 32bit relative branch"); + } + + auto* patch_pointer = PBYTE(pointer); + + if (use_far) + { + copy(patch_pointer, jump_data, sizeof(jump_data)); + copy(patch_pointer + 2, &data, sizeof(data)); + } + else + { + set(patch_pointer, 0xE9); + set(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5))); + } + } + + void jump(const size_t pointer, void* data, const bool use_far) + { + return jump(reinterpret_cast(pointer), data, use_far); + } + + void jump(const size_t pointer, const size_t data, const bool use_far) + { + return jump(pointer, reinterpret_cast(data), use_far); + } + + void* assemble(const std::function& asm_function) + { + static asmjit::JitRuntime runtime; + + asmjit::CodeHolder code; + code.init(runtime.environment()); + + assembler a(&code); + + asm_function(a); + + void* result = nullptr; + runtime.add(&result, &code); + + return result; + } + + void inject(void* pointer, const void* data) + { + if (is_relatively_far(pointer, data, 4)) + { + throw std::runtime_error("Too far away to create 32bit relative branch"); + } + + set(pointer, int32_t(size_t(data) - (size_t(pointer) + 4))); + } + + void inject(const size_t pointer, const void* data) + { + return inject(reinterpret_cast(pointer), data); + } + + void* follow_branch(void* address) + { + auto* const data = static_cast(address); + if (*data != 0xE8 && *data != 0xE9) + { + throw std::runtime_error("No branch instruction found"); + } + + return extract(data + 1); + } +} diff --git a/src/common/utils/hook.hpp b/src/common/utils/hook.hpp new file mode 100644 index 00000000..bb24f8ce --- /dev/null +++ b/src/common/utils/hook.hpp @@ -0,0 +1,205 @@ +#pragma once +#include "signature.hpp" + +#include +#include + +using namespace asmjit::x86; + +namespace utils::hook +{ + namespace detail + { + template + std::vector get_iota_functions() + { + if constexpr (entries == 0) + { + std::vector functions; + return functions; + } + else + { + auto functions = get_iota_functions(); + functions.emplace_back([]() + { + return entries - 1; + }); + return functions; + } + } + } + + // Gets the pointer to the entry in the v-table. + // It seems otherwise impossible to get this. + // This is ugly as fuck and only safely works on x64 + // Example: + // ID3D11Device* device = ... + // auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D); + template + void** get_vtable_entry(Class* obj, T (Class::* entry)(Args ...)) + { + union + { + decltype(entry) func; + void* pointer; + }; + + func = entry; + + auto iota_functions = detail::get_iota_functions(); + auto* object = iota_functions.data(); + + using FakeFunc = size_t(__thiscall*)(void* self); + auto index = static_cast(pointer)(&object); + + void** obj_v_table = *reinterpret_cast(obj); + return &obj_v_table[index]; + } + + class assembler : public Assembler + { + public: + using Assembler::Assembler; + using Assembler::call; + using Assembler::jmp; + + void pushad64(); + void popad64(); + + void prepare_stack_for_call(); + void restore_stack_after_call(); + + template + void call_aligned(T&& target) + { + this->prepare_stack_for_call(); + this->call(std::forward(target)); + this->restore_stack_after_call(); + } + + asmjit::Error call(void* target); + asmjit::Error jmp(void* target); + }; + + class detour + { + public: + detour() = default; + detour(void* place, void* target); + detour(size_t place, void* target); + ~detour(); + + detour(detour&& other) noexcept + { + this->operator=(std::move(other)); + } + + detour& operator=(detour&& other) noexcept + { + if (this != &other) + { + this->~detour(); + + this->place_ = other.place_; + this->original_ = other.original_; + + other.place_ = nullptr; + other.original_ = nullptr; + } + + return *this; + } + + detour(const detour&) = delete; + detour& operator=(const detour&) = delete; + + void enable() const; + void disable() const; + + void create(void* place, void* target); + void create(size_t place, void* target); + void clear(); + + template + T* get() const + { + return static_cast(this->get_original()); + } + + template + T invoke(Args ... args) + { + return static_cast(this->get_original())(args...); + } + + [[nodiscard]] void* get_original() const; + + private: + void* place_{}; + void* original_{}; + }; + + bool iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub); + + void nop(void* place, size_t length); + void nop(size_t place, size_t length); + + void copy(void* place, const void* data, size_t length); + void copy(size_t place, const void* data, size_t length); + + bool is_relatively_far(const void* pointer, const void* data, int offset = 5); + + void call(void* pointer, void* data); + void call(size_t pointer, void* data); + void call(size_t pointer, size_t data); + + void jump(void* pointer, void* data, bool use_far = false); + void jump(size_t pointer, void* data, bool use_far = false); + void jump(size_t pointer, size_t data, bool use_far = false); + + void* assemble(const std::function& asm_function); + + void inject(void* pointer, const void* data); + void inject(size_t pointer, const void* data); + + template + T extract(void* address) + { + auto* const data = static_cast(address); + const auto offset = *reinterpret_cast(data); + return reinterpret_cast(data + offset + 4); + } + + void* follow_branch(void* address); + + template + static void set(void* place, T value) + { + DWORD old_protect; + VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect); + + *static_cast(place) = value; + + VirtualProtect(place, sizeof(T), old_protect, &old_protect); + FlushInstructionCache(GetCurrentProcess(), place, sizeof(T)); + } + + template + static void set(const size_t place, T value) + { + return set(reinterpret_cast(place), value); + } + + template + static T invoke(size_t func, Args ... args) + { + return reinterpret_cast(func)(args...); + } + + template + static T invoke(void* func, Args ... args) + { + return static_cast(func)(args...); + } +} diff --git a/src/common/utils/http.cpp b/src/common/utils/http.cpp new file mode 100644 index 00000000..3cb59991 --- /dev/null +++ b/src/common/utils/http.cpp @@ -0,0 +1,48 @@ +#include "http.hpp" +#include "nt.hpp" +#include + +namespace utils::http +{ + std::optional get_data(const std::string& url) + { + CComPtr stream; + + if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr))) + { + return {}; + } + + char buffer[0x1000]; + std::string result; + + HRESULT status{}; + + do + { + DWORD bytes_read = 0; + status = stream->Read(buffer, sizeof(buffer), &bytes_read); + + if (bytes_read > 0) + { + result.append(buffer, bytes_read); + } + } + while (SUCCEEDED(status) && status != S_FALSE); + + if (FAILED(status)) + { + return {}; + } + + return {result}; + } + + std::future> get_data_async(const std::string& url) + { + return std::async(std::launch::async, [url]() + { + return get_data(url); + }); + } +} diff --git a/src/common/utils/http.hpp b/src/common/utils/http.hpp new file mode 100644 index 00000000..65628a9f --- /dev/null +++ b/src/common/utils/http.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +namespace utils::http +{ + std::optional get_data(const std::string& url); + std::future> get_data_async(const std::string& url); +} diff --git a/src/common/utils/info_string.cpp b/src/common/utils/info_string.cpp new file mode 100644 index 00000000..3b0287e3 --- /dev/null +++ b/src/common/utils/info_string.cpp @@ -0,0 +1,65 @@ +#include "info_string.hpp" +#include "string.hpp" + +namespace utils +{ + info_string::info_string(const std::string& buffer) + { + this->parse(buffer); + } + + info_string::info_string(const std::string_view& buffer) + : info_string(std::string{buffer}) + { + } + + void info_string::set(const std::string& key, const std::string& value) + { + this->key_value_pairs_[key] = value; + } + + std::string info_string::get(const std::string& key) const + { + const auto value = this->key_value_pairs_.find(key); + if (value != this->key_value_pairs_.end()) + { + return value->second; + } + + return ""; + } + + void info_string::parse(std::string buffer) + { + if (buffer[0] == '\\') + { + buffer = buffer.substr(1); + } + + auto key_values = string::split(buffer, '\\'); + for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1); i += 2) + { + const auto& key = key_values[i]; + const auto& value = key_values[i + 1]; + this->key_value_pairs_[key] = value; + } + } + + std::string info_string::build() const + { + //auto first = true; + std::string info_string; + for (auto i = this->key_value_pairs_.begin(); i != this->key_value_pairs_.end(); ++i) + { + //if (first) first = false; + /*else*/ + info_string.append("\\"); + + info_string.append(i->first); // Key + info_string.append("\\"); + info_string.append(i->second); // Value + } + + return info_string; + } +} diff --git a/src/common/utils/info_string.hpp b/src/common/utils/info_string.hpp new file mode 100644 index 00000000..73910411 --- /dev/null +++ b/src/common/utils/info_string.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace utils +{ + class info_string + { + public: + info_string() = default; + info_string(const std::string& buffer); + info_string(const std::string_view& buffer); + + void set(const std::string& key, const std::string& value); + std::string get(const std::string& key) const; + std::string build() const; + + private: + std::unordered_map key_value_pairs_{}; + + void parse(std::string buffer); + }; +} diff --git a/src/common/utils/io.cpp b/src/common/utils/io.cpp new file mode 100644 index 00000000..4968f449 --- /dev/null +++ b/src/common/utils/io.cpp @@ -0,0 +1,125 @@ +#include "io.hpp" +#include "nt.hpp" +#include + +namespace utils::io +{ + bool remove_file(const std::string& file) + { + return DeleteFileA(file.data()) == TRUE; + } + + bool move_file(const std::string& src, const std::string& target) + { + return MoveFileA(src.data(), target.data()) == TRUE; + } + + bool file_exists(const std::string& file) + { + return std::ifstream(file).good(); + } + + bool write_file(const std::string& file, const std::string& data, const bool append) + { + const auto pos = file.find_last_of("/\\"); + if (pos != std::string::npos) + { + create_directory(file.substr(0, pos)); + } + + std::ofstream stream( + file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : 0)); + + if (stream.is_open()) + { + stream.write(data.data(), data.size()); + stream.close(); + return true; + } + + return false; + } + + std::string read_file(const std::string& file) + { + std::string data; + read_file(file, &data); + return data; + } + + bool read_file(const std::string& file, std::string* data) + { + if (!data) return false; + data->clear(); + + if (file_exists(file)) + { + std::ifstream stream(file, std::ios::binary); + if (!stream.is_open()) return false; + + stream.seekg(0, std::ios::end); + const std::streamsize size = stream.tellg(); + stream.seekg(0, std::ios::beg); + + if (size > -1) + { + data->resize(static_cast(size)); + stream.read(const_cast(data->data()), size); + stream.close(); + return true; + } + } + + return false; + } + + size_t file_size(const std::string& file) + { + if (file_exists(file)) + { + std::ifstream stream(file, std::ios::binary); + + if (stream.good()) + { + stream.seekg(0, std::ios::end); + return static_cast(stream.tellg()); + } + } + + return 0; + } + + bool create_directory(const std::string& directory) + { + return std::filesystem::create_directories(directory); + } + + bool directory_exists(const std::string& directory) + { + return std::filesystem::is_directory(directory); + } + + bool directory_is_empty(const std::string& directory) + { + return std::filesystem::is_empty(directory); + } + + std::vector list_files(const std::string& directory) + { + std::vector files; + + for (auto& file : std::filesystem::directory_iterator(directory)) + { + files.push_back(file.path().generic_string()); + } + + return files; + } + + void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target) + { + std::filesystem::copy(src, target, + std::filesystem::copy_options::overwrite_existing | + std::filesystem::copy_options::recursive); + } +} diff --git a/src/common/utils/io.hpp b/src/common/utils/io.hpp new file mode 100644 index 00000000..ab4ebaa4 --- /dev/null +++ b/src/common/utils/io.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +namespace utils::io +{ + bool remove_file(const std::string& file); + bool move_file(const std::string& src, const std::string& target); + bool file_exists(const std::string& file); + bool write_file(const std::string& file, const std::string& data, bool append = false); + bool read_file(const std::string& file, std::string* data); + std::string read_file(const std::string& file); + size_t file_size(const std::string& file); + bool create_directory(const std::string& directory); + bool directory_exists(const std::string& directory); + bool directory_is_empty(const std::string& directory); + std::vector list_files(const std::string& directory); + void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); +} diff --git a/src/common/utils/memory.cpp b/src/common/utils/memory.cpp new file mode 100644 index 00000000..99a03916 --- /dev/null +++ b/src/common/utils/memory.cpp @@ -0,0 +1,173 @@ +#include "memory.hpp" +#include "nt.hpp" + +namespace utils +{ + memory::allocator memory::mem_allocator_; + + memory::allocator::~allocator() + { + this->clear(); + } + + void memory::allocator::clear() + { + std::lock_guard _(this->mutex_); + + for (auto& data : this->pool_) + { + memory::free(data); + } + + this->pool_.clear(); + } + + void memory::allocator::free(void* data) + { + std::lock_guard _(this->mutex_); + + const auto j = std::find(this->pool_.begin(), this->pool_.end(), data); + if (j != this->pool_.end()) + { + memory::free(data); + this->pool_.erase(j); + } + } + + void memory::allocator::free(const void* data) + { + this->free(const_cast(data)); + } + + void* memory::allocator::allocate(const size_t length) + { + std::lock_guard _(this->mutex_); + + const auto data = memory::allocate(length); + this->pool_.push_back(data); + return data; + } + + bool memory::allocator::empty() const + { + return this->pool_.empty(); + } + + char* memory::allocator::duplicate_string(const std::string& string) + { + std::lock_guard _(this->mutex_); + + const auto data = memory::duplicate_string(string); + this->pool_.push_back(data); + return data; + } + + bool memory::allocator::find(const void* data) + { + std::lock_guard _(this->mutex_); + + const auto j = std::find(this->pool_.begin(), this->pool_.end(), data); + return j != this->pool_.end(); + } + + void* memory::allocate(const size_t length) + { + return calloc(length, 1); + } + + char* memory::duplicate_string(const std::string& string) + { + const auto new_string = allocate_array(string.size() + 1); + std::memcpy(new_string, string.data(), string.size()); + return new_string; + } + + void memory::free(void* data) + { + if (data) + { + ::free(data); + } + } + + void memory::free(const void* data) + { + free(const_cast(data)); + } + + bool memory::is_set(const void* mem, const char chr, const size_t length) + { + const auto mem_arr = static_cast(mem); + + for (size_t i = 0; i < length; ++i) + { + if (mem_arr[i] != chr) + { + return false; + } + } + + return true; + } + + bool memory::is_bad_read_ptr(const void* ptr) + { + MEMORY_BASIC_INFORMATION mbi = {}; + if (VirtualQuery(ptr, &mbi, sizeof(mbi))) + { + const DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | + PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); + auto b = !(mbi.Protect & mask); + // check the page is not a guard page + if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true; + + return b; + } + return true; + } + + bool memory::is_bad_code_ptr(const void* ptr) + { + MEMORY_BASIC_INFORMATION mbi = {}; + if (VirtualQuery(ptr, &mbi, sizeof(mbi))) + { + const DWORD mask = (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); + auto b = !(mbi.Protect & mask); + // check the page is not a guard page + if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true; + + return b; + } + return true; + } + + bool memory::is_rdata_ptr(void* pointer) + { + const std::string rdata = ".rdata"; + const auto pointer_lib = utils::nt::library::get_by_address(pointer); + + for (const auto& section : pointer_lib.get_section_headers()) + { + const auto size = sizeof(section->Name); + char name[size + 1]; + name[size] = 0; + std::memcpy(name, section->Name, size); + + if (name == rdata) + { + const auto target = size_t(pointer); + const size_t source_start = size_t(pointer_lib.get_ptr()) + section->PointerToRawData; + const size_t source_end = source_start + section->SizeOfRawData; + + return target >= source_start && target <= source_end; + } + } + + return false; + } + + memory::allocator* memory::get_allocator() + { + return &memory::mem_allocator_; + } +} diff --git a/src/common/utils/memory.hpp b/src/common/utils/memory.hpp new file mode 100644 index 00000000..01f9554f --- /dev/null +++ b/src/common/utils/memory.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +namespace utils +{ + class memory final + { + public: + class allocator final + { + public: + ~allocator(); + + void clear(); + + void free(void* data); + + void free(const void* data); + + void* allocate(size_t length); + + template + inline T* allocate() + { + return this->allocate_array(1); + } + + template + inline T* allocate_array(const size_t count = 1) + { + return static_cast(this->allocate(count * sizeof(T))); + } + + bool empty() const; + + char* duplicate_string(const std::string& string); + + bool find(const void* data); + + private: + std::mutex mutex_; + std::vector pool_; + }; + + static void* allocate(size_t length); + + template + static inline T* allocate() + { + return allocate_array(1); + } + + template + static inline T* allocate_array(const size_t count = 1) + { + return static_cast(allocate(count * sizeof(T))); + } + + static char* duplicate_string(const std::string& string); + + static void free(void* data); + static void free(const void* data); + + static bool is_set(const void* mem, char chr, size_t length); + + static bool is_bad_read_ptr(const void* ptr); + static bool is_bad_code_ptr(const void* ptr); + static bool is_rdata_ptr(void* ptr); + + static allocator* get_allocator(); + + private: + static allocator mem_allocator_; + }; +} diff --git a/src/common/utils/nt.cpp b/src/common/utils/nt.cpp new file mode 100644 index 00000000..d57187be --- /dev/null +++ b/src/common/utils/nt.cpp @@ -0,0 +1,258 @@ +#include "nt.hpp" + +namespace utils::nt +{ + library library::load(const std::string& name) + { + return library(LoadLibraryA(name.data())); + } + + library library::load(const std::filesystem::path& path) + { + return library::load(path.generic_string()); + } + + library library::get_by_address(void* address) + { + HMODULE handle = nullptr; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast(address), &handle); + return library(handle); + } + + library::library() + { + this->module_ = GetModuleHandleA(nullptr); + } + + library::library(const std::string& name) + { + this->module_ = GetModuleHandleA(name.data()); + } + + library::library(const HMODULE handle) + { + this->module_ = handle; + } + + bool library::operator==(const library& obj) const + { + return this->module_ == obj.module_; + } + + library::operator bool() const + { + return this->is_valid(); + } + + library::operator HMODULE() const + { + return this->get_handle(); + } + + PIMAGE_NT_HEADERS library::get_nt_headers() const + { + if (!this->is_valid()) return nullptr; + return reinterpret_cast(this->get_ptr() + this->get_dos_header()->e_lfanew); + } + + PIMAGE_DOS_HEADER library::get_dos_header() const + { + return reinterpret_cast(this->get_ptr()); + } + + PIMAGE_OPTIONAL_HEADER library::get_optional_header() const + { + if (!this->is_valid()) return nullptr; + return &this->get_nt_headers()->OptionalHeader; + } + + std::vector library::get_section_headers() const + { + std::vector headers; + + auto nt_headers = this->get_nt_headers(); + auto section = IMAGE_FIRST_SECTION(nt_headers); + + for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) + { + if (section) headers.push_back(section); + else OutputDebugStringA("There was an invalid section :O"); + } + + return headers; + } + + std::uint8_t* library::get_ptr() const + { + return reinterpret_cast(this->module_); + } + + void library::unprotect() const + { + if (!this->is_valid()) return; + + DWORD protection; + VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, + &protection); + } + + size_t library::get_relative_entry_point() const + { + if (!this->is_valid()) return 0; + return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint; + } + + void* library::get_entry_point() const + { + if (!this->is_valid()) return nullptr; + return this->get_ptr() + this->get_relative_entry_point(); + } + + bool library::is_valid() const + { + return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE; + } + + std::string library::get_name() const + { + if (!this->is_valid()) return ""; + + auto path = this->get_path(); + const auto pos = path.find_last_of("/\\"); + if (pos == std::string::npos) return path; + + return path.substr(pos + 1); + } + + std::string library::get_path() const + { + if (!this->is_valid()) return ""; + + char name[MAX_PATH] = {0}; + GetModuleFileNameA(this->module_, name, sizeof name); + + return name; + } + + std::string library::get_folder() const + { + if (!this->is_valid()) return ""; + + const auto path = std::filesystem::path(this->get_path()); + return path.parent_path().generic_string(); + } + + void library::free() + { + if (this->is_valid()) + { + FreeLibrary(this->module_); + this->module_ = nullptr; + } + } + + HMODULE library::get_handle() const + { + return this->module_; + } + + void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const + { + if (!this->is_valid()) return nullptr; + + const library other_module(module_name); + if (!other_module.is_valid()) return nullptr; + + auto* const target_function = other_module.get_proc(proc_name); + if (!target_function) return nullptr; + + auto* header = this->get_optional_header(); + if (!header) return nullptr; + + auto* import_descriptor = reinterpret_cast(this->get_ptr() + header->DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + while (import_descriptor->Name) + { + if (!_stricmp(reinterpret_cast(this->get_ptr() + import_descriptor->Name), module_name.data())) + { + auto* original_thunk_data = reinterpret_cast(import_descriptor-> + OriginalFirstThunk + this->get_ptr()); + auto* thunk_data = reinterpret_cast(import_descriptor->FirstThunk + this-> + get_ptr()); + + while (original_thunk_data->u1.AddressOfData) + { + if(thunk_data->u1.Function == (uint64_t)target_function) { + return reinterpret_cast(&thunk_data->u1.Function); + } + + const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF; + + if (ordinal_number <= 0xFFFF) { + if (GetProcAddress(other_module.module_, reinterpret_cast(ordinal_number)) == + target_function) + { + return reinterpret_cast(&thunk_data->u1.Function); + } + } + + ++original_thunk_data; + ++thunk_data; + } + + //break; + } + + ++import_descriptor; + } + + return nullptr; + } + + void raise_hard_exception() + { + int data = false; + const library ntdll("ntdll.dll"); + ntdll.invoke_pascal("RtlAdjustPrivilege", 19, true, false, &data); + ntdll.invoke_pascal("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data); + } + + std::string load_resource(const int id) + { + auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA); + if (!res) return {}; + + auto* const handle = LoadResource(nullptr, res); + if (!handle) return {}; + + return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); + } + + void relaunch_self() + { + const utils::nt::library self; + + STARTUPINFOA startup_info; + PROCESS_INFORMATION process_info; + + ZeroMemory(&startup_info, sizeof(startup_info)); + ZeroMemory(&process_info, sizeof(process_info)); + startup_info.cb = sizeof(startup_info); + + char current_dir[MAX_PATH]; + GetCurrentDirectoryA(sizeof(current_dir), current_dir); + auto* const command_line = GetCommandLineA(); + + CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, + &startup_info, &process_info); + + if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); + if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess); + } + + void terminate(const uint32_t code) + { + TerminateProcess(GetCurrentProcess(), code); + } +} diff --git a/src/common/utils/nt.hpp b/src/common/utils/nt.hpp new file mode 100644 index 00000000..86001bea --- /dev/null +++ b/src/common/utils/nt.hpp @@ -0,0 +1,110 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include + +// min and max is required by gdi, therefore NOMINMAX won't work +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#include +#include +#include + +namespace utils::nt +{ + class library final + { + public: + static library load(const std::string& name); + static library load(const std::filesystem::path& path); + static library get_by_address(void* address); + + library(); + explicit library(const std::string& name); + explicit library(HMODULE handle); + + library(const library& a) : module_(a.module_) + { + } + + bool operator!=(const library& obj) const { return !(*this == obj); }; + bool operator==(const library& obj) const; + + operator bool() const; + operator HMODULE() const; + + void unprotect() const; + void* get_entry_point() const; + size_t get_relative_entry_point() const; + + bool is_valid() const; + std::string get_name() const; + std::string get_path() const; + std::string get_folder() const; + std::uint8_t* get_ptr() const; + void free(); + + HMODULE get_handle() const; + + template + T get_proc(const std::string& process) const + { + if (!this->is_valid()) T{}; + return reinterpret_cast(GetProcAddress(this->module_, process.data())); + } + + template + std::function get(const std::string& process) const + { + if (!this->is_valid()) return std::function(); + return static_cast(this->get_proc(process)); + } + + template + T invoke(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_pascal(const std::string& process, Args ... args) const + { + auto method = this->get(process); + if (method) return method(args...); + return T(); + } + + template + T invoke_this(const std::string& process, void* this_ptr, Args ... args) const + { + auto method = this->get(this_ptr, process); + if (method) return method(args...); + return T(); + } + + std::vector get_section_headers() const; + + PIMAGE_NT_HEADERS get_nt_headers() const; + PIMAGE_DOS_HEADER get_dos_header() const; + PIMAGE_OPTIONAL_HEADER get_optional_header() const; + + void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const; + + private: + HMODULE module_; + }; + + __declspec(noreturn) void raise_hard_exception(); + std::string load_resource(int id); + + void relaunch_self(); + __declspec(noreturn) void terminate(uint32_t code = 0); +} diff --git a/src/common/utils/signature.cpp b/src/common/utils/signature.cpp new file mode 100644 index 00000000..9bb8c621 --- /dev/null +++ b/src/common/utils/signature.cpp @@ -0,0 +1,212 @@ +#include "signature.hpp" +#include +#include + +#include + +namespace utils::hook +{ + void signature::load_pattern(const std::string& pattern) + { + this->mask_.clear(); + this->pattern_.clear(); + + uint8_t nibble = 0; + auto has_nibble = false; + + for (auto val : pattern) + { + if (val == ' ') continue; + if (val == '?') + { + this->mask_.push_back(val); + this->pattern_.push_back(0); + } + else + { + if ((val < '0' || val > '9') && (val < 'A' || val > 'F') && (val < 'a' || val > 'f')) + { + throw std::runtime_error("Invalid pattern"); + } + + char str[] = {val, 0}; + const auto current_nibble = static_cast(strtol(str, nullptr, 16)); + + if (!has_nibble) + { + has_nibble = true; + nibble = current_nibble; + } + else + { + has_nibble = false; + const uint8_t byte = current_nibble | (nibble << 4); + + this->mask_.push_back('x'); + this->pattern_.push_back(byte); + } + } + } + + while (!this->mask_.empty() && this->mask_.back() == '?') + { + this->mask_.pop_back(); + this->pattern_.pop_back(); + } + + if (this->has_sse_support()) + { + while (this->pattern_.size() < 16) + { + this->pattern_.push_back(0); + } + } + + if (has_nibble) + { + throw std::runtime_error("Invalid pattern"); + } + } + + std::vector signature::process_range(uint8_t* start, const size_t length) const + { + if (this->has_sse_support()) return this->process_range_vectorized(start, length); + return this->process_range_linear(start, length); + } + + std::vector signature::process_range_linear(uint8_t* start, const size_t length) const + { + std::vector result; + + for (size_t i = 0; i < length; ++i) + { + const auto address = start + i; + + size_t j = 0; + for (; j < this->mask_.size(); ++j) + { + if (this->mask_[j] != '?' && this->pattern_[j] != address[j]) + { + break; + } + } + + if (j == this->mask_.size()) + { + result.push_back(size_t(address)); + } + } + + return result; + } + + std::vector signature::process_range_vectorized(uint8_t* start, const size_t length) const + { + std::vector result; + __declspec(align(16)) char desired_mask[16] = {0}; + + for (size_t i = 0; i < this->mask_.size(); i++) + { + desired_mask[i / 8] |= (this->mask_[i] == '?' ? 0 : 1) << i % 8; + } + + const auto mask = _mm_load_si128(reinterpret_cast(desired_mask)); + const auto comparand = _mm_loadu_si128(reinterpret_cast(this->pattern_.data())); + + for (size_t i = 0; i < length; ++i) + { + const auto address = start + i; + const auto value = _mm_loadu_si128(reinterpret_cast(address)); + const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast(this->mask_.size()), + _SIDD_CMP_EQUAL_EACH); + + const auto matches = _mm_and_si128(mask, comparison); + const auto equivalence = _mm_xor_si128(mask, matches); + + if (_mm_test_all_zeros(equivalence, equivalence)) + { + result.push_back(size_t(address)); + } + } + + return result; + } + + signature::signature_result signature::process() const + { + const auto range = this->length_ - this->mask_.size(); + const auto cores = std::max(1u, std::thread::hardware_concurrency()); + + if (range <= cores * 10ull) return this->process_serial(); + return this->process_parallel(); + } + + signature::signature_result signature::process_serial() const + { + const auto sub = this->has_sse_support() ? 16 : this->mask_.size(); + return {this->process_range(this->start_, this->length_ - sub)}; + } + + signature::signature_result signature::process_parallel() const + { + const auto sub = this->has_sse_support() ? 16 : this->mask_.size(); + const auto range = this->length_ - sub; + const auto cores = std::max(1u, std::thread::hardware_concurrency() / 2); + // Only use half of the available cores + const auto grid = range / cores; + + std::mutex mutex; + std::vector result; + std::vector threads; + + for (auto i = 0u; i < cores; ++i) + { + const auto start = this->start_ + (grid * i); + const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid; + threads.emplace_back([&, start, length]() + { + auto local_result = this->process_range(start, length); + if (local_result.empty()) return; + + std::lock_guard _(mutex); + for (const auto& address : local_result) + { + result.push_back(address); + } + }); + } + + for (auto& t : threads) + { + if (t.joinable()) + { + t.join(); + } + } + + std::sort(result.begin(), result.end()); + return {std::move(result)}; + } + + bool signature::has_sse_support() const + { + if (this->mask_.size() <= 16) + { + int cpu_id[4]; + __cpuid(cpu_id, 0); + + if (cpu_id[0] >= 1) + { + __cpuidex(cpu_id, 1, 0); + return (cpu_id[2] & (1 << 20)) != 0; + } + } + + return false; + } +} + +utils::hook::signature::signature_result operator"" _sig(const char* str, const size_t len) +{ + return utils::hook::signature(std::string(str, len)).process(); +} diff --git a/src/common/utils/signature.hpp b/src/common/utils/signature.hpp new file mode 100644 index 00000000..a3728327 --- /dev/null +++ b/src/common/utils/signature.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "nt.hpp" +#include + +namespace utils::hook +{ + class signature final + { + public: + class signature_result + { + public: + signature_result(std::vector&& matches) : matches_(std::move(matches)) + { + } + + [[nodiscard]] uint8_t* get(const size_t index) const + { + if (index >= this->count()) + { + throw std::runtime_error("Invalid index"); + } + + return reinterpret_cast(this->matches_[index]); + } + + [[nodiscard]] size_t count() const + { + return this->matches_.size(); + } + + private: + std::vector matches_; + }; + + explicit signature(const std::string& pattern, const nt::library library = {}) + : signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage) + { + } + + signature(const std::string& pattern, void* start, void* end) + : signature(pattern, start, size_t(end) - size_t(start)) + { + } + + signature(const std::string& pattern, void* start, const size_t length) + : start_(static_cast(start)), length_(length) + { + this->load_pattern(pattern); + } + + signature_result process() const; + + private: + std::string mask_; + std::basic_string pattern_; + + uint8_t* start_; + size_t length_; + + void load_pattern(const std::string& pattern); + + signature_result process_parallel() const; + signature_result process_serial() const; + std::vector process_range(uint8_t* start, size_t length) const; + std::vector process_range_linear(uint8_t* start, size_t length) const; + std::vector process_range_vectorized(uint8_t* start, size_t length) const; + + bool has_sse_support() const; + }; +} + +utils::hook::signature::signature_result operator"" _sig(const char* str, size_t len); diff --git a/src/common/utils/smbios.cpp b/src/common/utils/smbios.cpp new file mode 100644 index 00000000..a3282c28 --- /dev/null +++ b/src/common/utils/smbios.cpp @@ -0,0 +1,94 @@ +#include "smbios.hpp" +#include "memory.hpp" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +namespace utils::smbios +{ + namespace + { +#pragma warning(push) +#pragma warning(disable: 4200) + struct RawSMBIOSData + { + BYTE Used20CallingMethod; + BYTE SMBIOSMajorVersion; + BYTE SMBIOSMinorVersion; + BYTE DmiRevision; + DWORD Length; + BYTE SMBIOSTableData[]; + }; + + typedef struct + { + BYTE type; + BYTE length; + WORD handle; + } dmi_header; +#pragma warning(pop) + + std::vector get_smbios_data() + { + DWORD size = 0; + std::vector data{}; + + size = GetSystemFirmwareTable('RSMB', 0, nullptr, size); + data.resize(size); + GetSystemFirmwareTable('RSMB', 0, data.data(), size); + + return data; + } + + std::string parse_uuid(const uint8_t* data) + { + if (utils::memory::is_set(data, 0, 16) || utils::memory::is_set(data, -1, 16)) + { + return {}; + } + + char uuid[16] = {0}; + *reinterpret_cast(uuid + 0) = + _byteswap_ulong(*reinterpret_cast(data + 0)); + *reinterpret_cast(uuid + 4) = + _byteswap_ushort(*reinterpret_cast(data + 4)); + *reinterpret_cast(uuid + 6) = + _byteswap_ushort(*reinterpret_cast(data + 6)); + memcpy(uuid + 8, data + 8, 8); + + return std::string(uuid, sizeof(uuid)); + } + } + + std::string get_uuid() + { + auto smbios_data = get_smbios_data(); + auto* raw_data = reinterpret_cast(smbios_data.data()); + + auto* data = raw_data->SMBIOSTableData; + for (DWORD i = 0; i + sizeof(dmi_header) < raw_data->Length;) + { + auto* header = reinterpret_cast(data + i); + if (header->length < 4) + { + return {}; + } + + if (header->type == 0x01 && header->length >= 0x19) + { + return parse_uuid(data + i + 0x8); + } + + i += header->length; + while ((i + 1) < raw_data->Length && *reinterpret_cast(data + i) != 0) + { + ++i; + } + + i += 2; + } + + return {}; + } +} diff --git a/src/common/utils/smbios.hpp b/src/common/utils/smbios.hpp new file mode 100644 index 00000000..bbd1939d --- /dev/null +++ b/src/common/utils/smbios.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace utils::smbios +{ + std::string get_uuid(); +} diff --git a/src/common/utils/string.cpp b/src/common/utils/string.cpp new file mode 100644 index 00000000..653ecff2 --- /dev/null +++ b/src/common/utils/string.cpp @@ -0,0 +1,179 @@ +#include "string.hpp" +#include +#include +#include + +#include "nt.hpp" + +namespace utils::string +{ + const char* va(const char* fmt, ...) + { + static thread_local va_provider<8, 256> provider; + + va_list ap; + va_start(ap, fmt); + + const char* result = provider.get(fmt, ap); + + va_end(ap); + return result; + } + + std::vector split(const std::string& s, const char delim) + { + std::stringstream ss(s); + std::string item; + std::vector elems; + + while (std::getline(ss, item, delim)) + { + elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson) + } + + return elems; + } + + std::string to_lower(std::string text) + { + std::transform(text.begin(), text.end(), text.begin(), [](const char input) + { + return static_cast(tolower(input)); + }); + + return text; + } + + std::string to_upper(std::string text) + { + std::transform(text.begin(), text.end(), text.begin(), [](const char input) + { + return static_cast(toupper(input)); + }); + + return text; + } + + bool starts_with(const std::string& text, const std::string& substring) + { + return text.find(substring) == 0; + } + + bool ends_with(const std::string& text, const std::string& substring) + { + if (substring.size() > text.size()) return false; + return std::equal(substring.rbegin(), substring.rend(), text.rbegin()); + } + + std::string dump_hex(const std::string& data, const std::string& separator) + { + std::string result; + + for (unsigned int i = 0; i < data.size(); ++i) + { + if (i > 0) + { + result.append(separator); + } + + result.append(va("%02X", data[i] & 0xFF)); + } + + return result; + } + + std::string get_clipboard_data() + { + if (OpenClipboard(nullptr)) + { + std::string data; + + auto* const clipboard_data = GetClipboardData(1u); + if (clipboard_data) + { + auto* const cliptext = static_cast(GlobalLock(clipboard_data)); + if (cliptext) + { + data.append(cliptext); + GlobalUnlock(clipboard_data); + } + } + CloseClipboard(); + + return data; + } + return {}; + } + + void strip(const char* in, char* out, int max) + { + if (!in || !out) return; + + max--; + auto current = 0; + while (*in != 0 && current < max) + { + const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48); + + if (*in == '^' && (color_index != 7 || *(in + 1) == '7')) + { + ++in; + } + else + { + *out = *in; + ++out; + ++current; + } + + ++in; + } + *out = '\0'; + } + +#pragma warning(push) +#pragma warning(disable: 4100) + std::string convert(const std::wstring& wstr) + { + std::string result; + result.reserve(wstr.size()); + + for (const auto& chr : wstr) + { + result.push_back(static_cast(chr)); + } + + return result; + } + + std::wstring convert(const std::string& str) + { + std::wstring result; + result.reserve(str.size()); + + for (const auto& chr : str) + { + result.push_back(static_cast(chr)); + } + + return result; + } +#pragma warning(pop) + + std::string replace(std::string str, const std::string& from, const std::string& to) + { + if (from.empty()) + { + return str; + } + + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + + return str; + } +} diff --git a/src/common/utils/string.hpp b/src/common/utils/string.hpp new file mode 100644 index 00000000..20fa4827 --- /dev/null +++ b/src/common/utils/string.hpp @@ -0,0 +1,100 @@ +#pragma once +#include "memory.hpp" +#include + +#ifndef ARRAYSIZE +template +size_t ARRAYSIZE(Type (&)[n]) { return n; } +#endif + +namespace utils::string +{ + template + class va_provider final + { + public: + static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0"); + + va_provider() : current_buffer_(0) + { + } + + char* get(const char* format, const va_list ap) + { + ++this->current_buffer_ %= ARRAYSIZE(this->string_pool_); + auto entry = &this->string_pool_[this->current_buffer_]; + + if (!entry->size || !entry->buffer) + { + throw std::runtime_error("String pool not initialized"); + } + + while (true) + { + const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); + if (res > 0) break; // Success + if (res == 0) return nullptr; // Error + + entry->double_size(); + } + + return entry->buffer; + } + + private: + class entry final + { + public: + explicit entry(const size_t _size = MinBufferSize) : size(_size), buffer(nullptr) + { + if (this->size < MinBufferSize) this->size = MinBufferSize; + this->allocate(); + } + + ~entry() + { + if (this->buffer) memory::get_allocator()->free(this->buffer); + this->size = 0; + this->buffer = nullptr; + } + + void allocate() + { + if (this->buffer) memory::get_allocator()->free(this->buffer); + this->buffer = memory::get_allocator()->allocate_array(this->size + 1); + } + + void double_size() + { + this->size *= 2; + this->allocate(); + } + + size_t size; + char* buffer; + }; + + size_t current_buffer_; + entry string_pool_[Buffers]; + }; + + const char* va(const char* fmt, ...); + + std::vector split(const std::string& s, char delim); + + std::string to_lower(std::string text); + std::string to_upper(std::string text); + bool starts_with(const std::string& text, const std::string& substring); + bool ends_with(const std::string& text, const std::string& substring); + + std::string dump_hex(const std::string& data, const std::string& separator = " "); + + std::string get_clipboard_data(); + + void strip(const char* in, char* out, int max); + + std::string convert(const std::wstring& wstr); + std::wstring convert(const std::string& str); + + std::string replace(std::string str, const std::string& from, const std::string& to); +} diff --git a/src/common/utils/thread.cpp b/src/common/utils/thread.cpp new file mode 100644 index 00000000..714704b6 --- /dev/null +++ b/src/common/utils/thread.cpp @@ -0,0 +1,127 @@ +#include "thread.hpp" +#include "string.hpp" +#include "finally.hpp" + +#include + +namespace utils::thread +{ + bool set_name(const HANDLE t, const std::string& name) + { + const nt::library kernel32("kernel32.dll"); + if (!kernel32) + { + return false; + } + + const auto set_description = kernel32.get_proc("SetThreadDescription"); + if (!set_description) + { + return false; + } + + return SUCCEEDED(set_description(t, string::convert(name).data())); + } + + bool set_name(const DWORD id, const std::string& name) + { + auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id); + if (!t) return false; + + const auto _ = utils::finally([t]() + { + CloseHandle(t); + }); + + return set_name(t, name); + } + + bool set_name(std::thread& t, const std::string& name) + { + return set_name(t.native_handle(), name); + } + + bool set_name(const std::string& name) + { + return set_name(GetCurrentThread(), name); + } + + std::vector get_thread_ids() + { + auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId()); + if (h == INVALID_HANDLE_VALUE) + { + return {}; + } + + const auto _ = utils::finally([h]() + { + CloseHandle(h); + }); + + THREADENTRY32 entry{}; + entry.dwSize = sizeof(entry); + if (!Thread32First(h, &entry)) + { + return {}; + } + + std::vector ids{}; + + do + { + const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + + sizeof(entry.th32OwnerProcessID); + entry.dwSize = sizeof(entry); + + if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId()) + { + ids.emplace_back(entry.th32ThreadID); + } + } + while (Thread32Next(h, &entry)); + + return ids; + } + + void for_each_thread(const std::function& callback) + { + const auto ids = get_thread_ids(); + + for (const auto& id : ids) + { + auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id); + if (thread != nullptr) + { + const auto _ = utils::finally([thread]() + { + CloseHandle(thread); + }); + + callback(thread); + } + } + } + + void suspend_other_threads() + { + for_each_thread([](const HANDLE thread) + { + if (GetThreadId(thread) != GetCurrentThreadId()) + { + SuspendThread(thread); + } + }); + } + + void resume_other_threads() + { + for_each_thread([](const HANDLE thread) + { + if (GetThreadId(thread) != GetCurrentThreadId()) + { + ResumeThread(thread); + } + }); + } +} diff --git a/src/common/utils/thread.hpp b/src/common/utils/thread.hpp new file mode 100644 index 00000000..4ea3598c --- /dev/null +++ b/src/common/utils/thread.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include "nt.hpp" + +namespace utils::thread +{ + bool set_name(HANDLE t, const std::string& name); + bool set_name(DWORD id, const std::string& name); + bool set_name(std::thread& t, const std::string& name); + bool set_name(const std::string& name); + + template + std::thread create_named_thread(const std::string& name, Args&&... args) + { + auto t = std::thread(std::forward(args)...); + set_name(t, name); + return t; + } + + std::vector get_thread_ids(); + void for_each_thread(const std::function& callback); + + void suspend_other_threads(); + void resume_other_threads(); +} diff --git a/tools/premake5.exe b/tools/premake5.exe new file mode 100644 index 00000000..c73da1fb Binary files /dev/null and b/tools/premake5.exe differ