Init iw4x base

This commit is contained in:
ineedbots 2021-06-28 12:13:34 -06:00
commit 0c2c478990
21 changed files with 1460 additions and 0 deletions

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.hpp eol=crlf
*.cpp eol=crlf
*.lua eol=crlf
*.proto eol=crlf

153
.gitignore vendored Normal file
View File

@ -0,0 +1,153 @@
### Windows
# 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
*.idb
*.i64
ida/*
### Custom user files
# User scripts
user*.bat
# Premake binary
#premake5.exe

4
generate.bat Normal file
View File

@ -0,0 +1,4 @@
@echo off
echo Updating submodules...
call git submodule update --init --recursive
call tools\premake5 %* vs2019

317
premake5.lua Normal file
View File

@ -0,0 +1,317 @@
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)
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 to an array of numbers {X,X,X}.
-- In the case where the format does not work fall back to old {4,2,REVISION}.
function vertonumarr(value, vernumber)
vernum = {}
for num in string.gmatch(value, "%d+") do
table.insert(vernum, tonumber(num))
end
if #vernum < 3 then
return {4,2,tonumber(vernumber)}
end
return vernum
end
-- Option to allow copying the DLL file to a custom folder after build
newoption {
trigger = "copy-to",
description = "Optional, copy the DLL to a custom folder after build, define the path here if wanted.",
value = "PATH"
}
newoption {
trigger = "no-new-structure",
description = "Do not use new virtual path structure (separating headers and source files)."
}
newoption {
trigger = "copy-pdb",
description = "Copy debug information for binaries as well to the path given via --copy-to."
}
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 revision number via git
local proc = assert(io.popen("git rev-list --count HEAD", "r"))
local revNumber = assert(proc:read('*a')):gsub("%s+", "")
-- get current version via git
local proc = assert(io.popen(gitVersioningCommand, "r"))
local gitDescribeOutput = 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 = assert(proc:read('*l'))
-- 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
-- generate version.hpp with a revision number if not equal
gitDescribeOutputQuoted = cstrquote(gitDescribeOutput)
if oldVersion ~= gitDescribeOutputQuoted then
print ("Update " .. oldVersion .. " -> " .. gitDescribeOutputQuoted)
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, you fucking moron!\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_TAG " .. cstrquote(tagName) .. "\n")
versionHeader:write("\n")
versionHeader:write("// Legacy definitions (needed for update check)\n")
versionHeader:write("#define REVISION " .. revNumber .. "\n")
versionHeader:write("\n")
versionHeader:write("// Version transformed for RC files\n")
versionHeader:write("#define VERSION_RC " .. table.concat(vertonumarr(tagName, revNumber), ",") .. "\n")
versionHeader:write("\n")
versionHeader:write("// Alias definitions\n")
versionHeader:write("#define VERSION GIT_DESCRIBE\n")
versionHeader:write("#define SHORTVERSION " .. cstrquote(table.concat(vertonumarr(tagName, revNumber), ".")) .. "\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, you fucking moron!\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
}
workspace "cod2m"
startproject "cod2m"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
buildlog "%{wks.location}/obj/%{cfg.architecture}/%{cfg.buildcfg}/%{prj.name}/%{prj.name}.log"
configurations { "Debug", "Release" }
architecture "x86"
platforms "x86"
--exceptionhandling ("SEH")
staticruntime "On"
configuration "windows"
defines { "_WINDOWS", "WIN32" }
configuration "Release*"
defines { "NDEBUG" }
flags { "MultiProcessorCompile", "LinkTimeOptimization", "No64BitChecks" }
optimize "On"
rtti ("Off")
configuration "Debug*"
defines { "DEBUG", "_DEBUG" }
flags { "MultiProcessorCompile", "No64BitChecks" }
optimize "Debug"
if symbols ~= nil then
symbols "On"
else
flags { "Symbols" }
end
project "d3d9"
kind "SharedLib"
language "C++"
files {
"./src/**.rc",
"./src/**.hpp",
"./src/**.cpp",
--"./src/**.proto",
}
includedirs {
"%{prj.location}/src",
"./src",
"./lib/include",
}
syslibdirs {
"./lib/bin",
}
resincludedirs {
"$(ProjectDir)src" -- fix for VS IDE
}
-- Pre-compiled header
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
pchsource "src/STDInclude.cpp" -- real path
buildoptions { "/Zm200" }
-- fix vpaths for protobuf sources
vpaths
{
["*"] = { "./src/**" },
--["Proto/Generated"] = { "**.pb.*" }, -- meh.
}
-- Virtual paths
if not _OPTIONS["no-new-structure"] then
vpaths
{
["Headers/*"] = { "./src/**.hpp" },
["Sources/*"] = { "./src/**.cpp" },
["Resource/*"] = { "./src/**.rc" },
--["Proto/Definitions/*"] = { "./src/Proto/**.proto" },
--["Proto/Generated/*"] = { "**.pb.*" }, -- meh.
}
end
vpaths
{
["Docs/*"] = { "**.txt","**.md" },
}
-- Pre-build
prebuildcommands
{
"pushd %{_MAIN_SCRIPT_DIR}",
"tools\\premake5 generate-buildinfo",
"popd",
}
-- Post-build
if _OPTIONS["copy-to"] then
saneCopyToPath = string.gsub(_OPTIONS["copy-to"] .. "\\", "\\\\", "\\")
postbuildcommands {
"if not exist \"" .. saneCopyToPath .. "\" mkdir \"" .. saneCopyToPath .. "\"",
}
if _OPTIONS["copy-pdb"] then
postbuildcommands {
"copy /y \"$(TargetDir)*.pdb\" \"" .. saneCopyToPath .. "\"",
}
end
-- This has to be the last one, as otherwise VisualStudio will succeed building even if copying fails
postbuildcommands {
"copy /y \"$(TargetDir)*.dll\" \"" .. saneCopyToPath .. "\"",
}
end
-- Specific configurations
flags { "UndefinedIdentifiers" }
warnings "Extra"
if symbols ~= nil then
symbols "On"
else
flags { "Symbols" }
end
configuration "Release*"
flags {
"FatalCompileWarnings",
"FatalLinkWarnings",
}
configuration {}
--[[
-- Generate source code from protobuf definitions
rules { "ProtobufCompiler" }
-- Workaround: Consume protobuf generated source files
matches = os.matchfiles(path.join("src/Proto/**.proto"))
for i, srcPath in ipairs(matches) do
basename = path.getbasename(srcPath)
files
{
string.format("%%{prj.location}/src/proto/%s.pb.h", basename),
string.format("%%{prj.location}/src/proto/%s.pb.cc", basename),
}
end
includedirs
{
"%{prj.location}/src/proto",
}
filter "files:**.pb.*"
flags {
"NoPCH",
}
buildoptions {
"/wd4100", -- "Unused formal parameter"
"/wd4389", -- "Signed/Unsigned mismatch"
"/wd6011", -- "Dereferencing NULL pointer"
"/wd4125", -- "Decimal digit terminates octal escape sequence"
}
defines {
"_SCL_SECURE_NO_WARNINGS",
}
filter {}
]]
workspace "*"
buildoptions {
"/std:c++latest"
}
systemversion "latest"
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }

9
src/Game/MP.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "STDInclude.hpp"
namespace MP
{
void PatchT4()
{
MessageBoxA(nullptr, "MP", "DEBUG", 0);
}
}

6
src/Game/MP.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace MP
{
void PatchT4();
}

View File

0
src/Game/MP/Structs.hpp Normal file
View File

9
src/Game/SP.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "STDInclude.hpp"
namespace SP
{
void PatchT4()
{
MessageBoxA(nullptr, "SP", "DEBUG", 0);
}
}

6
src/Game/SP.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace SP
{
void PatchT4();
}

View File

0
src/Game/SP/Structs.hpp Normal file
View File

89
src/Main.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "STDInclude.hpp"
namespace Main
{
static BYTE originalCode[5];
static PBYTE originalEP = 0;
void UnprotectModule(HMODULE hModule)
{
PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew);
// unprotect the entire PE image
SIZE_T size = ntHeader->OptionalHeader.SizeOfImage;
DWORD oldProtect;
VirtualProtect((LPVOID)hModule, size, PAGE_EXECUTE_READWRITE, &oldProtect);
}
void DoInit()
{
// return to the original EP
memcpy(originalEP, &originalCode, sizeof(originalCode));
// unprotect our entire PE image
HMODULE hModule;
if (SUCCEEDED(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)DoInit, &hModule)))
{
UnprotectModule(hModule);
}
// detect which executable's patches to apply
DWORD dataStrData = Utils::Hook::Get<DWORD>(0x881CAC);
if (dataStrData == 0x62616E55)
MP::PatchT4();
else
SP::PatchT4();
hModule = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew);
// back up original code
originalEP = (PBYTE)((DWORD)hModule + ntHeader->OptionalHeader.AddressOfEntryPoint);
__asm
{
jmp originalEP
}
}
void SetSafeInit()
{
// find the entry point for the executable process, set page access, and replace the EP
HMODULE hModule = GetModuleHandle(NULL); // passing NULL should be safe even with the loader lock being held (according to ReactOS ldr.c)
if (hModule)
{
PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + header->e_lfanew);
UnprotectModule(hModule);
// back up original code
PBYTE ep = (PBYTE)((DWORD)hModule + ntHeader->OptionalHeader.AddressOfEntryPoint);
memcpy(originalCode, ep, sizeof(originalCode));
// patch to call our EP
int newEP = (int)DoInit - ((int)ep + 5);
ep[0] = 0xE9; // for some reason this doesn't work properly when run under the debugger
memcpy(&ep[1], &newEP, 4);
originalEP = ep;
}
}
}
bool APIENTRY DllMain(HMODULE, DWORD dwReason, LPVOID)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DWORD textSegData = Utils::Hook::Get<DWORD>(0x401000);
// detect for all executables to hook into
//if (textSegData == 0x9EF490B8 || textSegData == 0x83EC8B55)
Main::SetSafeInit();
}
return true;
}

97
src/Resource.rc Normal file
View File

@ -0,0 +1,97 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "STDInclude.hpp"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#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
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "CoD2m"
#ifdef _DEBUG
VALUE "FileDescription", "CoD2 client/server modification (DEBUG)"
#else
VALUE "FileDescription", "CoD2 client/server modification"
#endif
VALUE "FileVersion", SHORTVERSION
VALUE "InternalName", "cod2m"
VALUE "LegalCopyright", "All rights reserved."
VALUE "OriginalFilename", "d3d9.dll"
VALUE "ProductName", "CoD2m"
VALUE "ProductVersion", SHORTVERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

90
src/SDLLP.cpp Normal file
View File

@ -0,0 +1,90 @@
// --------------------------------------+
// System Dynamic Link Library Proxy
// by momo5502
// --------------------------------------+
#include "STDInclude.hpp"
// Macro to declare an export
// --------------------------------------+
#define EXPORT(_export) extern "C" __declspec(naked) __declspec(dllexport) void _export() { static FARPROC function = 0; if(!function) function = SDLLP::GetExport(__FUNCTION__, LIBRARY); __asm { jmp function } }
// Static class
// --------------------------------------+
class SDLLP
{
private:
static std::map<std::string, HINSTANCE> mLibraries;
static void Log(const char* message, ...);
static void LoadLibrary(const char* library);
static bool IsLibraryLoaded(const char* library);
public:
static FARPROC GetExport(const char* function, const char* library);
};
// Class variable declarations
// --------------------------------------+
std::map<std::string, HINSTANCE> SDLLP::mLibraries;
// Load necessary library
// --------------------------------------+
void SDLLP::LoadLibrary(const char* library)
{
Log("[SDLLP] Loading library '%s'.", library);
CHAR mPath[MAX_PATH];
GetSystemDirectoryA(mPath, MAX_PATH);
strcat_s(mPath, "\\");
strcat_s(mPath, library);
mLibraries[library] = ::LoadLibraryA(mPath);
if (!IsLibraryLoaded(library)) Log("[SDLLP] Unable to load library '%s'.", library);
}
// Check if export already loaded
// --------------------------------------+
bool SDLLP::IsLibraryLoaded(const char* library)
{
return (mLibraries.find(library) != mLibraries.end() && mLibraries[library]);
}
// Get export address
// --------------------------------------+
FARPROC SDLLP::GetExport(const char* function, const char* library)
{
Log("[SDLLP] Export '%s' requested from %s.", function, library);
if (!IsLibraryLoaded(library)) LoadLibrary(library);
FARPROC address = GetProcAddress(mLibraries[library], function);
if (!address) Log("[SDLLP] Unable to load export '%s' from library '%s'.", function, library);
return address;
}
// Write debug string
// --------------------------------------+
void SDLLP::Log(const char* message, ...)
{
CHAR buffer[1024];
va_list ap;
va_start(ap, message);
vsprintf(buffer, message, ap);
va_end(ap);
OutputDebugStringA(buffer);
}
// --------------------------------------+
// Adapt export functions and library
// --------------------------------------+
#define LIBRARY "d3d9.dll"
EXPORT(D3DPERF_BeginEvent)
EXPORT(D3DPERF_EndEvent)
EXPORT(Direct3DCreate9)

115
src/STDInclude.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "STDInclude.hpp"
// Rename sections
#ifndef DEBUG
#pragma comment(linker, "/merge:.text=.UPX0")
#pragma comment(linker, "/merge:.data=.UPX1")
#pragma comment(linker, "/merge:.rdata=.UPX2")
#pragma comment(linker, "/merge:.tls=.UPX3")
#pragma comment(linker, "/merge:.gfids=.UPX4")
#endif
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Do necessary assertions here
// Some compilers treat them differently which causes a size mismatch
// WinAPI types
AssertSize(DWORD, 4);
AssertSize(WORD, 2);
AssertSize(BYTE, 1);
// 128 bit integers (only x64)
//AssertSize(__int128, 16);
//AssertSize(unsigned __int128, 16);
// 64 bit integers
AssertSize(__int64, 8);
AssertSize(unsigned __int64, 8);
AssertSize(long long, 8);
AssertSize(unsigned long long, 8);
AssertSize(int64_t, 8);
AssertSize(uint64_t, 8);
AssertSize(std::int64_t, 8);
AssertSize(std::uint64_t, 8);
// 64 bit double precision floating point numbers
AssertSize(double, 8);
// 32 bit integers
AssertSize(__int32, 4);
AssertSize(unsigned __int32, 4);
AssertSize(int, 4);
AssertSize(unsigned int, 4);
AssertSize(long, 4);
AssertSize(unsigned long, 4);
AssertSize(int32_t, 4);
AssertSize(uint32_t, 4);
AssertSize(std::int32_t, 4);
AssertSize(std::uint32_t, 4);
// 32 bit single precision floating point numbers
AssertSize(float, 4);
// 16 bit integers
AssertSize(__int16, 2);
AssertSize(unsigned __int16, 2);
AssertSize(short, 2);
AssertSize(unsigned short, 2);
AssertSize(int16_t, 2);
AssertSize(uint16_t, 2);
AssertSize(std::int16_t, 2);
AssertSize(std::uint16_t, 2);
// 8 bit integers
AssertSize(bool, 1);
AssertSize(__int8, 1);
AssertSize(unsigned __int8, 1);
AssertSize(char, 1);
AssertSize(unsigned char, 1);
AssertSize(int8_t, 1);
AssertSize(uint8_t, 1);
AssertSize(std::int8_t, 1);
AssertSize(std::uint8_t, 1);
// Ensure pointers are 4 bytes in size (32-bit)
// ReSharper disable CppRedundantBooleanExpressionArgument
static_assert(sizeof(intptr_t) == 4 && sizeof(void*) == 4 && sizeof(size_t) == 4, "This doesn't seem to be a 32-bit environment!");
// ReSharper restore CppRedundantBooleanExpressionArgument
#if !defined(_M_IX86)
#error "Invalid processor achritecture!"
#endif
extern "C"
{
// Disable telemetry data logging
void __cdecl __vcrt_initialize_telemetry_provider() {}
void __cdecl __telemetry_main_invoke_trigger() {}
void __cdecl __telemetry_main_return_trigger() {}
void __cdecl __vcrt_uninitialize_telemetry_provider() {}
// Enable 'High Performance Graphics'
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
// Tommath fixes
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;
}
};

120
src/STDInclude.hpp Normal file
View File

@ -0,0 +1,120 @@
#pragma once
// Version number
#include "version.h"
#ifndef RC_INVOKED
#define _HAS_CXX17 1
#define _HAS_CXX20 1
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
#define VLD_FORCE_ENABLE
//#include <vld.h>
#pragma warning(disable: 4740)
#include <windows.h>
#include <timeapi.h>
#include <shellapi.h>
#include <Wininet.h>
#include <Aclapi.h>
#include <Psapi.h>
#include <tlhelp32.h>
#include <Shlwapi.h>
#pragma warning(push)
#pragma warning(disable: 4091)
#pragma warning(disable: 4244)
#include <dbghelp.h>
#include <sstream>
#include <fstream>
#include <cctype>
#include <regex>
#include <thread>
#include <future>
#include <map>
#include <queue>
// Experimental C++17 features
#include <filesystem>
#include <optional>
#pragma warning(pop)
// Usefull for debugging
template <size_t S> class Sizer { };
#define BindNum(x, y) Sizer<x> y;
#define Size_Of(x, y) BindNum(sizeof(x), y)
#define Offset_Of(x, y, z) BindNum(offsetof(x, y), z)
// Submodules
// Ignore the warnings, it's not our code!
#pragma warning(push)
#pragma warning(disable: 4005)
#pragma warning(disable: 4091)
#pragma warning(disable: 4100)
#pragma warning(disable: 4244)
#pragma warning(disable: 4389)
#pragma warning(disable: 4702)
#pragma warning(disable: 4800)
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 5054)
#pragma warning(disable: 6001)
#pragma warning(disable: 6011)
#pragma warning(disable: 6031)
#pragma warning(disable: 6255)
#pragma warning(disable: 6258)
#pragma warning(disable: 6386)
#pragma warning(disable: 6387)
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#pragma warning(pop)
#include "Utils/Hooking.hpp"
#include "Game/MP.hpp"
#include "Game/SP.hpp"
// Libraries
#pragma comment(lib, "Winmm.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Wininet.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "Urlmon.lib")
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "rpcrt4.lib")
#pragma comment(lib, "dbghelp.lib")
// Enable additional literals
using namespace std::literals;
#endif
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.")
#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.")
// Resource stuff
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
// Defines below make accessing the resources from the code easier.
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

256
src/Utils/Hooking.cpp Normal file
View File

@ -0,0 +1,256 @@
#include "STDInclude.hpp"
namespace Utils
{
std::map<void*, void*> Hook::Interceptor::IReturn;
std::map<void*, void(*)()> Hook::Interceptor::ICallbacks;
void Hook::Signature::process()
{
if (this->signatures.empty()) return;
char* _start = reinterpret_cast<char*>(this->start);
unsigned int sigCount = this->signatures.size();
Hook::Signature::Container* containers = this->signatures.data();
for (size_t i = 0; i < this->length; ++i)
{
char* address = _start + i;
for (unsigned int k = 0; k < sigCount; ++k)
{
Hook::Signature::Container* container = &containers[k];
unsigned int j;
for (j = 0; j < strlen(container->mask); ++j)
{
if (container->mask[j] != '?' &&container->signature[j] != address[j])
{
break;
}
}
if (j == strlen(container->mask))
{
container->callback(address);
}
}
}
}
void Hook::Signature::add(Hook::Signature::Container& container)
{
Hook::Signature::signatures.push_back(container);
}
void Hook::Interceptor::Install(void* place, void* stub)
{
return Hook::Interceptor::Install(place, static_cast<void(*)()>(stub));
}
void Hook::Interceptor::Install(void* place, void(*stub)())
{
return Hook::Interceptor::Install(reinterpret_cast<void**>(place), stub);
}
void Hook::Interceptor::Install(void** place, void(*stub)())
{
Hook::Interceptor::IReturn[place] = *place;
Hook::Interceptor::ICallbacks[place] = stub;
*place = Hook::Interceptor::InterceptionStub;
}
__declspec(naked) void Hook::Interceptor::InterceptionStub()
{
__asm
{
sub esp, 4h // Reserve space on the stack for the return address
pushad // Store registers
lea eax, [esp + 20h] // Load initial stack pointer
push eax // Push it onto the stack
call Hook::Interceptor::RunCallback // Run the callback based on the given stack pointer
call Hook::Interceptor::PopReturn // Get the initial return address according to the stack pointer
add esp, 4h // Clear the stack
mov [esp + 20h], eax // Store the return address at the reserved space
popad // Restore the registers
retn // Return (jump to our return address)
}
}
void Hook::Interceptor::RunCallback(void* place)
{
auto iCallback = Hook::Interceptor::ICallbacks.find(place);
if (iCallback != Hook::Interceptor::ICallbacks.end())
{
iCallback->second();
Hook::Interceptor::ICallbacks.erase(iCallback);
}
}
void* Hook::Interceptor::PopReturn(void* _place)
{
void* retVal = nullptr;
auto iReturn = Hook::Interceptor::IReturn.find(_place);
if (iReturn != Hook::Interceptor::IReturn.end())
{
retVal = iReturn->second;
Hook::Interceptor::IReturn.erase(iReturn);
}
return retVal;
}
Hook::~Hook()
{
if (this->initialized)
{
this->uninstall();
}
}
Hook* Hook::initialize(DWORD _place, void(*_stub)(), bool _useJump)
{
return this->initialize(_place, reinterpret_cast<void*>(_stub), _useJump);
}
Hook* Hook::initialize(DWORD _place, void* _stub, bool _useJump)
{
return this->initialize(reinterpret_cast<void*>(_place), _stub, _useJump);
}
Hook* Hook::initialize(void* _place, void* _stub, bool _useJump)
{
if (this->initialized) return this;
this->initialized = true;
this->useJump = _useJump;
this->place = _place;
this->stub = _stub;
this->original = static_cast<char*>(this->place) + 5 + *reinterpret_cast<DWORD*>((static_cast<char*>(this->place) + 1));
return this;
}
Hook* Hook::install(bool unprotect, bool keepUnprotected)
{
std::lock_guard<std::mutex> _(this->stateMutex);
if (!this->initialized || this->installed)
{
return this;
}
this->installed = true;
if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection);
std::memcpy(this->buffer, this->place, sizeof(this->buffer));
char* code = static_cast<char*>(this->place);
*code = static_cast<char>(this->useJump ? 0xE9 : 0xE8);
*reinterpret_cast<size_t*>(code + 1) = reinterpret_cast<size_t>(this->stub) - (reinterpret_cast<size_t>(this->place) + 5);
if (unprotect && !keepUnprotected) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection);
FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer));
return this;
}
void Hook::quick()
{
if (Hook::installed)
{
Hook::installed = false;
}
}
Hook* Hook::uninstall(bool unprotect)
{
std::lock_guard<std::mutex> _(this->stateMutex);
if (!this->initialized || !this->installed)
{
return this;
}
this->installed = false;
if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection);
std::memcpy(this->place, this->buffer, sizeof(this->buffer));
if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection);
FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer));
return this;
}
void* Hook::getAddress()
{
return this->place;
}
void Hook::Nop(void* place, size_t length)
{
DWORD oldProtect;
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &oldProtect);
memset(place, 0x90, length);
VirtualProtect(place, length, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, length);
}
void Hook::Nop(DWORD place, size_t length)
{
Nop(reinterpret_cast<void*>(place), length);
}
void Hook::SetString(void* place, const char* string, size_t length)
{
DWORD oldProtect;
VirtualProtect(place, length + 1, PAGE_EXECUTE_READWRITE, &oldProtect);
strncpy_s(static_cast<char*>(place), length, string, length);
VirtualProtect(place, length + 1, oldProtect, &oldProtect);
}
void Hook::SetString(DWORD place, const char* string, size_t length)
{
Hook::SetString(reinterpret_cast<void*>(place), string, length);
}
void Hook::SetString(void* place, const char* string)
{
Hook::SetString(place, string, strlen(static_cast<char*>(place)));
}
void Hook::SetString(DWORD place, const char* string)
{
Hook::SetString(reinterpret_cast<void*>(place), string);
}
void Hook::RedirectJump(void* place, void* stub)
{
char* operandPtr = static_cast<char*>(place) + 2;
int newOperand = reinterpret_cast<int>(stub) - (reinterpret_cast<int>(place) + 6);
Utils::Hook::Set<int>(operandPtr, newOperand);
}
void Hook::RedirectJump(DWORD place, void* stub)
{
Hook::RedirectJump(reinterpret_cast<void*>(place), stub);
}
}

185
src/Utils/Hooking.hpp Normal file
View File

@ -0,0 +1,185 @@
#pragma once
#define HOOK_JUMP true
#define HOOK_CALL false
namespace Utils
{
class Hook
{
public:
class Signature
{
public:
struct Container
{
const char* signature;
const char* mask;
std::function<void(char*)> callback;
};
Signature(void* _start, size_t _length) : start(_start), length(_length) {}
Signature(DWORD _start, size_t _length) : Signature(reinterpret_cast<void*>(_start), _length) {}
Signature() : Signature(0x400000, 0x800000) {}
void process();
void add(Container& container);
private:
void* start;
size_t length;
std::vector<Container> signatures;
};
class Interceptor
{
public:
static void Install(void* place, void* stub);
static void Install(void* place, void(*stub)());
static void Install(void** place, void(*stub)());
private:
static std::map<void*, void*> IReturn;
static std::map<void*, void(*)()> ICallbacks;
static void InterceptionStub();
static void RunCallback(void* place);
static void* PopReturn(void* place);
};
Hook() : initialized(false), installed(false), place(nullptr), stub(nullptr), original(nullptr), useJump(false), protection(0) { ZeroMemory(this->buffer, sizeof(this->buffer)); }
Hook(void* place, void* stub, bool useJump = true) : Hook() { this->initialize(place, stub, useJump); }
Hook(void* place, void(*stub)(), bool useJump = true) : Hook(place, reinterpret_cast<void*>(stub), useJump) {}
Hook(DWORD place, void* stub, bool useJump = true) : Hook(reinterpret_cast<void*>(place), stub, useJump) {}
Hook(DWORD place, DWORD stub, bool useJump = true) : Hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), useJump) {}
Hook(DWORD place, void(*stub)(), bool useJump = true) : Hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), useJump) {}
~Hook();
Hook* initialize(void* place, void* stub, bool useJump = true);
Hook* initialize(DWORD place, void* stub, bool useJump = true);
Hook* initialize(DWORD place, void(*stub)(), bool useJump = true); // For lambdas
Hook* install(bool unprotect = true, bool keepUnprotected = false);
Hook* uninstall(bool unprotect = true);
void* getAddress();
void quick();
template <typename T> static std::function<T> Call(DWORD function)
{
return std::function<T>(reinterpret_cast<T*>(function));
}
template <typename T> static std::function<T> Call(FARPROC function)
{
return Call<T>(reinterpret_cast<DWORD>(function));
}
template <typename T> static std::function<T> Call(void* function)
{
return Call<T>(reinterpret_cast<DWORD>(function));
}
static void SetString(void* place, const char* string, size_t length);
static void SetString(DWORD place, const char* string, size_t length);
static void SetString(void* place, const char* string);
static void SetString(DWORD place, const char* string);
static void Nop(void* place, size_t length);
static void Nop(DWORD place, size_t length);
static void RedirectJump(void* place, void* stub);
static void RedirectJump(DWORD place, void* stub);
template <typename T> static void Set(void* place, T value)
{
DWORD oldProtect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
*static_cast<T*>(place) = value;
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
}
template <typename T> static void Set(DWORD place, T value)
{
return Set<T>(reinterpret_cast<void*>(place), value);
}
template <typename T> static void Xor(void* place, T value)
{
DWORD oldProtect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
*static_cast<T*>(place) ^= value;
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
}
template <typename T> static void Xor(DWORD place, T value)
{
return Xor<T>(reinterpret_cast<void*>(place), value);
}
template <typename T> static void Or(void* place, T value)
{
DWORD oldProtect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
*static_cast<T*>(place) |= value;
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
}
template <typename T> static void Or(DWORD place, T value)
{
return Or<T>(reinterpret_cast<void*>(place), value);
}
template <typename T> static void And(void* place, T value)
{
DWORD oldProtect;
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &oldProtect);
*static_cast<T*>(place) &= value;
VirtualProtect(place, sizeof(T), oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
}
template <typename T> static void And(DWORD place, T value)
{
return And<T>(reinterpret_cast<void*>(place), value);
}
template <typename T> static T Get(void* place)
{
return *static_cast<T*>(place);
}
template <typename T> static T Get(DWORD place)
{
return Get<T>(reinterpret_cast<void*>(place));
}
private:
bool initialized;
bool installed;
void* place;
void* stub;
void* original;
char buffer[5];
bool useJump;
DWORD protection;
std::mutex stateMutex;
};
}

BIN
tools/premake5.exe Normal file

Binary file not shown.

BIN
tools/protoc.exe Normal file

Binary file not shown.