mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-04-19 21:22:54 +00:00
Use t6-gsc-utils as a base.
This commit is contained in:
parent
e5b1504c1c
commit
011bb0c554
150
.gitignore
vendored
Normal file
150
.gitignore
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
### Windows
|
||||||
|
|
||||||
|
version.hpp
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### OSX
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear on external disk
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Visual Studio
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
build
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
### IDA
|
||||||
|
*.id0
|
||||||
|
*.id1
|
||||||
|
*.id2
|
||||||
|
*.nam
|
||||||
|
*.til
|
||||||
|
|
||||||
|
### Custom user files
|
||||||
|
# User scripts
|
||||||
|
user*.bat
|
||||||
|
*.code-workspace
|
34
deps/premake/asmjit.lua
vendored
Normal file
34
deps/premake/asmjit.lua
vendored
Normal file
@ -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)
|
73
deps/premake/curl.lua
vendored
Normal file
73
deps/premake/curl.lua
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
curl = {
|
||||||
|
source = path.join(dependencies.basePath, "curl"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function curl.import()
|
||||||
|
links { "curl" }
|
||||||
|
|
||||||
|
filter "toolset:msc*"
|
||||||
|
links { "Crypt32.lib" }
|
||||||
|
filter {}
|
||||||
|
|
||||||
|
curl.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function curl.includes()
|
||||||
|
filter "toolset:msc*"
|
||||||
|
includedirs {
|
||||||
|
path.join(curl.source, "include"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"CURL_STRICTER",
|
||||||
|
"CURL_STATICLIB",
|
||||||
|
"CURL_DISABLE_LDAP",
|
||||||
|
}
|
||||||
|
filter {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function curl.project()
|
||||||
|
if not os.istarget("windows") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
project "curl"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
curl.includes()
|
||||||
|
|
||||||
|
includedirs {
|
||||||
|
path.join(curl.source, "lib"),
|
||||||
|
}
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(curl.source, "lib/**.c"),
|
||||||
|
path.join(curl.source, "lib/**.h"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"BUILDING_LIBCURL",
|
||||||
|
}
|
||||||
|
|
||||||
|
filter "toolset:msc*"
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"USE_SCHANNEL",
|
||||||
|
"USE_WINDOWS_SSPI",
|
||||||
|
"USE_THREADS_WIN32",
|
||||||
|
}
|
||||||
|
|
||||||
|
filter "toolset:not msc*"
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"USE_GNUTLS",
|
||||||
|
"USE_THREADS_POSIX",
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, curl)
|
19
deps/premake/gsl.lua
vendored
Normal file
19
deps/premake/gsl.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
gsl = {
|
||||||
|
source = path.join(dependencies.basePath, "GSL"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function gsl.import()
|
||||||
|
gsl.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function gsl.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(gsl.source, "include")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function gsl.project()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, gsl)
|
19
deps/premake/json.lua
vendored
Normal file
19
deps/premake/json.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
json = {
|
||||||
|
source = path.join(dependencies.basePath, "json"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function json.import()
|
||||||
|
json.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(json.source, "single_include/*")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.project()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, json)
|
31
deps/premake/minhook.lua
vendored
Normal file
31
deps/premake/minhook.lua
vendored
Normal file
@ -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)
|
43
deps/premake/minizip.lua
vendored
Normal file
43
deps/premake/minizip.lua
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
minizip = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function minizip.import()
|
||||||
|
links { "minizip" }
|
||||||
|
zlib.import()
|
||||||
|
minizip.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.includes()
|
||||||
|
includedirs {
|
||||||
|
minizip.source
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.project()
|
||||||
|
project "minizip"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
minizip.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(minizip.source, "*.h"),
|
||||||
|
path.join(minizip.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
removefiles {
|
||||||
|
path.join(minizip.source, "miniunz.c"),
|
||||||
|
path.join(minizip.source, "minizip.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, minizip)
|
39
deps/premake/zlib.lua
vendored
Normal file
39
deps/premake/zlib.lua
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
zlib = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function zlib.import()
|
||||||
|
links { "zlib" }
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.includes()
|
||||||
|
includedirs {
|
||||||
|
zlib.source
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"ZLIB_CONST",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.project()
|
||||||
|
project "zlib"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(zlib.source, "*.h"),
|
||||||
|
path.join(zlib.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, zlib)
|
3
generate.bat
Normal file
3
generate.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
call git submodule update --init --recursive
|
||||||
|
tools\windows\premake5.exe vs2022
|
100
premake5.lua
Normal file
100
premake5.lua
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
dependencies.load()
|
||||||
|
|
||||||
|
workspace "t6-gsc-utils"
|
||||||
|
location "./build"
|
||||||
|
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
||||||
|
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||||
|
targetname "%{prj.name}"
|
||||||
|
|
||||||
|
configurations { "Debug", "Release", }
|
||||||
|
|
||||||
|
language "C++"
|
||||||
|
cppdialect "C++20"
|
||||||
|
|
||||||
|
architecture "x86"
|
||||||
|
|
||||||
|
systemversion "latest"
|
||||||
|
symbols "On"
|
||||||
|
staticruntime "On"
|
||||||
|
editandcontinue "Off"
|
||||||
|
warnings "Extra"
|
||||||
|
characterset "ASCII"
|
||||||
|
|
||||||
|
flags
|
||||||
|
{
|
||||||
|
"NoIncrementalLink",
|
||||||
|
"MultiProcessorCompile",
|
||||||
|
}
|
||||||
|
|
||||||
|
filter "configurations:Release"
|
||||||
|
optimize "Full"
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
filter {}
|
||||||
|
|
||||||
|
filter "configurations:Debug"
|
||||||
|
optimize "Debug"
|
||||||
|
defines { "DEBUG", "_DEBUG" }
|
||||||
|
filter {}
|
||||||
|
|
||||||
|
startproject "t6-gsc-utils"
|
||||||
|
|
||||||
|
project "t6-gsc-utils"
|
||||||
|
kind "SharedLib"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
files
|
||||||
|
{
|
||||||
|
"./src/**.h",
|
||||||
|
"./src/**.hpp",
|
||||||
|
"./src/**.cpp",
|
||||||
|
}
|
||||||
|
|
||||||
|
includedirs
|
||||||
|
{
|
||||||
|
"%{prj.location}/src",
|
||||||
|
"./src",
|
||||||
|
}
|
||||||
|
|
||||||
|
resincludedirs
|
||||||
|
{
|
||||||
|
"$(ProjectDir)src"
|
||||||
|
}
|
||||||
|
|
||||||
|
pchheader "stdinc.hpp"
|
||||||
|
pchsource "src/stdinc.cpp"
|
||||||
|
buildoptions { "/Zm100 -Zm100" }
|
||||||
|
|
||||||
|
dependencies.imports()
|
||||||
|
|
||||||
|
group "Dependencies"
|
||||||
|
dependencies.projects()
|
72
src/component/signatures.cpp
Normal file
72
src/component/signatures.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "signatures.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace signatures
|
||||||
|
{
|
||||||
|
size_t load_image_size()
|
||||||
|
{
|
||||||
|
MODULEINFO info{};
|
||||||
|
GetModuleInformation(GetCurrentProcess(),
|
||||||
|
GetModuleHandle("plutonium-bootstrapper-win32.exe"), &info, sizeof(MODULEINFO));
|
||||||
|
return info.SizeOfImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_image_size()
|
||||||
|
{
|
||||||
|
static const auto image_size = load_image_size();
|
||||||
|
return image_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t find_string_ptr(const std::string& string)
|
||||||
|
{
|
||||||
|
const char* string_ptr = nullptr;
|
||||||
|
std::string mask(string.size(), 'x');
|
||||||
|
const auto base = reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
||||||
|
utils::hook::signature signature(base, get_image_size() - base);
|
||||||
|
|
||||||
|
signature.add({
|
||||||
|
string,
|
||||||
|
mask,
|
||||||
|
[&](char* address)
|
||||||
|
{
|
||||||
|
string_ptr = address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
signature.process();
|
||||||
|
return reinterpret_cast<size_t>(string_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t find_string_ref(const std::string& string)
|
||||||
|
{
|
||||||
|
char bytes[4] = {0};
|
||||||
|
const auto string_ptr = find_string_ptr(string);
|
||||||
|
memcpy(bytes, &string_ptr, sizeof(bytes));
|
||||||
|
return find_string_ptr({bytes, 4});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool process_printf()
|
||||||
|
{
|
||||||
|
const auto string_ref = find_string_ref("A critical exception occured!\n");
|
||||||
|
if (!string_ref)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto offset = *reinterpret_cast<size_t*>(string_ref + 5);
|
||||||
|
OutputDebugString(utils::string::va("%p\n", string_ref + 4 + 5 + offset));
|
||||||
|
game::plutonium::printf.set(string_ref + 4 + 5 + offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process()
|
||||||
|
{
|
||||||
|
return process_printf();
|
||||||
|
}
|
||||||
|
}
|
6
src/component/signatures.hpp
Normal file
6
src/component/signatures.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace signatures
|
||||||
|
{
|
||||||
|
bool process();
|
||||||
|
}
|
32
src/dllmain.cpp
Normal file
32
src/dllmain.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "component/signatures.hpp"
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
||||||
|
{
|
||||||
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
if (!signatures::process())
|
||||||
|
{
|
||||||
|
MessageBoxA(NULL,
|
||||||
|
"This version of T4SP-Server-Plugin is outdated.\n" \
|
||||||
|
"Download the latest dll from here: https://github.com/fedddddd/t6-gsc-utils/releases",
|
||||||
|
"ERROR", MB_ICONERROR);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game::plutonium::printf.get() != nullptr)
|
||||||
|
{
|
||||||
|
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), game::plutonium::printf);
|
||||||
|
}
|
||||||
|
|
||||||
|
component_loader::post_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
32
src/game/game.cpp
Normal file
32
src/game/game.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "game.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
gamemode current = reinterpret_cast<const char*>(0x88A5DC) != "CoDWaW.exe"s
|
||||||
|
? gamemode::multiplayer
|
||||||
|
: gamemode::singleplayer;
|
||||||
|
|
||||||
|
namespace environment
|
||||||
|
{
|
||||||
|
bool t4mp()
|
||||||
|
{
|
||||||
|
return current == gamemode::multiplayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool t4sp()
|
||||||
|
{
|
||||||
|
return current == gamemode::singleplayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace plutonium
|
||||||
|
{
|
||||||
|
bool is_up_to_date()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
src/game/game.hpp
Normal file
66
src/game/game.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "structs.hpp"
|
||||||
|
|
||||||
|
#define SELECT(mp, sp) (game::environment::t4mp() ? mp : sp)
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
enum gamemode
|
||||||
|
{
|
||||||
|
multiplayer,
|
||||||
|
singleplayer,
|
||||||
|
none
|
||||||
|
};
|
||||||
|
|
||||||
|
extern gamemode current;
|
||||||
|
|
||||||
|
namespace environment
|
||||||
|
{
|
||||||
|
bool t4mp();
|
||||||
|
bool t4sp();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class symbol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
symbol(const size_t t4mp, const size_t t4sp)
|
||||||
|
: t4mp_(reinterpret_cast<T*>(t4mp))
|
||||||
|
, t4sp_(reinterpret_cast<T*>(t4sp))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
if (environment::t4mp())
|
||||||
|
{
|
||||||
|
return t4mp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t4sp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const size_t ptr)
|
||||||
|
{
|
||||||
|
this->t4mp_ = reinterpret_cast<T*>(ptr);
|
||||||
|
this->t4sp_ = reinterpret_cast<T*>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T* () const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* t4mp_;
|
||||||
|
T* t4sp_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "symbols.hpp"
|
9
src/game/structs.hpp
Normal file
9
src/game/structs.hpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
typedef float vec_t;
|
||||||
|
typedef vec_t vec2_t[2];
|
||||||
|
typedef vec_t vec3_t[3];
|
||||||
|
typedef vec_t vec4_t[4];
|
||||||
|
}
|
15
src/game/symbols.hpp
Normal file
15
src/game/symbols.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WEAK __declspec(selectany)
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
namespace plutonium
|
||||||
|
{
|
||||||
|
WEAK symbol<int(const char* fmt, ...)> printf{0, 0};
|
||||||
|
}
|
||||||
|
}
|
35
src/loader/component_interface.hpp
Normal file
35
src/loader/component_interface.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~component_interface()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void post_start()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void post_load()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void pre_destroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void post_unpack()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool is_supported()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
127
src/loader/component_loader.cpp
Normal file
127
src/loader/component_loader.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "component_loader.hpp"
|
||||||
|
|
||||||
|
void component_loader::register_component(std::unique_ptr<component_interface>&& component_)
|
||||||
|
{
|
||||||
|
get_components().push_back(std::move(component_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool component_loader::post_start()
|
||||||
|
{
|
||||||
|
static auto handled = false;
|
||||||
|
if (handled) return true;
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (const auto& component_ : get_components())
|
||||||
|
{
|
||||||
|
component_->post_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (premature_shutdown_trigger&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::load_import(const std::string& library, const std::string& function)
|
||||||
|
{
|
||||||
|
void* function_ptr = nullptr;
|
||||||
|
|
||||||
|
for (const auto& component_ : get_components())
|
||||||
|
{
|
||||||
|
auto* const component_function_ptr = component_->load_import(library, function);
|
||||||
|
if (component_function_ptr)
|
||||||
|
{
|
||||||
|
function_ptr = component_function_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void component_loader::trigger_premature_shutdown()
|
||||||
|
{
|
||||||
|
throw premature_shutdown_trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<component_interface>>& component_loader::get_components()
|
||||||
|
{
|
||||||
|
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||||
|
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||||
|
|
||||||
|
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
||||||
|
{
|
||||||
|
pre_destroy();
|
||||||
|
delete component_vector;
|
||||||
|
});
|
||||||
|
|
||||||
|
return *components;
|
||||||
|
}
|
61
src/loader/component_loader.hpp
Normal file
61
src/loader/component_loader.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#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 <typename T>
|
||||||
|
class installer final
|
||||||
|
{
|
||||||
|
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
||||||
|
|
||||||
|
public:
|
||||||
|
installer()
|
||||||
|
{
|
||||||
|
register_component(std::make_unique<T>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T* get()
|
||||||
|
{
|
||||||
|
for (const auto& component_ : get_components())
|
||||||
|
{
|
||||||
|
if (typeid(*component_.get()) == typeid(T))
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T*>(component_.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_component(std::unique_ptr<component_interface>&& component);
|
||||||
|
|
||||||
|
static bool post_start();
|
||||||
|
static bool post_load();
|
||||||
|
static void post_unpack();
|
||||||
|
static void pre_destroy();
|
||||||
|
static void clean();
|
||||||
|
|
||||||
|
static void* load_import(const std::string& library, const std::string& function);
|
||||||
|
|
||||||
|
static void trigger_premature_shutdown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
||||||
|
};
|
||||||
|
|
||||||
|
#define REGISTER_COMPONENT(name) \
|
||||||
|
namespace \
|
||||||
|
{ \
|
||||||
|
static component_loader::installer<name> __component; \
|
||||||
|
}
|
1
src/stdinc.cpp
Normal file
1
src/stdinc.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include <stdinc.hpp>
|
56
src/stdinc.hpp
Normal file
56
src/stdinc.hpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(disable: 6011)
|
||||||
|
#pragma warning(disable: 6054)
|
||||||
|
#pragma warning(disable: 6387)
|
||||||
|
#pragma warning(disable: 26451)
|
||||||
|
#pragma warning(disable: 26812)
|
||||||
|
#pragma warning(disable: 28182)
|
||||||
|
|
||||||
|
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <map>
|
||||||
|
#include <csetjmp>
|
||||||
|
#include <atlcomcli.h>
|
||||||
|
#include <variant>
|
||||||
|
#include <optional>
|
||||||
|
#include <Psapi.h>
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <MinHook.h>
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
|
#include <asmjit/core/jitruntime.h>
|
||||||
|
#include <asmjit/x86/x86assembler.h>
|
||||||
|
|
||||||
|
#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;
|
170
src/utils/compression.cpp
Normal file
170
src/utils/compression.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "memory.hpp"
|
||||||
|
#include "compression.hpp"
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <zip.h>
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
#include "io.hpp"
|
||||||
|
|
||||||
|
namespace utils::compression
|
||||||
|
{
|
||||||
|
namespace zlib
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class zlib_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
zlib_stream()
|
||||||
|
{
|
||||||
|
memset(&stream_, 0, sizeof(stream_));
|
||||||
|
valid_ = inflateInit(&stream_) == Z_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib_stream(zlib_stream&&) = delete;
|
||||||
|
zlib_stream(const zlib_stream&) = delete;
|
||||||
|
zlib_stream& operator=(zlib_stream&&) = delete;
|
||||||
|
zlib_stream& operator=(const zlib_stream&) = delete;
|
||||||
|
|
||||||
|
~zlib_stream()
|
||||||
|
{
|
||||||
|
if (valid_)
|
||||||
|
{
|
||||||
|
inflateEnd(&stream_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z_stream& get()
|
||||||
|
{
|
||||||
|
return stream_; //
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid() const
|
||||||
|
{
|
||||||
|
return valid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool valid_{false};
|
||||||
|
z_stream stream_{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string decompress(const std::string& data)
|
||||||
|
{
|
||||||
|
std::string buffer{};
|
||||||
|
zlib_stream stream_container{};
|
||||||
|
if (!stream_container.is_valid())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret{};
|
||||||
|
size_t offset = 0;
|
||||||
|
static thread_local uint8_t dest[CHUNK] = {0};
|
||||||
|
auto& stream = stream_container.get();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||||
|
stream.avail_in = static_cast<uInt>(input_size);
|
||||||
|
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||||
|
offset += stream.avail_in;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stream.avail_out = sizeof(dest);
|
||||||
|
stream.next_out = dest;
|
||||||
|
|
||||||
|
ret = inflate(&stream, Z_NO_FLUSH);
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||||
|
}
|
||||||
|
while (stream.avail_out == 0);
|
||||||
|
}
|
||||||
|
while (ret != Z_STREAM_END);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string compress(const std::string& data)
|
||||||
|
{
|
||||||
|
std::string result{};
|
||||||
|
auto length = compressBound(static_cast<uLong>(data.size()));
|
||||||
|
result.resize(length);
|
||||||
|
|
||||||
|
if (compress2(reinterpret_cast<Bytef*>(result.data()), &length,
|
||||||
|
reinterpret_cast<const Bytef*>(data.data()), static_cast<uLong>(data.size()),
|
||||||
|
Z_BEST_COMPRESSION) != Z_OK)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result.resize(length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace zip
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool add_file(zipFile& zip_file, const std::string& filename, const std::string& data)
|
||||||
|
{
|
||||||
|
const auto zip_64 = data.size() > 0xffffffff ? 1 : 0;
|
||||||
|
if (ZIP_OK != zipOpenNewFileInZip64(zip_file, filename.data(), nullptr, nullptr, 0, nullptr, 0, nullptr,
|
||||||
|
Z_DEFLATED, Z_BEST_COMPRESSION, zip_64))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([&zip_file]()
|
||||||
|
{
|
||||||
|
zipCloseFileInZip(zip_file);
|
||||||
|
});
|
||||||
|
|
||||||
|
return ZIP_OK == zipWriteInFileInZip(zip_file, data.data(), static_cast<unsigned>(data.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive::add(std::string filename, std::string data)
|
||||||
|
{
|
||||||
|
this->files_[std::move(filename)] = std::move(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool archive::write(const std::string& filename, const std::string& comment)
|
||||||
|
{
|
||||||
|
// Hack to create the directory :3
|
||||||
|
io::write_file(filename, {});
|
||||||
|
io::remove_file(filename);
|
||||||
|
|
||||||
|
auto* zip_file = zipOpen64(filename.data(), 0);
|
||||||
|
if (!zip_file)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([&zip_file, &comment]()
|
||||||
|
{
|
||||||
|
zipClose(zip_file, comment.empty() ? nullptr : comment.data());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& file : this->files_)
|
||||||
|
{
|
||||||
|
if (!add_file(zip_file, file.first, file.second))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/utils/compression.hpp
Normal file
28
src/utils/compression.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#define CHUNK 16384u
|
||||||
|
|
||||||
|
namespace utils::compression
|
||||||
|
{
|
||||||
|
namespace zlib
|
||||||
|
{
|
||||||
|
std::string compress(const std::string& data);
|
||||||
|
std::string decompress(const std::string& data);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace zip
|
||||||
|
{
|
||||||
|
class archive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(std::string filename, std::string data);
|
||||||
|
bool write(const std::string& filename, const std::string& comment = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::string> files_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
46
src/utils/concurrency.hpp
Normal file
46
src/utils/concurrency.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace utils::concurrency
|
||||||
|
{
|
||||||
|
template <typename T, typename MutexType = std::mutex>
|
||||||
|
class container
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor)
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor)
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& get_raw() { return object_; }
|
||||||
|
const T& get_raw() const { return object_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable MutexType mutex_{};
|
||||||
|
T object_{};
|
||||||
|
};
|
||||||
|
}
|
54
src/utils/flags.cpp
Normal file
54
src/utils/flags.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
#include "flags.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
namespace utils::flags
|
||||||
|
{
|
||||||
|
void parse_flags(std::vector<std::string>& 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<std::string> 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;
|
||||||
|
}
|
||||||
|
}
|
6
src/utils/flags.hpp
Normal file
6
src/utils/flags.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils::flags
|
||||||
|
{
|
||||||
|
bool has_flag(const std::string& flag);
|
||||||
|
}
|
247
src/utils/hook.cpp
Normal file
247
src/utils/hook.cpp
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "hook.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
// iw6x-client
|
||||||
|
|
||||||
|
namespace utils::hook
|
||||||
|
{
|
||||||
|
void signature::process()
|
||||||
|
{
|
||||||
|
if (this->signatures_.empty()) return;
|
||||||
|
|
||||||
|
const auto start = static_cast<char*>(this->start_);
|
||||||
|
|
||||||
|
const unsigned int sig_count = this->signatures_.size();
|
||||||
|
const auto containers = this->signatures_.data();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < this->length_; ++i)
|
||||||
|
{
|
||||||
|
const auto address = start + i;
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < sig_count; ++k)
|
||||||
|
{
|
||||||
|
const auto container = &containers[k];
|
||||||
|
|
||||||
|
unsigned int j;
|
||||||
|
for (j = 0; j < static_cast<unsigned int>(container->mask.size()); ++j)
|
||||||
|
{
|
||||||
|
if (container->mask[j] != '?' && container->signature[j] != address[j])
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == container->mask.size())
|
||||||
|
{
|
||||||
|
container->callback(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void signature::add(const container& container)
|
||||||
|
{
|
||||||
|
signatures_.push_back(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
[[maybe_unused]] class _
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_()
|
||||||
|
{
|
||||||
|
if (MH_Initialize() != MH_OK)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to initialize MinHook");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~_()
|
||||||
|
{
|
||||||
|
MH_Uninitialize();
|
||||||
|
}
|
||||||
|
} __;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<void*>(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<void*>(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_;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<void*>(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<void*>(place), data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_relatively_far(const void* pointer, const void* data, 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<uint8_t>(patch_pointer, 0xE8);
|
||||||
|
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void call(const size_t pointer, void* data)
|
||||||
|
{
|
||||||
|
return call(reinterpret_cast<void*>(pointer), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call(const size_t pointer, const size_t data)
|
||||||
|
{
|
||||||
|
return call(pointer, reinterpret_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes)
|
||||||
|
{
|
||||||
|
DWORD oldProtect = 0;
|
||||||
|
|
||||||
|
auto* place = reinterpret_cast<void*>(address);
|
||||||
|
VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
memcpy(place, bytes.data(), bytes.size());
|
||||||
|
VirtualProtect(place, bytes.size(), oldProtect, &oldProtect);
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), place, bytes.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(std::uintptr_t address, void* buffer, size_t size)
|
||||||
|
{
|
||||||
|
DWORD oldProtect = 0;
|
||||||
|
|
||||||
|
auto* place = reinterpret_cast<void*>(address);
|
||||||
|
VirtualProtect(place, size, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
memcpy(place, buffer, size);
|
||||||
|
VirtualProtect(place, size, oldProtect, &oldProtect);
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), place, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump(std::uintptr_t address, void* destination)
|
||||||
|
{
|
||||||
|
if (!address) return;
|
||||||
|
|
||||||
|
std::uint8_t* bytes = new std::uint8_t[5];
|
||||||
|
*bytes = 0xE9;
|
||||||
|
*reinterpret_cast<std::uint32_t*>(bytes + 1) = CalculateRelativeJMPAddress(address, destination);
|
||||||
|
|
||||||
|
set(address, bytes, 5);
|
||||||
|
|
||||||
|
delete[] bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* assemble(const std::function<void(assembler&)>& 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;
|
||||||
|
}
|
||||||
|
}
|
165
src/utils/hook.hpp
Normal file
165
src/utils/hook.hpp
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CalculateRelativeJMPAddress(X, Y) (((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||||
|
|
||||||
|
#include <asmjit/core/jitruntime.h>
|
||||||
|
#include <asmjit/x86/x86assembler.h>
|
||||||
|
|
||||||
|
using namespace asmjit::x86;
|
||||||
|
|
||||||
|
namespace utils::hook
|
||||||
|
{
|
||||||
|
class signature final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct container final
|
||||||
|
{
|
||||||
|
std::string signature;
|
||||||
|
std::string mask;
|
||||||
|
std::function<void(char*)> callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
signature(void* start, const size_t length) : start_(start), length_(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
signature(const DWORD start, const size_t length) : signature(reinterpret_cast<void*>(start), length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
signature() : signature(0x400000, 0x800000)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void process();
|
||||||
|
void add(const container& container);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* start_;
|
||||||
|
size_t length_;
|
||||||
|
std::vector<container> signatures_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class assembler : public Assembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Assembler::Assembler;
|
||||||
|
using Assembler::call;
|
||||||
|
using Assembler::jmp;
|
||||||
|
|
||||||
|
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 <typename T>
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->get_original());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke(Args... args)
|
||||||
|
{
|
||||||
|
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] void* get_original() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* place_{};
|
||||||
|
void* original_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
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(std::uintptr_t address, void* destination);
|
||||||
|
|
||||||
|
void* assemble(const std::function<void(assembler&)>& asm_function);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T extract(void* address)
|
||||||
|
{
|
||||||
|
const auto data = static_cast<uint8_t*>(address);
|
||||||
|
const auto offset = *reinterpret_cast<int32_t*>(data);
|
||||||
|
return reinterpret_cast<T>(data + offset + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void set(void* place, T value)
|
||||||
|
{
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
|
|
||||||
|
*static_cast<T*>(place) = value;
|
||||||
|
|
||||||
|
VirtualProtect(place, sizeof(T), old_protect, &old_protect);
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void set(const size_t place, T value)
|
||||||
|
{
|
||||||
|
return set<T>(reinterpret_cast<void*>(place), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
static T invoke(size_t func, Args... args)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
static T invoke(void* func, Args... args)
|
||||||
|
{
|
||||||
|
return static_cast<T(*)(Args ...)>(func)(args...);
|
||||||
|
}
|
||||||
|
}
|
101
src/utils/http.cpp
Normal file
101
src/utils/http.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "http.hpp"
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
|
||||||
|
namespace utils::http
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct progress_helper
|
||||||
|
{
|
||||||
|
const std::function<void(size_t)>* callback{};
|
||||||
|
std::exception_ptr exception{};
|
||||||
|
};
|
||||||
|
|
||||||
|
int progress_callback(void* clientp, const curl_off_t /*dltotal*/, const curl_off_t dlnow, const curl_off_t /*ultotal*/, const curl_off_t /*ulnow*/)
|
||||||
|
{
|
||||||
|
auto* helper = static_cast<progress_helper*>(clientp);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (*helper->callback)
|
||||||
|
{
|
||||||
|
(*helper->callback)(dlnow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
helper->exception = std::current_exception();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp)
|
||||||
|
{
|
||||||
|
auto* buffer = static_cast<std::string*>(userp);
|
||||||
|
|
||||||
|
const auto total_size = size * nmemb;
|
||||||
|
buffer->append(static_cast<char*>(contents), total_size);
|
||||||
|
return total_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> get_data(const std::string& url, const headers& headers, const std::function<void(size_t)>& callback)
|
||||||
|
{
|
||||||
|
curl_slist* header_list = nullptr;
|
||||||
|
auto* curl = curl_easy_init();
|
||||||
|
if (!curl)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _ = gsl::finally([&]()
|
||||||
|
{
|
||||||
|
curl_slist_free_all(header_list);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto& header : headers)
|
||||||
|
{
|
||||||
|
auto data = header.first + ": " + header.second;
|
||||||
|
header_list = curl_slist_append(header_list, data.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buffer{};
|
||||||
|
progress_helper helper{};
|
||||||
|
helper.callback = &callback;
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.data());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||||
|
|
||||||
|
if (curl_easy_perform(curl) == CURLE_OK)
|
||||||
|
{
|
||||||
|
return {std::move(buffer)};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper.exception)
|
||||||
|
{
|
||||||
|
std::rethrow_exception(helper.exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
||||||
|
{
|
||||||
|
return std::async(std::launch::async, [url, headers]()
|
||||||
|
{
|
||||||
|
return get_data(url, headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
13
src/utils/http.hpp
Normal file
13
src/utils/http.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
namespace utils::http
|
||||||
|
{
|
||||||
|
using headers = std::unordered_map<std::string, std::string>;
|
||||||
|
|
||||||
|
std::optional<std::string> get_data(const std::string& url, const headers& headers = {}, const std::function<void(size_t)>& callback = {});
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
|
||||||
|
}
|
118
src/utils/io.cpp
Normal file
118
src/utils/io.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include "io.hpp"
|
||||||
|
|
||||||
|
namespace utils::io
|
||||||
|
{
|
||||||
|
bool remove_file(const std::string& file)
|
||||||
|
{
|
||||||
|
return DeleteFileA(file.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<uint32_t>(size));
|
||||||
|
stream.read(const_cast<char*>(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<size_t>(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<std::string> list_files(const std::string& directory)
|
||||||
|
{
|
||||||
|
std::vector<std::string> 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);
|
||||||
|
}
|
||||||
|
}
|
20
src/utils/io.hpp
Normal file
20
src/utils/io.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace utils::io
|
||||||
|
{
|
||||||
|
bool remove_file(const std::string& file);
|
||||||
|
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<std::string> list_files(const std::string& directory);
|
||||||
|
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||||
|
}
|
109
src/utils/memory.cpp
Normal file
109
src/utils/memory.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "memory.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<void*>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<char>(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<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_set(const void* mem, const char chr, const size_t length)
|
||||||
|
{
|
||||||
|
const auto mem_arr = static_cast<const char*>(mem);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (mem_arr[i] != chr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory::allocator* memory::get_allocator()
|
||||||
|
{
|
||||||
|
return &memory::mem_allocator_;
|
||||||
|
}
|
||||||
|
}
|
75
src/utils/memory.hpp
Normal file
75
src/utils/memory.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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 <typename T>
|
||||||
|
inline T* allocate()
|
||||||
|
{
|
||||||
|
return this->allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::vector<void*> pool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* allocate(size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate()
|
||||||
|
{
|
||||||
|
return allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(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_;
|
||||||
|
};
|
||||||
|
}
|
172
src/utils/string.cpp
Normal file
172
src/utils/string.cpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "string.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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<std::string> split(const std::string& s, const char delim)
|
||||||
|
{
|
||||||
|
std::stringstream ss(s);
|
||||||
|
std::string item;
|
||||||
|
std::vector<std::string> 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<char>(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<char>(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());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_numeric(const std::string& text)
|
||||||
|
{
|
||||||
|
return std::to_string(atoi(text.data())) == text;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<char>(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<wchar_t>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_timestamp()
|
||||||
|
{
|
||||||
|
tm ltime{};
|
||||||
|
char timestamp[MAX_PATH] = { 0 };
|
||||||
|
const auto time = _time64(nullptr);
|
||||||
|
|
||||||
|
_localtime64_s(<ime, &time);
|
||||||
|
strftime(timestamp, sizeof(timestamp) - 1, "%Y-%m-%d-%H-%M-%S", <ime);
|
||||||
|
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
101
src/utils/string.hpp
Normal file
101
src/utils/string.hpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "memory.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifndef ARRAYSIZE
|
||||||
|
template <class Type, size_t n>
|
||||||
|
size_t ARRAYSIZE(Type(&)[n]) { return n; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace utils::string
|
||||||
|
{
|
||||||
|
template <size_t Buffers, size_t MinBufferSize>
|
||||||
|
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<char>(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<std::string> 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);
|
||||||
|
bool is_numeric(const std::string& text);
|
||||||
|
|
||||||
|
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::string get_timestamp();
|
||||||
|
}
|
89
src/utils/thread.cpp
Normal file
89
src/utils/thread.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "thread.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
#include <TlHelp32.h>
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
namespace utils::thread
|
||||||
|
{
|
||||||
|
std::vector<DWORD> get_thread_ids()
|
||||||
|
{
|
||||||
|
auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([h]()
|
||||||
|
{
|
||||||
|
CloseHandle(h);
|
||||||
|
});
|
||||||
|
|
||||||
|
THREADENTRY32 entry{};
|
||||||
|
entry.dwSize = sizeof(entry);
|
||||||
|
if (!Thread32First(h, &entry))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> 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<void(HANDLE)>& 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 _ = gsl::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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
19
src/utils/thread.hpp
Normal file
19
src/utils/thread.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace utils::thread
|
||||||
|
{
|
||||||
|
template <typename ...Args>
|
||||||
|
std::thread create_named_thread(const std::string& name, Args&&... args)
|
||||||
|
{
|
||||||
|
auto t = std::thread(std::forward<Args>(args)...);
|
||||||
|
set_name(t, name);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> get_thread_ids();
|
||||||
|
void for_each_thread(const std::function<void(HANDLE)>& callback);
|
||||||
|
|
||||||
|
void suspend_other_threads();
|
||||||
|
void resume_other_threads();
|
||||||
|
}
|
BIN
tools/windows/premake5.exe
Normal file
BIN
tools/windows/premake5.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user