Initial commit
This commit is contained in:
parent
e5455a8f39
commit
680bc7fe66
63
.gitattributes
vendored
63
.gitattributes
vendored
@ -1,2 +1,63 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
|
150
.gitignore
vendored
Normal file
150
.gitignore
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
### 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
|
||||||
|
|
||||||
|
### Custom user files
|
||||||
|
# User scripts
|
||||||
|
user*.bat
|
||||||
|
|
||||||
|
# Premake binary
|
||||||
|
#premake5.exe
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "deps/minhook"]
|
||||||
|
path = deps/minhook
|
||||||
|
url = https://github.com/TsudaKageyu/minhook.git
|
1
deps/minhook
vendored
Submodule
1
deps/minhook
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 423d1e45af2ed2719a5c31e990e935ef301ed9c3
|
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)
|
3
generate.bat
Normal file
3
generate.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
git submodule update --init --recursive
|
||||||
|
tools\windows\premake5.exe vs2019
|
121
premake5.lua
Normal file
121
premake5.lua
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
gitVersioningCommand = "git describe --tags --dirty --always"
|
||||||
|
gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"
|
||||||
|
|
||||||
|
-- Quote the given string input as a C string
|
||||||
|
function cstrquote(value)
|
||||||
|
if value == nil then
|
||||||
|
return "\"\""
|
||||||
|
end
|
||||||
|
result = value:gsub("\\", "\\\\")
|
||||||
|
result = result:gsub("\"", "\\\"")
|
||||||
|
result = result:gsub("\n", "\\n")
|
||||||
|
result = result:gsub("\t", "\\t")
|
||||||
|
result = result:gsub("\r", "\\r")
|
||||||
|
result = result:gsub("\a", "\\a")
|
||||||
|
result = result:gsub("\b", "\\b")
|
||||||
|
result = "\"" .. result .. "\""
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Converts tags in "vX.X.X" format and given revision number Y to an array of numbers {X,X,X,Y}.
|
||||||
|
-- In the case where the format does not work fall back to padding with zeroes and just ending with the revision number.
|
||||||
|
-- partscount can be either 3 or 4.
|
||||||
|
function vertonumarr(value, vernumber, partscount)
|
||||||
|
vernum = {}
|
||||||
|
for num in string.gmatch(value or "", "%d+") do
|
||||||
|
if #vernum < 3 then
|
||||||
|
table.insert(vernum, tonumber(num))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while #vernum < 3 do
|
||||||
|
table.insert(vernum, 0)
|
||||||
|
end
|
||||||
|
if #vernum < partscount then
|
||||||
|
table.insert(vernum, tonumber(vernumber))
|
||||||
|
end
|
||||||
|
return vernum
|
||||||
|
end
|
||||||
|
|
||||||
|
dependencies = {
|
||||||
|
basePath = "./deps"
|
||||||
|
}
|
||||||
|
|
||||||
|
function dependencies.load()
|
||||||
|
dir = path.join(dependencies.basePath, "premake/*.lua")
|
||||||
|
deps = os.matchfiles(dir)
|
||||||
|
|
||||||
|
for i, dep in pairs(deps) do
|
||||||
|
dep = dep:gsub(".lua", "")
|
||||||
|
require(dep)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function dependencies.imports()
|
||||||
|
for i, proj in pairs(dependencies) do
|
||||||
|
if type(i) == 'number' then
|
||||||
|
proj.import()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function dependencies.projects()
|
||||||
|
for i, proj in pairs(dependencies) do
|
||||||
|
if type(i) == 'number' then
|
||||||
|
proj.project()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dependencies.load()
|
||||||
|
|
||||||
|
workspace "h2-mod"
|
||||||
|
location "./build"
|
||||||
|
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
||||||
|
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||||
|
targetname "%{prj.name}"
|
||||||
|
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
architecture "x64"
|
||||||
|
platforms "x64"
|
||||||
|
|
||||||
|
buildoptions "/std:c++latest"
|
||||||
|
systemversion "latest"
|
||||||
|
|
||||||
|
flags
|
||||||
|
{
|
||||||
|
"NoIncrementalLink",
|
||||||
|
"MultiProcessorCompile",
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations { "Debug", "Release", }
|
||||||
|
|
||||||
|
symbols "On"
|
||||||
|
|
||||||
|
configuration "Release"
|
||||||
|
optimize "Full"
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
configuration{}
|
||||||
|
|
||||||
|
configuration "Debug"
|
||||||
|
optimize "Debug"
|
||||||
|
defines { "DEBUG", "_DEBUG" }
|
||||||
|
configuration {}
|
||||||
|
|
||||||
|
startproject "h2-mod"
|
||||||
|
|
||||||
|
project "h2-mod"
|
||||||
|
kind "SharedLib"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
pchheader "stdinc.hpp"
|
||||||
|
pchsource "src/stdinc.cpp"
|
||||||
|
|
||||||
|
includedirs { "src" }
|
||||||
|
|
||||||
|
files { "src/**.h", "src/**.hpp", "src/**.cpp" }
|
||||||
|
|
||||||
|
dependencies.imports()
|
||||||
|
|
||||||
|
group "Dependencies"
|
||||||
|
dependencies.projects()
|
232
src/component/command.cpp
Normal file
232
src/component/command.cpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "command.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/memory.hpp>
|
||||||
|
|
||||||
|
namespace command
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::function<void(params&)>> handlers;
|
||||||
|
|
||||||
|
void main_handler()
|
||||||
|
{
|
||||||
|
params params = {};
|
||||||
|
|
||||||
|
const auto command = utils::string::to_lower(params[0]);
|
||||||
|
if (handlers.find(command) != handlers.end())
|
||||||
|
{
|
||||||
|
handlers[command](params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride)
|
||||||
|
{
|
||||||
|
game::DB_EnumXAssets_Internal(type, static_cast<void(*)(game::XAssetHeader, void*)>([](game::XAssetHeader header, void* data)
|
||||||
|
{
|
||||||
|
const auto& cb = *static_cast<const std::function<void(game::XAssetHeader)>*>(data);
|
||||||
|
cb(header);
|
||||||
|
}), &callback, includeOverride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params::params()
|
||||||
|
: nesting_(game::cmd_args->nesting)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int params::size() const
|
||||||
|
{
|
||||||
|
return game::cmd_args->argc[this->nesting_];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* params::get(const int index) const
|
||||||
|
{
|
||||||
|
if (index >= this->size())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return game::cmd_args->argv[this->nesting_][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string params::join(const int index) const
|
||||||
|
{
|
||||||
|
std::string result = {};
|
||||||
|
|
||||||
|
for (auto i = index; i < this->size(); i++)
|
||||||
|
{
|
||||||
|
if (i > index) result.append(" ");
|
||||||
|
result.append(this->get(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_raw(const char* name, void (*callback)())
|
||||||
|
{
|
||||||
|
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const char* name, const std::function<void(const params&)>& callback)
|
||||||
|
{
|
||||||
|
const auto command = utils::string::to_lower(name);
|
||||||
|
|
||||||
|
if (handlers.find(command) == handlers.end())
|
||||||
|
add_raw(name, main_handler);
|
||||||
|
|
||||||
|
handlers[command] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const char* name, const std::function<void()>& callback)
|
||||||
|
{
|
||||||
|
add(name, [callback](const params&)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(std::string command, const bool sync)
|
||||||
|
{
|
||||||
|
command += "\n";
|
||||||
|
|
||||||
|
if (sync)
|
||||||
|
{
|
||||||
|
game::Cmd_ExecuteSingleCommand(0, 0, command.data());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game::Cbuf_AddText(0, command.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
add("listassetpool", [](const params& params)
|
||||||
|
{
|
||||||
|
if (params.size() < 2)
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "listassetpool <poolnumber>: list all the assets in the specified pool\n");
|
||||||
|
|
||||||
|
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "%d %s\n", i, game::g_assetNames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto type = static_cast<game::XAssetType>(atoi(params.get(1)));
|
||||||
|
|
||||||
|
if (type < 0 || type >= game::XAssetType::ASSET_TYPE_COUNT)
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "Invalid pool passed must be between [%d, %d]\n", 0, game::XAssetType::ASSET_TYPE_COUNT - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_console::print(game_console::con_type_info, "Listing assets in pool %s\n", game::g_assetNames[type]);
|
||||||
|
|
||||||
|
enum_assets(type, [type](const game::XAssetHeader header)
|
||||||
|
{
|
||||||
|
const auto asset = game::XAsset{ type, header };
|
||||||
|
const auto* const asset_name = game::DB_GetXAssetName(&asset);
|
||||||
|
//const auto entry = game::DB_FindXAssetEntry(type, asset_name);
|
||||||
|
//TODO: display which zone the asset is from
|
||||||
|
game_console::print(game_console::con_type_info, "%s\n", asset_name);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add("baseAddress", []()
|
||||||
|
{
|
||||||
|
printf("%p\n", (void*)game::base_address);
|
||||||
|
});
|
||||||
|
|
||||||
|
add("commandDump", []()
|
||||||
|
{
|
||||||
|
printf("======== Start command dump =========\n");
|
||||||
|
|
||||||
|
game::cmd_function_s* cmd = (*game::cmd_functions);
|
||||||
|
|
||||||
|
while (cmd)
|
||||||
|
{
|
||||||
|
if (cmd->name)
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "%s\n", cmd->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = cmd->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("======== End command dump =========\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
/*add("noclip", [&]()
|
||||||
|
{
|
||||||
|
if (!game::SV_Loaded())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::sp::g_entities[0].client->flags ^= 1;
|
||||||
|
game::CG_GameMessage(0, utils::string::va("noclip %s",
|
||||||
|
game::sp::g_entities[0].client->flags & 1
|
||||||
|
? "^2on"
|
||||||
|
: "^1off"));
|
||||||
|
});
|
||||||
|
|
||||||
|
add("ufo", [&]()
|
||||||
|
{
|
||||||
|
if (!game::SV_Loaded())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::sp::g_entities[0].client->flags ^= 2;
|
||||||
|
game::CG_GameMessage(
|
||||||
|
0, utils::string::va("ufo %s", game::sp::g_entities[0].client->flags & 2 ? "^2on" : "^1off"));
|
||||||
|
});
|
||||||
|
|
||||||
|
add("give", [](const params& params)
|
||||||
|
{
|
||||||
|
if (!game::SV_Loaded())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() < 2)
|
||||||
|
{
|
||||||
|
game::CG_GameMessage(0, "You did not specify a weapon name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ps = game::SV_GetPlayerstateForClientNum(0);
|
||||||
|
auto wp = game::G_GetWeaponForName(params.get(1));
|
||||||
|
if (game::G_GivePlayerWeapon(ps, wp, 0, 0, 0))
|
||||||
|
{
|
||||||
|
game::G_InitializeAmmo(ps, wp, 0);
|
||||||
|
game::G_SelectWeapon(0, wp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add("take", [](const params& params)
|
||||||
|
{
|
||||||
|
if (!game::SV_Loaded())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() < 2)
|
||||||
|
{
|
||||||
|
game::CG_GameMessage(0, "You did not specify a weapon name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ps = game::SV_GetPlayerstateForClientNum(0);
|
||||||
|
auto wp = game::G_GetWeaponForName(params.get(1));
|
||||||
|
game::G_TakePlayerWeapon(ps, wp);
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
}
|
30
src/component/command.hpp
Normal file
30
src/component/command.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace command
|
||||||
|
{
|
||||||
|
class params
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
params();
|
||||||
|
|
||||||
|
int size() const;
|
||||||
|
const char* get(int index) const;
|
||||||
|
std::string join(int index) const;
|
||||||
|
|
||||||
|
const char* operator[](const int index) const
|
||||||
|
{
|
||||||
|
return this->get(index); //
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int nesting_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_raw(const char* name, void (*callback)());
|
||||||
|
void add(const char* name, const std::function<void(const params&)>& callback);
|
||||||
|
void add(const char* name, const std::function<void()>& callback);
|
||||||
|
|
||||||
|
void execute(std::string command, bool sync = false);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
}
|
685
src/component/game_console.cpp
Normal file
685
src/component/game_console.cpp
Normal file
@ -0,0 +1,685 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "game_console.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#define console_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 18)
|
||||||
|
#define material_white game::Material_RegisterHandle("white")
|
||||||
|
|
||||||
|
namespace game_console
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct console_globals
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float left_x;
|
||||||
|
float font_height;
|
||||||
|
bool may_auto_complete;
|
||||||
|
char auto_complete_choice[64];
|
||||||
|
int info_line_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ingame_console
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
int cursor;
|
||||||
|
int font_height;
|
||||||
|
int visible_line_count;
|
||||||
|
int visible_pixel_width;
|
||||||
|
float screen_min[2]; //left & top
|
||||||
|
float screen_max[2]; //right & bottom
|
||||||
|
console_globals globals;
|
||||||
|
bool output_visible;
|
||||||
|
int display_line_offset;
|
||||||
|
int line_count;
|
||||||
|
std::deque<std::string> output;
|
||||||
|
};
|
||||||
|
|
||||||
|
ingame_console con;
|
||||||
|
|
||||||
|
std::int32_t history_index = -1;
|
||||||
|
std::deque<std::string> history;
|
||||||
|
|
||||||
|
std::string fixed_input;
|
||||||
|
std::vector<std::string> matches;
|
||||||
|
|
||||||
|
float color_white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
|
float color_iw6[4] = { 0.0f, 0.7f, 1.0f, 1.0f };
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
strncpy_s(con.buffer, "", 256);
|
||||||
|
con.cursor = 0;
|
||||||
|
|
||||||
|
fixed_input = "";
|
||||||
|
matches.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const std::string& data)
|
||||||
|
{
|
||||||
|
if (con.visible_line_count > 0 && con.display_line_offset == (con.output.size() - con.visible_line_count))
|
||||||
|
{
|
||||||
|
con.display_line_offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
con.output.push_back(data);
|
||||||
|
|
||||||
|
printf("%s\n", data.data());
|
||||||
|
|
||||||
|
if (con.output.size() > 1024)
|
||||||
|
{
|
||||||
|
con.output.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle_console()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
|
con.output_visible = false;
|
||||||
|
*game::keyCatchers ^= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle_console_output()
|
||||||
|
{
|
||||||
|
con.output_visible = con.output_visible == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_resize()
|
||||||
|
{
|
||||||
|
con.screen_min[0] = 6.0f;
|
||||||
|
con.screen_min[1] = 6.0f;
|
||||||
|
con.screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 6.0f;
|
||||||
|
con.screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1] - 6.0f;
|
||||||
|
|
||||||
|
if (console_font)
|
||||||
|
{
|
||||||
|
con.font_height = console_font->pixelHeight;
|
||||||
|
con.visible_line_count = static_cast<int>((con.screen_max[1] - con.screen_min[1] - (con.font_height * 2)
|
||||||
|
) -
|
||||||
|
24.0f) / con.font_height;
|
||||||
|
con.visible_pixel_width = static_cast<int>(((con.screen_max[0] - con.screen_min[0]) - 10.0f) - 18.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
con.font_height = 0;
|
||||||
|
con.visible_line_count = 0;
|
||||||
|
con.visible_pixel_width = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_box(const float x, const float y, const float w, const float h, float* color)
|
||||||
|
{
|
||||||
|
game::vec4_t dark_color;
|
||||||
|
|
||||||
|
dark_color[0] = color[0] * 0.5f;
|
||||||
|
dark_color[1] = color[1] * 0.5f;
|
||||||
|
dark_color[2] = color[2] * 0.5f;
|
||||||
|
dark_color[3] = color[3];
|
||||||
|
|
||||||
|
game::R_AddCmdDrawStretchPic(x, y, w, h, 0.0f, 0.0f, 0.0f, 0.0f, color, material_white);
|
||||||
|
game::R_AddCmdDrawStretchPic(x, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white);
|
||||||
|
game::R_AddCmdDrawStretchPic((x + w) - 2.0f, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
|
||||||
|
material_white);
|
||||||
|
game::R_AddCmdDrawStretchPic(x, y, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white);
|
||||||
|
game::R_AddCmdDrawStretchPic(x, (y + h) - 2.0f, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
|
||||||
|
material_white);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_input_box(const int lines, float* color)
|
||||||
|
{
|
||||||
|
draw_box(
|
||||||
|
con.globals.x - 6.0f,
|
||||||
|
con.globals.y - 6.0f,
|
||||||
|
(con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]),
|
||||||
|
(lines * con.globals.font_height) + 12.0f,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_input_text_and_over(const char* str, float* color)
|
||||||
|
{
|
||||||
|
game::R_AddCmdDrawText(str, 0x7FFFFFFF, console_font, con.globals.x,
|
||||||
|
con.globals.y + con.globals.font_height, 1.0f,
|
||||||
|
1.0f, 0.0f, color, 0);
|
||||||
|
con.globals.x = game::R_TextWidth(str, 0, console_font) + con.globals.x + 6.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_hint_box(const int lines, float* color, [[maybe_unused]] float offset_x = 0.0f,
|
||||||
|
[[maybe_unused]] float offset_y = 0.0f)
|
||||||
|
{
|
||||||
|
const auto _h = lines * con.globals.font_height + 12.0f;
|
||||||
|
const auto _y = con.globals.y - 3.0f + con.globals.font_height + 12.0f;
|
||||||
|
const auto _w = (con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]);
|
||||||
|
|
||||||
|
draw_box(con.globals.x - 6.0f, _y, _w, _h, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_hint_text(const int line, const char* text, float* color, const float offset = 0.0f)
|
||||||
|
{
|
||||||
|
const auto _y = con.globals.font_height + con.globals.y + (con.globals.font_height * (line + 1)) + 15.0f;
|
||||||
|
|
||||||
|
game::R_AddCmdDrawText(text, 0x7FFFFFFF, console_font, con.globals.x + offset, _y, 1.0f, 1.0f, 0.0f, color, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match_compare(const std::string& input, const std::string& text, const bool exact)
|
||||||
|
{
|
||||||
|
if (exact && text == input) return true;
|
||||||
|
if (!exact && text.find(input) != std::string::npos) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void find_matches(std::string input, std::vector<std::string>& suggestions, const bool exact)
|
||||||
|
{
|
||||||
|
input = utils::string::to_lower(input);
|
||||||
|
|
||||||
|
game::cmd_function_s* cmd = (*game::cmd_functions);
|
||||||
|
while (cmd)
|
||||||
|
{
|
||||||
|
if (cmd->name)
|
||||||
|
{
|
||||||
|
std::string name = utils::string::to_lower(cmd->name);
|
||||||
|
|
||||||
|
if (match_compare(input, name, exact))
|
||||||
|
{
|
||||||
|
suggestions.push_back(cmd->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exact && suggestions.size() > 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd = cmd->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_input()
|
||||||
|
{
|
||||||
|
con.globals.font_height = static_cast<float>(console_font->pixelHeight);
|
||||||
|
con.globals.x = con.screen_min[0] + 6.0f;
|
||||||
|
con.globals.y = con.screen_min[1] + 6.0f;
|
||||||
|
con.globals.left_x = con.screen_min[0] + 6.0f;
|
||||||
|
|
||||||
|
draw_input_box(1, dvars::con_inputBoxColor->current.vector);
|
||||||
|
draw_input_text_and_over("h1-mod >", color_iw6);
|
||||||
|
|
||||||
|
con.globals.left_x = con.globals.x;
|
||||||
|
con.globals.auto_complete_choice[0] = 0;
|
||||||
|
|
||||||
|
game::R_AddCmdDrawText(con.buffer, 0x7FFFFFFF, console_font, con.globals.x,
|
||||||
|
con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0.0f, color_white, 0/*,
|
||||||
|
con.cursor, '|'*/);
|
||||||
|
|
||||||
|
// check if using a prefixed '/' or not
|
||||||
|
const auto input = con.buffer[1] && (con.buffer[0] == '/' || con.buffer[0] == '\\')
|
||||||
|
? std::string(con.buffer).substr(1)
|
||||||
|
: std::string(con.buffer);
|
||||||
|
|
||||||
|
if (!input.length())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input != fixed_input)
|
||||||
|
{
|
||||||
|
matches.clear();
|
||||||
|
|
||||||
|
if (input.find(" ") != std::string::npos)
|
||||||
|
{
|
||||||
|
find_matches(input.substr(0, input.find(" ")), matches, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
find_matches(input, matches, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
con.globals.may_auto_complete = false;
|
||||||
|
if (matches.size() > 24)
|
||||||
|
{
|
||||||
|
draw_hint_box(1, dvars::con_inputHintBoxColor->current.vector);
|
||||||
|
draw_hint_text(0, utils::string::va("%i matches (too many to show here)", matches.size()),
|
||||||
|
dvars::con_inputDvarMatchColor->current.vector);
|
||||||
|
}
|
||||||
|
else if (matches.size() == 1)
|
||||||
|
{
|
||||||
|
draw_hint_box(1, dvars::con_inputHintBoxColor->current.vector);
|
||||||
|
draw_hint_text(0, matches[0].data(), dvars::con_inputCmdMatchColor->current.vector);
|
||||||
|
|
||||||
|
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||||
|
con.globals.may_auto_complete = true;
|
||||||
|
}
|
||||||
|
else if (matches.size() > 1)
|
||||||
|
{
|
||||||
|
draw_hint_box(static_cast<int>(matches.size()), dvars::con_inputHintBoxColor->current.vector);
|
||||||
|
|
||||||
|
const auto offset = (con.screen_max[0] - con.globals.x) / 2.5f;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < matches.size(); i++)
|
||||||
|
{
|
||||||
|
draw_hint_text(static_cast<int>(i), matches[i].data(), dvars::con_inputCmdMatchColor->current.vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
|
||||||
|
con.globals.may_auto_complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_output_scrollbar(const float x, float y, const float width, const float height)
|
||||||
|
{
|
||||||
|
const auto _x = (x + width) - 10.0f;
|
||||||
|
draw_box(_x, y, 10.0f, height, dvars::con_outputBarColor->current.vector);
|
||||||
|
|
||||||
|
auto _height = height;
|
||||||
|
if (con.output.size() > con.visible_line_count)
|
||||||
|
{
|
||||||
|
const auto percentage = static_cast<float>(con.visible_line_count) / con.output.size();
|
||||||
|
_height *= percentage;
|
||||||
|
|
||||||
|
const auto remainingSpace = height - _height;
|
||||||
|
const auto percentageAbove = static_cast<float>(con.display_line_offset) / (con.output.size() - con.
|
||||||
|
visible_line_count);
|
||||||
|
|
||||||
|
y = y + (remainingSpace * percentageAbove);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_box(_x, y, 10.0f, _height, dvars::con_outputSliderColor->current.vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_output_text(const float x, float y)
|
||||||
|
{
|
||||||
|
const auto offset = con.output.size() >= con.visible_line_count
|
||||||
|
? 0.0f
|
||||||
|
: (con.font_height * (con.visible_line_count - con.output.size()));
|
||||||
|
|
||||||
|
for (auto i = 0; i < con.visible_line_count; i++)
|
||||||
|
{
|
||||||
|
y = console_font->pixelHeight + y;
|
||||||
|
|
||||||
|
const auto index = i + con.display_line_offset;
|
||||||
|
if (index >= con.output.size())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::R_AddCmdDrawText(con.output.at(index).data(), 0x7FFF, console_font, x, y + offset, 1.0f, 1.0f,
|
||||||
|
0.0f, color_white, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_output_window()
|
||||||
|
{
|
||||||
|
draw_box(con.screen_min[0], con.screen_min[1] + 32.0f, con.screen_max[0] - con.screen_min[0],
|
||||||
|
(con.screen_max[1] - con.screen_min[1]) - 32.0f, dvars::con_outputWindowColor->current.vector);
|
||||||
|
|
||||||
|
const auto x = con.screen_min[0] + 6.0f;
|
||||||
|
const auto y = (con.screen_min[1] + 32.0f) + 6.0f;
|
||||||
|
const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f;
|
||||||
|
const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f;
|
||||||
|
|
||||||
|
game::R_AddCmdDrawText("h1-mod", 0x7FFFFFFF, console_font, x,
|
||||||
|
((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_iw6, 0);
|
||||||
|
|
||||||
|
draw_output_scrollbar(x, y, width, height);
|
||||||
|
draw_output_text(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_console()
|
||||||
|
{
|
||||||
|
check_resize();
|
||||||
|
|
||||||
|
if (*game::keyCatchers & 1)
|
||||||
|
{
|
||||||
|
if (!(*game::keyCatchers & 1))
|
||||||
|
{
|
||||||
|
con.output_visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con.output_visible)
|
||||||
|
{
|
||||||
|
draw_output_window();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const int type, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
char va_buffer[0x200] = { 0 };
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsprintf_s(va_buffer, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
const auto formatted = std::string(va_buffer);
|
||||||
|
const auto lines = utils::string::split(formatted, '\n');
|
||||||
|
|
||||||
|
for (auto& line : lines)
|
||||||
|
{
|
||||||
|
print(type == con_type_info ? line : "^"s.append(std::to_string(type)).append(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool console_char_event(const int localClientNum, const int key)
|
||||||
|
{
|
||||||
|
if (key == game::keyNum_t::K_GRAVE || key == game::keyNum_t::K_TILDE)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*game::keyCatchers & 1)
|
||||||
|
{
|
||||||
|
if (key == game::keyNum_t::K_TAB) // tab (auto complete)
|
||||||
|
{
|
||||||
|
if (con.globals.may_auto_complete)
|
||||||
|
{
|
||||||
|
const auto firstChar = con.buffer[0];
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (firstChar == '\\' || firstChar == '/')
|
||||||
|
{
|
||||||
|
con.buffer[0] = firstChar;
|
||||||
|
con.buffer[1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
|
||||||
|
con.cursor = static_cast<int>(std::string(con.buffer).length());
|
||||||
|
|
||||||
|
if (con.cursor != 254)
|
||||||
|
{
|
||||||
|
con.buffer[con.cursor++] = ' ';
|
||||||
|
con.buffer[con.cursor] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'v' - 'a' + 1) // paste
|
||||||
|
{
|
||||||
|
const auto clipboard = utils::string::get_clipboard_data();
|
||||||
|
if (clipboard.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < clipboard.length(); i++)
|
||||||
|
{
|
||||||
|
console_char_event(localClientNum, clipboard[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'c' - 'a' + 1) // clear
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
con.line_count = 0;
|
||||||
|
con.output.clear();
|
||||||
|
history_index = -1;
|
||||||
|
history.clear();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'h' - 'a' + 1) // backspace
|
||||||
|
{
|
||||||
|
if (con.cursor > 0)
|
||||||
|
{
|
||||||
|
memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor,
|
||||||
|
strlen(con.buffer) + 1 - con.cursor);
|
||||||
|
con.cursor--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key < 32)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con.cursor == 256 - 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(con.buffer + con.cursor + 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor);
|
||||||
|
con.buffer[con.cursor] = static_cast<char>(key);
|
||||||
|
con.cursor++;
|
||||||
|
|
||||||
|
if (con.cursor == strlen(con.buffer) + 1)
|
||||||
|
{
|
||||||
|
con.buffer[con.cursor] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool console_key_event(const int localClientNum, const int key, const int down)
|
||||||
|
{
|
||||||
|
if (key == game::keyNum_t::K_F10)
|
||||||
|
{
|
||||||
|
game::Cmd_ExecuteSingleCommand(localClientNum, 0, "lui_open menu_systemlink_join\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == game::keyNum_t::K_GRAVE || key == game::keyNum_t::K_TILDE)
|
||||||
|
{
|
||||||
|
if (!down)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game::playerKeys[localClientNum].keys[game::keyNum_t::K_SHIFT].down)
|
||||||
|
{
|
||||||
|
if (!(*game::keyCatchers & 1))
|
||||||
|
toggle_console();
|
||||||
|
|
||||||
|
toggle_console_output();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_console();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*game::keyCatchers & 1)
|
||||||
|
{
|
||||||
|
if (down)
|
||||||
|
{
|
||||||
|
if (key == game::keyNum_t::K_UPARROW)
|
||||||
|
{
|
||||||
|
if (++history_index >= history.size())
|
||||||
|
{
|
||||||
|
history_index = static_cast<int>(history.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
|
||||||
|
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == game::keyNum_t::K_DOWNARROW)
|
||||||
|
{
|
||||||
|
if (--history_index < -1)
|
||||||
|
{
|
||||||
|
history_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
|
||||||
|
con.cursor = static_cast<int>(strlen(con.buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == game::keyNum_t::K_RIGHTARROW)
|
||||||
|
{
|
||||||
|
if (con.cursor < strlen(con.buffer))
|
||||||
|
{
|
||||||
|
con.cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == game::keyNum_t::K_LEFTARROW)
|
||||||
|
{
|
||||||
|
if (con.cursor > 0)
|
||||||
|
{
|
||||||
|
con.cursor--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//scroll through output
|
||||||
|
if (key == game::keyNum_t::K_MWHEELUP || key == game::keyNum_t::K_PGUP)
|
||||||
|
{
|
||||||
|
if (con.output.size() > con.visible_line_count && con.display_line_offset > 0)
|
||||||
|
{
|
||||||
|
con.display_line_offset--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == game::keyNum_t::K_MWHEELDOWN || key == game::keyNum_t::K_PGDN)
|
||||||
|
{
|
||||||
|
if (con.output.size() > con.visible_line_count && con.display_line_offset < (con.output.size() -
|
||||||
|
con.
|
||||||
|
visible_line_count))
|
||||||
|
{
|
||||||
|
con.display_line_offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == game::keyNum_t::K_ENTER)
|
||||||
|
{
|
||||||
|
game::Cbuf_AddText(0, utils::string::va("%s \n", fixed_input.data()));
|
||||||
|
|
||||||
|
if (history_index != -1)
|
||||||
|
{
|
||||||
|
const auto itr = history.begin() + history_index;
|
||||||
|
|
||||||
|
if (*itr == con.buffer)
|
||||||
|
{
|
||||||
|
history.erase(history.begin() + history_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push_front(con.buffer);
|
||||||
|
|
||||||
|
print("]"s.append(con.buffer));
|
||||||
|
|
||||||
|
if (history.size() > 10)
|
||||||
|
{
|
||||||
|
history.erase(history.begin() + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
history_index = -1;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
scheduler::loop(draw_console, scheduler::pipeline::renderer);
|
||||||
|
|
||||||
|
con.cursor = 0;
|
||||||
|
con.visible_line_count = 0;
|
||||||
|
con.output_visible = false;
|
||||||
|
con.display_line_offset = 0;
|
||||||
|
con.line_count = 0;
|
||||||
|
strncpy_s(con.buffer, "", 256);
|
||||||
|
|
||||||
|
con.globals.x = 0.0f;
|
||||||
|
con.globals.y = 0.0f;
|
||||||
|
con.globals.left_x = 0.0f;
|
||||||
|
con.globals.font_height = 0.0f;
|
||||||
|
con.globals.may_auto_complete = false;
|
||||||
|
con.globals.info_line_count = 0;
|
||||||
|
strncpy_s(con.globals.auto_complete_choice, "", 64);
|
||||||
|
|
||||||
|
// add clear command
|
||||||
|
command::add("clear", [&]()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
con.line_count = 0;
|
||||||
|
con.output.clear();
|
||||||
|
history_index = -1;
|
||||||
|
history.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
char a2[1] = {};
|
||||||
|
|
||||||
|
// add our dvars
|
||||||
|
dvars::con_inputBoxColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputBoxColor"), a2,
|
||||||
|
0.2f, 0.2f, 0.2f, 0.9f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputHintBoxColor"), a2,
|
||||||
|
0.3f, 0.3f, 0.3f, 1.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_outputBarColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputBarColor"), a2,
|
||||||
|
0.5f, 0.5f, 0.5f, 0.6f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_outputSliderColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputSliderColor"), a2,
|
||||||
|
0.0f, 0.7f, 1.0f, 1.00f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_outputWindowColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputWindowColor"), a2,
|
||||||
|
0.25f, 0.25f, 0.25f, 0.85f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarMatchColor"), a2,
|
||||||
|
1.0f, 1.0f, 0.8f, 1.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarValueColor"), a2,
|
||||||
|
1.0f, 1.0f, 0.8f, 1.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarInactiveValueColor"), a2,
|
||||||
|
0.8f, 0.8f, 0.8f, 1.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputCmdMatchColor"), a2,
|
||||||
|
0.80f, 0.80f, 1.0f, 1.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
18
src/component/game_console.hpp
Normal file
18
src/component/game_console.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game_console
|
||||||
|
{
|
||||||
|
enum console_type
|
||||||
|
{
|
||||||
|
con_type_error = 1,
|
||||||
|
con_type_warning = 3,
|
||||||
|
con_type_info = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
void print(int type, const char* fmt, ...);
|
||||||
|
|
||||||
|
bool console_char_event(int local_client_num, int key);
|
||||||
|
bool console_key_event(int local_client_num, int key, int down);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
}
|
42
src/component/input.cpp
Normal file
42
src/component/input.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "game_console.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
namespace input
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::hook::detour cl_char_event_hook;
|
||||||
|
utils::hook::detour cl_key_event_hook;
|
||||||
|
|
||||||
|
void cl_char_event_stub(const int local_client_num, const int key)
|
||||||
|
{
|
||||||
|
if (!game_console::console_char_event(local_client_num, key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_char_event_hook.invoke<void>(local_client_num, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cl_key_event_stub(const int local_client_num, const int key, const int down)
|
||||||
|
{
|
||||||
|
if (!game_console::console_key_event(local_client_num, key, down))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_key_event_hook.invoke<void>(local_client_num, key, down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
cl_char_event_hook.create(game::base_address + 0x3D27B0, cl_char_event_stub);
|
||||||
|
cl_key_event_hook.create(game::base_address + 0x3D2AE0, cl_key_event_stub);
|
||||||
|
}
|
||||||
|
}
|
6
src/component/input.hpp
Normal file
6
src/component/input.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace input
|
||||||
|
{
|
||||||
|
void init();
|
||||||
|
}
|
151
src/component/scheduler.cpp
Normal file
151
src/component/scheduler.cpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct task
|
||||||
|
{
|
||||||
|
std::function<bool()> handler{};
|
||||||
|
std::chrono::milliseconds interval{};
|
||||||
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using task_list = std::vector<task>;
|
||||||
|
|
||||||
|
class task_pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(task&& task)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
this->merge_callbacks();
|
||||||
|
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - i->last_call;
|
||||||
|
|
||||||
|
if (diff < i->interval)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->last_call = now;
|
||||||
|
|
||||||
|
const auto res = i->handler();
|
||||||
|
if (res == cond_end)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::concurrency::container<task_list> new_callbacks_;
|
||||||
|
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||||
|
|
||||||
|
void merge_callbacks()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||||
|
new_tasks = {};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile bool kill = false;
|
||||||
|
std::thread thread;
|
||||||
|
task_pipeline pipelines[pipeline::count];
|
||||||
|
utils::hook::detour r_end_frame_hook;
|
||||||
|
|
||||||
|
void execute(const pipeline type)
|
||||||
|
{
|
||||||
|
assert(type >= 0 && type < pipeline::count);
|
||||||
|
pipelines[type].execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void r_end_frame_stub()
|
||||||
|
{
|
||||||
|
execute(pipeline::renderer);
|
||||||
|
r_end_frame_hook.invoke<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_frame_stub()
|
||||||
|
{
|
||||||
|
//game::G_Glass_Update();
|
||||||
|
execute(pipeline::server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
assert(type >= 0 && type < pipeline::count);
|
||||||
|
|
||||||
|
task task;
|
||||||
|
task.handler = callback;
|
||||||
|
task.interval = delay;
|
||||||
|
task.last_call = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
pipelines[type].add(std::move(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return cond_continue;
|
||||||
|
}, type, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void once(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return cond_end;
|
||||||
|
}, type, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
thread = std::thread([]()
|
||||||
|
{
|
||||||
|
while (!kill)
|
||||||
|
{
|
||||||
|
execute(pipeline::async);
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
r_end_frame_hook.create(game::base_address + 0x76D7B0, scheduler::r_end_frame_stub);
|
||||||
|
//utils::hook::call(0x1402F8879, scheduler::server_frame_stub);
|
||||||
|
}
|
||||||
|
}
|
33
src/component/scheduler.hpp
Normal file
33
src/component/scheduler.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
enum pipeline
|
||||||
|
{
|
||||||
|
// Asynchronuous pipeline, disconnected from the game
|
||||||
|
async = 0,
|
||||||
|
|
||||||
|
// The game's rendering pipeline
|
||||||
|
renderer,
|
||||||
|
|
||||||
|
// The game's server thread
|
||||||
|
server,
|
||||||
|
|
||||||
|
// The game's main thread
|
||||||
|
main,
|
||||||
|
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const bool cond_continue = false;
|
||||||
|
static const bool cond_end = true;
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void loop(const std::function<void()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void once(const std::function<void()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
}
|
48
src/dllmain.cpp
Normal file
48
src/dllmain.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "stdinc.hpp"
|
||||||
|
|
||||||
|
#pragma warning(disable:4996)
|
||||||
|
|
||||||
|
DWORD WINAPI dwConsole(LPVOID)
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
AttachConsole(GetCurrentProcessId());
|
||||||
|
|
||||||
|
freopen("CONIN$", "r", stdin);
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
|
||||||
|
std::string cmd;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::cout << "\n";
|
||||||
|
std::getline(std::cin, cmd);
|
||||||
|
|
||||||
|
game::Cbuf_AddText(0, cmd.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
CreateThread(0, 0, dwConsole, 0, 0, 0);
|
||||||
|
|
||||||
|
game::load_base_address();
|
||||||
|
|
||||||
|
utils::hook::set(game::base_address + 0xBE7F83C, true); // disable bnet popup
|
||||||
|
|
||||||
|
command::init();
|
||||||
|
input::init();
|
||||||
|
scheduler::init();
|
||||||
|
game_console::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
136
src/game/dvars.cpp
Normal file
136
src/game/dvars.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
#include "game.hpp"
|
||||||
|
|
||||||
|
namespace dvars
|
||||||
|
{
|
||||||
|
game::dvar_t* con_inputBoxColor = nullptr;
|
||||||
|
game::dvar_t* con_inputHintBoxColor = nullptr;
|
||||||
|
game::dvar_t* con_outputBarColor = nullptr;
|
||||||
|
game::dvar_t* con_outputSliderColor = nullptr;
|
||||||
|
game::dvar_t* con_outputWindowColor = nullptr;
|
||||||
|
game::dvar_t* con_inputDvarMatchColor = nullptr;
|
||||||
|
game::dvar_t* con_inputDvarValueColor = nullptr;
|
||||||
|
game::dvar_t* con_inputDvarInactiveValueColor = nullptr;
|
||||||
|
game::dvar_t* con_inputCmdMatchColor = nullptr;
|
||||||
|
|
||||||
|
game::dvar_t* g_playerEjection = nullptr;
|
||||||
|
game::dvar_t* g_playerCollision = nullptr;
|
||||||
|
game::dvar_t* g_gravity = nullptr;
|
||||||
|
game::dvar_t* g_speed = nullptr;
|
||||||
|
|
||||||
|
game::dvar_t* pm_bouncing = nullptr;
|
||||||
|
|
||||||
|
game::dvar_t* jump_slowDownEnable = nullptr;
|
||||||
|
game::dvar_t* jump_enableFallDamage = nullptr;
|
||||||
|
game::dvar_t* jump_height = nullptr;
|
||||||
|
game::dvar_t* jump_ladderPushVel = nullptr;
|
||||||
|
|
||||||
|
game::dvar_t* r_aspectRatioCustom = nullptr;
|
||||||
|
game::dvar_t* r_fullbright = nullptr;
|
||||||
|
|
||||||
|
game::dvar_t* aimassist_enabled = nullptr;
|
||||||
|
|
||||||
|
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
|
||||||
|
{
|
||||||
|
if (domain.vector.min == -FLT_MAX)
|
||||||
|
{
|
||||||
|
if (domain.vector.max == FLT_MAX)
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any %iD vector", components);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any %iD vector with components %g or smaller", components,
|
||||||
|
domain.vector.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (domain.vector.max == FLT_MAX)
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any %iD vector with components %g or bigger", components,
|
||||||
|
domain.vector.min);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any %iD vector with components from %g to %g", components,
|
||||||
|
domain.vector.min, domain.vector.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain)
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case game::dvar_type::boolean:
|
||||||
|
return "Domain is 0 or 1"s;
|
||||||
|
|
||||||
|
case game::dvar_type::value:
|
||||||
|
if (domain.value.min == -FLT_MAX)
|
||||||
|
{
|
||||||
|
if (domain.value.max == FLT_MAX)
|
||||||
|
{
|
||||||
|
return "Domain is any number"s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::string::va("Domain is any number %g or smaller", domain.value.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain.value.max == FLT_MAX)
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any number %g or bigger", domain.value.min);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::string::va("Domain is any number from %g to %g", domain.value.min, domain.value.max);
|
||||||
|
|
||||||
|
case game::dvar_type::vec2:
|
||||||
|
return dvar_get_vector_domain(2, domain);
|
||||||
|
|
||||||
|
case game::dvar_type::rgb:
|
||||||
|
case game::dvar_type::vec3:
|
||||||
|
return dvar_get_vector_domain(3, domain);
|
||||||
|
|
||||||
|
case game::dvar_type::vec4:
|
||||||
|
return dvar_get_vector_domain(4, domain);
|
||||||
|
|
||||||
|
case game::dvar_type::integer:
|
||||||
|
if (domain.enumeration.stringCount == INT_MIN)
|
||||||
|
{
|
||||||
|
if (domain.integer.max == INT_MAX)
|
||||||
|
{
|
||||||
|
return "Domain is any integer"s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::string::va("Domain is any integer %i or smaller", domain.integer.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain.integer.max == INT_MAX)
|
||||||
|
{
|
||||||
|
return utils::string::va("Domain is any integer %i or bigger", domain.integer.min);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::string::va("Domain is any integer from %i to %i", domain.integer.min, domain.integer.max);
|
||||||
|
|
||||||
|
case game::dvar_type::color:
|
||||||
|
return "Domain is any 4-component color, in RGBA format"s;
|
||||||
|
|
||||||
|
case game::dvar_type::enumeration:
|
||||||
|
str = "Domain is one of the following:"s;
|
||||||
|
|
||||||
|
for (auto string_index = 0; string_index < domain.enumeration.stringCount; ++string_index)
|
||||||
|
{
|
||||||
|
str += utils::string::va("\n %2i: %s", string_index, domain.enumeration.strings[string_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
case game::dvar_type::string:
|
||||||
|
return "Domain is any text"s;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return utils::string::va("unhandled dvar type '%i'", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/game/dvars.hpp
Normal file
38
src/game/dvars.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "game.hpp"
|
||||||
|
#include "structs.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dvars
|
||||||
|
{
|
||||||
|
extern game::dvar_t* con_inputBoxColor;
|
||||||
|
extern game::dvar_t* con_inputHintBoxColor;
|
||||||
|
extern game::dvar_t* con_outputBarColor;
|
||||||
|
extern game::dvar_t* con_outputSliderColor;
|
||||||
|
extern game::dvar_t* con_outputWindowColor;
|
||||||
|
extern game::dvar_t* con_inputDvarMatchColor;
|
||||||
|
extern game::dvar_t* con_inputDvarValueColor;
|
||||||
|
extern game::dvar_t* con_inputDvarInactiveValueColor;
|
||||||
|
extern game::dvar_t* con_inputCmdMatchColor;
|
||||||
|
|
||||||
|
extern game::dvar_t* g_playerCollision;
|
||||||
|
extern game::dvar_t* g_playerEjection;
|
||||||
|
extern game::dvar_t* g_gravity;
|
||||||
|
extern game::dvar_t* g_speed;
|
||||||
|
|
||||||
|
extern game::dvar_t* pm_bouncing;
|
||||||
|
|
||||||
|
extern game::dvar_t* r_aspectRatioCustom;
|
||||||
|
extern game::dvar_t* jump_slowDownEnable;
|
||||||
|
extern game::dvar_t* jump_enableFallDamage;
|
||||||
|
extern game::dvar_t* jump_height;
|
||||||
|
extern game::dvar_t* jump_ladderPushVel;
|
||||||
|
|
||||||
|
extern game::dvar_t* r_fullbright;
|
||||||
|
|
||||||
|
extern game::dvar_t* aimassist_enabled;
|
||||||
|
|
||||||
|
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain);
|
||||||
|
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain);
|
||||||
|
}
|
12
src/game/game.cpp
Normal file
12
src/game/game.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "stdinc.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
uint64_t base_address;
|
||||||
|
|
||||||
|
void load_base_address()
|
||||||
|
{
|
||||||
|
const auto module = GetModuleHandle(NULL);
|
||||||
|
base_address = uint64_t(module);
|
||||||
|
}
|
||||||
|
}
|
38
src/game/game.hpp
Normal file
38
src/game/game.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
extern uint64_t base_address;
|
||||||
|
|
||||||
|
void load_base_address();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class symbol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
symbol(const size_t address)
|
||||||
|
: address_(reinterpret_cast<T*>(address))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T*>((uint64_t)address_ + base_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T* () const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* address_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "symbols.hpp"
|
531
src/game/structs.hpp
Normal file
531
src/game/structs.hpp
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
#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];
|
||||||
|
|
||||||
|
struct gclient_s
|
||||||
|
{
|
||||||
|
char __pad0[7684];
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntityState
|
||||||
|
{
|
||||||
|
char entityNum;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gentity_s
|
||||||
|
{
|
||||||
|
EntityState s;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Material
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Glyph
|
||||||
|
{
|
||||||
|
unsigned short letter;
|
||||||
|
char x0;
|
||||||
|
char y0;
|
||||||
|
char dx;
|
||||||
|
char pixelWidth;
|
||||||
|
char pixelHeight;
|
||||||
|
float s0;
|
||||||
|
float t0;
|
||||||
|
float s1;
|
||||||
|
float t1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Font_s
|
||||||
|
{
|
||||||
|
const char* fontName;
|
||||||
|
int pixelHeight;
|
||||||
|
int glyphCount;
|
||||||
|
Material* material;
|
||||||
|
Material* glowMaterial;
|
||||||
|
Glyph* glyphs;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum keyNum_t
|
||||||
|
{
|
||||||
|
K_NONE = 0x0,
|
||||||
|
K_FIRSTGAMEPADBUTTON_RANGE_1 = 0x1,
|
||||||
|
K_BUTTON_A = 0x1,
|
||||||
|
K_BUTTON_B = 0x2,
|
||||||
|
K_BUTTON_X = 0x3,
|
||||||
|
K_BUTTON_Y = 0x4,
|
||||||
|
K_BUTTON_LSHLDR = 0x5,
|
||||||
|
K_BUTTON_RSHLDR = 0x6,
|
||||||
|
K_LASTGAMEPADBUTTON_RANGE_1 = 0x6,
|
||||||
|
K_BS = 0x8,
|
||||||
|
K_TAB = 0x9,
|
||||||
|
K_ENTER = 0xD,
|
||||||
|
K_FIRSTGAMEPADBUTTON_RANGE_2 = 0xE,
|
||||||
|
K_BUTTON_START = 0xE,
|
||||||
|
K_BUTTON_BACK = 0xF,
|
||||||
|
K_BUTTON_LSTICK = 0x10,
|
||||||
|
K_BUTTON_RSTICK = 0x11,
|
||||||
|
K_BUTTON_LTRIG = 0x12,
|
||||||
|
K_BUTTON_RTRIG = 0x13,
|
||||||
|
K_DPAD_UP = 0x14,
|
||||||
|
K_FIRSTDPAD = 0x14,
|
||||||
|
K_DPAD_DOWN = 0x15,
|
||||||
|
K_DPAD_LEFT = 0x16,
|
||||||
|
K_DPAD_RIGHT = 0x17,
|
||||||
|
K_BUTTON_LSTICK_ALTIMAGE2 = 0x10,
|
||||||
|
K_BUTTON_RSTICK_ALTIMAGE2 = 0x11,
|
||||||
|
K_BUTTON_LSTICK_ALTIMAGE = 0xBC,
|
||||||
|
K_BUTTON_RSTICK_ALTIMAGE = 0xBD,
|
||||||
|
K_LASTDPAD = 0x17,
|
||||||
|
K_LASTGAMEPADBUTTON_RANGE_2 = 0x17,
|
||||||
|
K_ESCAPE = 0x1B,
|
||||||
|
K_FIRSTGAMEPADBUTTON_RANGE_3 = 0x1C,
|
||||||
|
K_APAD_UP = 0x1C,
|
||||||
|
K_FIRSTAPAD = 0x1C,
|
||||||
|
K_APAD_DOWN = 0x1D,
|
||||||
|
K_APAD_LEFT = 0x1E,
|
||||||
|
K_APAD_RIGHT = 0x1F,
|
||||||
|
K_LASTAPAD = 0x1F,
|
||||||
|
K_LASTGAMEPADBUTTON_RANGE_3 = 0x1F,
|
||||||
|
K_SPACE = 0x20,
|
||||||
|
K_GRAVE = 0x60,
|
||||||
|
K_TILDE = 0x7E,
|
||||||
|
K_BACKSPACE = 0x7F,
|
||||||
|
K_ASCII_FIRST = 0x80,
|
||||||
|
K_ASCII_181 = 0x80,
|
||||||
|
K_ASCII_191 = 0x81,
|
||||||
|
K_ASCII_223 = 0x82,
|
||||||
|
K_ASCII_224 = 0x83,
|
||||||
|
K_ASCII_225 = 0x84,
|
||||||
|
K_ASCII_228 = 0x85,
|
||||||
|
K_ASCII_229 = 0x86,
|
||||||
|
K_ASCII_230 = 0x87,
|
||||||
|
K_ASCII_231 = 0x88,
|
||||||
|
K_ASCII_232 = 0x89,
|
||||||
|
K_ASCII_233 = 0x8A,
|
||||||
|
K_ASCII_236 = 0x8B,
|
||||||
|
K_ASCII_241 = 0x8C,
|
||||||
|
K_ASCII_242 = 0x8D,
|
||||||
|
K_ASCII_243 = 0x8E,
|
||||||
|
K_ASCII_246 = 0x8F,
|
||||||
|
K_ASCII_248 = 0x90,
|
||||||
|
K_ASCII_249 = 0x91,
|
||||||
|
K_ASCII_250 = 0x92,
|
||||||
|
K_ASCII_252 = 0x93,
|
||||||
|
K_END_ASCII_CHARS = 0x94,
|
||||||
|
K_COMMAND = 0x96,
|
||||||
|
K_CAPSLOCK = 0x97,
|
||||||
|
K_POWER = 0x98,
|
||||||
|
K_PAUSE = 0x99,
|
||||||
|
K_UPARROW = 0x9A,
|
||||||
|
K_DOWNARROW = 0x9B,
|
||||||
|
K_LEFTARROW = 0x9C,
|
||||||
|
K_RIGHTARROW = 0x9D,
|
||||||
|
K_ALT = 0x9E,
|
||||||
|
K_CTRL = 0x9F,
|
||||||
|
K_SHIFT = 0xA0,
|
||||||
|
K_INS = 0xA1,
|
||||||
|
K_DEL = 0xA2,
|
||||||
|
K_PGDN = 0xA3,
|
||||||
|
K_PGUP = 0xA4,
|
||||||
|
K_HOME = 0xA5,
|
||||||
|
K_END = 0xA6,
|
||||||
|
K_F1 = 0xA7,
|
||||||
|
K_F2 = 0xA8,
|
||||||
|
K_F3 = 0xA9,
|
||||||
|
K_F4 = 0xAA,
|
||||||
|
K_F5 = 0xAB,
|
||||||
|
K_F6 = 0xAC,
|
||||||
|
K_F7 = 0xAD,
|
||||||
|
K_F8 = 0xAE,
|
||||||
|
K_F9 = 0xAF,
|
||||||
|
K_F10 = 0xB0,
|
||||||
|
K_F11 = 0xB1,
|
||||||
|
K_F12 = 0xB2,
|
||||||
|
K_F13 = 0xB3,
|
||||||
|
K_F14 = 0xB4,
|
||||||
|
K_F15 = 0xB5,
|
||||||
|
K_KP_HOME = 0xB6,
|
||||||
|
K_KP_UPARROW = 0xB7,
|
||||||
|
K_KP_PGUP = 0xB8,
|
||||||
|
K_KP_LEFTARROW = 0xB9,
|
||||||
|
K_KP_5 = 0xBA,
|
||||||
|
K_KP_RIGHTARROW = 0xBB,
|
||||||
|
K_KP_END = 0xBC,
|
||||||
|
K_KP_DOWNARROW = 0xBD,
|
||||||
|
K_KP_PGDN = 0xBE,
|
||||||
|
K_KP_ENTER = 0xBF,
|
||||||
|
K_KP_INS = 0xC0,
|
||||||
|
K_KP_DEL = 0xC1,
|
||||||
|
K_KP_SLASH = 0xC2,
|
||||||
|
K_KP_MINUS = 0xC3,
|
||||||
|
K_KP_PLUS = 0xC4,
|
||||||
|
K_KP_NUMLOCK = 0xC5,
|
||||||
|
K_KP_STAR = 0xC6,
|
||||||
|
K_KP_EQUALS = 0xC7,
|
||||||
|
K_MOUSE1 = 0xC8,
|
||||||
|
K_MOUSE2 = 0xC9,
|
||||||
|
K_MOUSE3 = 0xCA,
|
||||||
|
K_MOUSE4 = 0xCB,
|
||||||
|
K_MOUSE5 = 0xCC,
|
||||||
|
K_MWHEELDOWN = 0xCD,
|
||||||
|
K_MWHEELUP = 0xCE,
|
||||||
|
K_AUX1 = 0xCF,
|
||||||
|
K_AUX2 = 0xD0,
|
||||||
|
K_AUX3 = 0xD1,
|
||||||
|
K_AUX4 = 0xD2,
|
||||||
|
K_AUX5 = 0xD3,
|
||||||
|
K_AUX6 = 0xD4,
|
||||||
|
K_AUX7 = 0xD5,
|
||||||
|
K_AUX8 = 0xD6,
|
||||||
|
K_AUX9 = 0xD7,
|
||||||
|
K_AUX10 = 0xD8,
|
||||||
|
K_AUX11 = 0xD9,
|
||||||
|
K_AUX12 = 0xDA,
|
||||||
|
K_AUX13 = 0xDB,
|
||||||
|
K_AUX14 = 0xDC,
|
||||||
|
K_AUX15 = 0xDD,
|
||||||
|
K_AUX16 = 0xDE,
|
||||||
|
K_LAST_KEY = 0xDF
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeyState
|
||||||
|
{
|
||||||
|
int down;
|
||||||
|
int repeats;
|
||||||
|
int binding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayerKeyState
|
||||||
|
{
|
||||||
|
int overstrikeMode;
|
||||||
|
int anyKeyDown;
|
||||||
|
KeyState keys[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DvarSetSource : std::uint32_t
|
||||||
|
{
|
||||||
|
DVAR_SOURCE_INTERNAL = 0x0,
|
||||||
|
DVAR_SOURCE_EXTERNAL = 0x1,
|
||||||
|
DVAR_SOURCE_SCRIPT = 0x2,
|
||||||
|
DVAR_SOURCE_UISCRIPT = 0x3,
|
||||||
|
DVAR_SOURCE_SERVERCMD = 0x4,
|
||||||
|
DVAR_SOURCE_NUM = 0x5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DvarFlags : std::uint32_t
|
||||||
|
{
|
||||||
|
DVAR_FLAG_NONE = 0,
|
||||||
|
DVAR_FLAG_SAVED = 0x1,
|
||||||
|
DVAR_FLAG_LATCHED = 0x2,
|
||||||
|
DVAR_FLAG_CHEAT = 0x4,
|
||||||
|
DVAR_FLAG_REPLICATED = 0x8,
|
||||||
|
DVAR_FLAG_WRITE = 0x800,
|
||||||
|
DVAR_FLAG_READ = 0x2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dvar_type : std::int8_t
|
||||||
|
{
|
||||||
|
boolean = 0,
|
||||||
|
value = 1,
|
||||||
|
vec2 = 2,
|
||||||
|
vec3 = 3,
|
||||||
|
vec4 = 4,
|
||||||
|
integer = 5,
|
||||||
|
enumeration = 6,
|
||||||
|
string = 7,
|
||||||
|
color = 8,
|
||||||
|
rgb = 9 // Color without alpha
|
||||||
|
};
|
||||||
|
|
||||||
|
union dvar_value
|
||||||
|
{
|
||||||
|
bool enabled;
|
||||||
|
int integer;
|
||||||
|
unsigned int unsignedInt;
|
||||||
|
float value;
|
||||||
|
float vector[4];
|
||||||
|
const char* string;
|
||||||
|
char color[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct $A37BA207B3DDD6345C554D4661813EDD
|
||||||
|
{
|
||||||
|
int stringCount;
|
||||||
|
const char* const* strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct $9CA192F9DB66A3CB7E01DE78A0DEA53D
|
||||||
|
{
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct $251C2428A496074035CACA7AAF3D55BD
|
||||||
|
{
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
};
|
||||||
|
|
||||||
|
union dvar_limits
|
||||||
|
{
|
||||||
|
$A37BA207B3DDD6345C554D4661813EDD enumeration;
|
||||||
|
$9CA192F9DB66A3CB7E01DE78A0DEA53D integer;
|
||||||
|
$251C2428A496074035CACA7AAF3D55BD value;
|
||||||
|
$251C2428A496074035CACA7AAF3D55BD vector;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dvar_t
|
||||||
|
{
|
||||||
|
const char* name; //00
|
||||||
|
unsigned int flags; //08
|
||||||
|
dvar_type type; //0C
|
||||||
|
bool modified; //0D
|
||||||
|
dvar_value current; //10
|
||||||
|
dvar_value latched;
|
||||||
|
dvar_value reset;
|
||||||
|
dvar_limits domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScreenPlacement
|
||||||
|
{
|
||||||
|
vec2_t scaleVirtualToReal;
|
||||||
|
vec2_t scaleVirtualToFull;
|
||||||
|
vec2_t scaleRealToVirtual;
|
||||||
|
vec2_t realViewportPosition;
|
||||||
|
vec2_t realViewportSize;
|
||||||
|
vec2_t virtualViewableMin;
|
||||||
|
vec2_t virtualViewableMax;
|
||||||
|
vec2_t realViewableMin;
|
||||||
|
vec2_t realViewableMax;
|
||||||
|
vec2_t virtualAdjustableMin;
|
||||||
|
vec2_t virtualAdjustableMax;
|
||||||
|
vec2_t realAdjustableMin;
|
||||||
|
vec2_t realAdjustableMax;
|
||||||
|
vec2_t subScreenLeft;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdArgs
|
||||||
|
{
|
||||||
|
int nesting;
|
||||||
|
int localClientNum[8];
|
||||||
|
int controllerIndex[8];
|
||||||
|
int argc[8];
|
||||||
|
const char** argv[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_function_s
|
||||||
|
{
|
||||||
|
cmd_function_s* next;
|
||||||
|
const char* name;
|
||||||
|
void(__cdecl* function)();
|
||||||
|
};
|
||||||
|
|
||||||
|
enum XAssetType
|
||||||
|
{
|
||||||
|
ASSET_TYPE_PHYSPRESET,
|
||||||
|
ASSET_TYPE_PHYSCOLLMAP,
|
||||||
|
ASSET_TYPE_PHYSWATERPRESET,
|
||||||
|
ASSET_TYPE_PHYSWORLDMAP,
|
||||||
|
ASSET_TYPE_PHYSCONSTRAINT,
|
||||||
|
ASSET_TYPE_XANIMPARTS,
|
||||||
|
ASSET_TYPE_XMODELSURFS,
|
||||||
|
ASSET_TYPE_XMODEL,
|
||||||
|
ASSET_TYPE_MATERIAL,
|
||||||
|
ASSET_TYPE_COMPUTESHADER,
|
||||||
|
ASSET_TYPE_VERTEXSHADER,
|
||||||
|
ASSET_TYPE_HULLSHADER,
|
||||||
|
ASSET_TYPE_DOMAINSHADER,
|
||||||
|
ASSET_TYPE_PIXELSHADER,
|
||||||
|
ASSET_TYPE_VERTEXDECL,
|
||||||
|
ASSET_TYPE_TECHNIQUE_SET,
|
||||||
|
ASSET_TYPE_IMAGE,
|
||||||
|
ASSET_TYPE_SOUND,
|
||||||
|
ASSET_TYPE_SOUND_SUBMIX,
|
||||||
|
ASSET_TYPE_SOUND_CURVE,
|
||||||
|
ASSET_TYPE_LPF_CURVE,
|
||||||
|
ASSET_TYPE_REVERB_CURVE,
|
||||||
|
ASSET_TYPE_SOUND_CONTEXT,
|
||||||
|
ASSET_TYPE_LOADED_SOUND,
|
||||||
|
ASSET_TYPE_CLIPMAP,
|
||||||
|
ASSET_TYPE_COMWORLD,
|
||||||
|
ASSET_TYPE_GLASSWORLD,
|
||||||
|
ASSET_TYPE_PATHDATA,
|
||||||
|
ASSET_TYPE_VEHICLE_TRACK,
|
||||||
|
ASSET_TYPE_MAP_ENTS,
|
||||||
|
ASSET_TYPE_FXWORLD,
|
||||||
|
ASSET_TYPE_GFXWORLD,
|
||||||
|
ASSET_TYPE_LIGHT_DEF,
|
||||||
|
ASSET_TYPE_UI_MAP,
|
||||||
|
ASSET_TYPE_FONT,
|
||||||
|
ASSET_TYPE_MENULIST,
|
||||||
|
ASSET_TYPE_MENU,
|
||||||
|
ASSET_TYPE_ANIMCLASS,
|
||||||
|
ASSET_TYPE_LOCALIZE_ENTRY,
|
||||||
|
ASSET_TYPE_ATTACHMENT,
|
||||||
|
ASSET_TYPE_WEAPON,
|
||||||
|
ASSET_TYPE_SNDDRIVER_GLOBALS,
|
||||||
|
ASSET_TYPE_FX,
|
||||||
|
ASSET_TYPE_IMPACT_FX,
|
||||||
|
ASSET_TYPE_SURFACE_FX,
|
||||||
|
ASSET_TYPE_AITYPE,
|
||||||
|
ASSET_TYPE_MPTYPE,
|
||||||
|
ASSET_TYPE_CHARACTER,
|
||||||
|
ASSET_TYPE_XMODELALIAS,
|
||||||
|
ASSET_TYPE_RAWFILE,
|
||||||
|
ASSET_TYPE_SCRIPTFILE,
|
||||||
|
ASSET_TYPE_STRINGTABLE,
|
||||||
|
ASSET_TYPE_LEADERBOARD,
|
||||||
|
ASSET_TYPE_STRUCTURED_DATA_DEF,
|
||||||
|
ASSET_TYPE_TRACER,
|
||||||
|
ASSET_TYPE_VEHICLE,
|
||||||
|
ASSET_TYPE_ADDON_MAP_ENTS,
|
||||||
|
ASSET_TYPE_NET_CONST_STRINGS,
|
||||||
|
ASSET_TYPE_REVERB_PRESET,
|
||||||
|
ASSET_TYPE_LUA_FILE,
|
||||||
|
ASSET_TYPE_SCRIPTABLE,
|
||||||
|
ASSET_TYPE_EQUIPMENT_SND_TABLE,
|
||||||
|
ASSET_TYPE_VECTORFIELD,
|
||||||
|
ASSET_TYPE_DOPPLER_PRESET,
|
||||||
|
ASSET_TYPE_PARTICLE_SIM_ANIMATION,
|
||||||
|
ASSET_TYPE_LASER,
|
||||||
|
ASSET_TYPE_SKELETON_SCRIPT,
|
||||||
|
ASSET_TYPE_CLUT,
|
||||||
|
ASSET_TYPE_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StreamFileNameRaw
|
||||||
|
{
|
||||||
|
const char* dir;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StreamFileNamePacked
|
||||||
|
{
|
||||||
|
unsigned __int64 offset;
|
||||||
|
unsigned __int64 length;
|
||||||
|
};
|
||||||
|
|
||||||
|
union StreamFileInfo
|
||||||
|
{
|
||||||
|
StreamFileNameRaw raw;
|
||||||
|
StreamFileNamePacked packed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StreamFileName
|
||||||
|
{
|
||||||
|
unsigned __int16 isLocalized;
|
||||||
|
unsigned __int16 fileIndex;
|
||||||
|
StreamFileInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StreamedSound
|
||||||
|
{
|
||||||
|
StreamFileName filename;
|
||||||
|
unsigned int totalMsec;
|
||||||
|
};
|
||||||
|
|
||||||
|
union SoundFileRef
|
||||||
|
{
|
||||||
|
StreamedSound streamSnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SoundFile
|
||||||
|
{
|
||||||
|
char type;
|
||||||
|
char exists;
|
||||||
|
SoundFileRef u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct snd_alias_t
|
||||||
|
{
|
||||||
|
const char* aliasName;
|
||||||
|
char __pad0[24];
|
||||||
|
SoundFile* soundFile;
|
||||||
|
char __pad1[198];
|
||||||
|
// not gonna map this out...
|
||||||
|
};
|
||||||
|
|
||||||
|
struct snd_alias_list_t
|
||||||
|
{
|
||||||
|
const char* aliasName;
|
||||||
|
snd_alias_t* head;
|
||||||
|
void* unk;
|
||||||
|
unsigned char count;
|
||||||
|
unsigned char unkCount;
|
||||||
|
char __pad0[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RawFile
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int compressedLen;
|
||||||
|
int len;
|
||||||
|
const char* buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptFile
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int compressedLen;
|
||||||
|
int len;
|
||||||
|
int bytecodeLen;
|
||||||
|
const char* buffer;
|
||||||
|
char* bytecode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StringTableCell
|
||||||
|
{
|
||||||
|
const char* string;
|
||||||
|
int hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StringTable
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int columnCount;
|
||||||
|
int rowCount;
|
||||||
|
StringTableCell* values;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LuaFile
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int len;
|
||||||
|
char strippingType;
|
||||||
|
const char* buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
union XAssetHeader
|
||||||
|
{
|
||||||
|
void* data;
|
||||||
|
Material* material;
|
||||||
|
Font_s* font;
|
||||||
|
RawFile* rawfile;
|
||||||
|
ScriptFile* scriptfile;
|
||||||
|
StringTable* stringTable;
|
||||||
|
LuaFile* luaFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XAsset
|
||||||
|
{
|
||||||
|
XAssetType type;
|
||||||
|
XAssetHeader header;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XAssetEntry
|
||||||
|
{
|
||||||
|
XAsset asset;
|
||||||
|
char zoneIndex;
|
||||||
|
volatile char inuseMask;
|
||||||
|
unsigned int nextHash;
|
||||||
|
unsigned int nextOverride;
|
||||||
|
unsigned int nextPoolEntry;
|
||||||
|
};
|
||||||
|
}
|
58
src/game/symbols.hpp
Normal file
58
src/game/symbols.hpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WEAK __declspec(selectany)
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x59A050};
|
||||||
|
|
||||||
|
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x59A5F0};
|
||||||
|
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x59ABA0};
|
||||||
|
|
||||||
|
WEAK symbol<void(XAssetType type, void(__cdecl* func)(game::XAssetHeader, void*), const void* inData, bool includeOverride)>
|
||||||
|
DB_EnumXAssets_Internal{0x4129F0};
|
||||||
|
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x3E4090};
|
||||||
|
|
||||||
|
WEAK symbol<dvar_t* (const char* name)> Dvar_FindVar{0x618F90};
|
||||||
|
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x5A75D0};
|
||||||
|
WEAK symbol<dvar_t* (const char* dvarName, bool value, unsigned int flags, const char* description)>
|
||||||
|
Dvar_RegisterBool{0x617BB0};
|
||||||
|
WEAK symbol<dvar_t* (int dvarName, const char* a2, float x, float y, float z, float w, float min, float max,
|
||||||
|
unsigned int flags)> Dvar_RegisterVec4{0x6185F0};
|
||||||
|
WEAK symbol<const char* (dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x61B8F0};
|
||||||
|
|
||||||
|
WEAK symbol<int(const char* fname)> generateHashValue{0x343D20};
|
||||||
|
|
||||||
|
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x759BA0};
|
||||||
|
|
||||||
|
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
|
||||||
|
float* color, Material* material)> R_AddCmdDrawStretchPic{0x3C9710};
|
||||||
|
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, float xScale, float yScale,
|
||||||
|
float rotation, float* color, int style)> R_AddCmdDrawText{0x76C660};
|
||||||
|
WEAK symbol<void(const char*, int, Font_s*, float, float, float, float, float, const float*, int, int, char)>
|
||||||
|
R_AddCmdDrawTextWithCursor{0x769D90};
|
||||||
|
WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x746FE0};
|
||||||
|
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x7472A0};
|
||||||
|
|
||||||
|
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{0x3E16A0};
|
||||||
|
|
||||||
|
WEAK symbol<void()> Sys_ShowConsole{0x633080};
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
WEAK symbol<cmd_function_s*> cmd_functions{0xAD17BB8};
|
||||||
|
WEAK symbol<CmdArgs> cmd_args{0xAD17A60};
|
||||||
|
|
||||||
|
WEAK symbol<const char*> g_assetNames{0xBEF280};
|
||||||
|
|
||||||
|
WEAK symbol<gentity_s> g_entities{0x52DDEC0};
|
||||||
|
|
||||||
|
WEAK symbol<int> keyCatchers{0x203F3C0};
|
||||||
|
|
||||||
|
WEAK symbol<PlayerKeyState> playerKeys{0x1E8767C};
|
||||||
|
|
||||||
|
WEAK symbol<int> dvarCount{0xBFBB310};
|
||||||
|
WEAK symbol<dvar_t*> sortedDvars{0xBFBB320};
|
||||||
|
}
|
1
src/stdinc.cpp
Normal file
1
src/stdinc.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "stdinc.hpp"
|
38
src/stdinc.hpp
Normal file
38
src/stdinc.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(disable: 4244)
|
||||||
|
#pragma warning(disable: 26812)
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <thread>
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
#include <MinHook.h>
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
#include "utils/memory.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "utils/hook.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
|
#include "game/structs.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "component/command.hpp"
|
||||||
|
#include "component/scheduler.hpp"
|
||||||
|
#include "component/input.hpp"
|
||||||
|
#include "component/game_console.hpp"
|
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_{};
|
||||||
|
};
|
||||||
|
}
|
180
src/utils/hook.cpp
Normal file
180
src/utils/hook.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include "stdinc.hpp"
|
||||||
|
#include "hook.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
namespace utils::hook
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
[[maybe_unused]] class _
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_()
|
||||||
|
{
|
||||||
|
if (MH_Initialize() != MH_OK)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to initialize MinHook");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~_()
|
||||||
|
{
|
||||||
|
MH_Uninitialize();
|
||||||
|
}
|
||||||
|
} __;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 jump(void* pointer, void* data, const bool use_far)
|
||||||
|
{
|
||||||
|
static const unsigned char jump_data[] = {
|
||||||
|
0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xe0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!use_far && is_relatively_far(pointer, data))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* patch_pointer = PBYTE(pointer);
|
||||||
|
|
||||||
|
if (use_far)
|
||||||
|
{
|
||||||
|
copy(patch_pointer, jump_data, sizeof(jump_data));
|
||||||
|
copy(patch_pointer + 2, &data, sizeof(data));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set<uint8_t>(patch_pointer, 0xE9);
|
||||||
|
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump(const size_t pointer, void* data, const bool use_far)
|
||||||
|
{
|
||||||
|
return jump(reinterpret_cast<void*>(pointer), data, use_far);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump(const size_t pointer, const size_t data, const bool use_far)
|
||||||
|
{
|
||||||
|
return jump(pointer, reinterpret_cast<void*>(data), use_far);
|
||||||
|
}
|
||||||
|
}
|
121
src/utils/hook.hpp
Normal file
121
src/utils/hook.hpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils::hook
|
||||||
|
{
|
||||||
|
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(void* pointer, void* data, bool use_far = false);
|
||||||
|
void jump(size_t pointer, void* data, bool use_far = false);
|
||||||
|
void jump(size_t pointer, size_t data, bool use_far = false);
|
||||||
|
|
||||||
|
void inject(void* pointer, const void* data);
|
||||||
|
void inject(size_t pointer, const void* data);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* follow_branch(void* address);
|
||||||
|
|
||||||
|
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...);
|
||||||
|
}
|
||||||
|
}
|
112
src/utils/memory.cpp
Normal file
112
src/utils/memory.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// https://github.com/momo5502/open-iw5
|
||||||
|
|
||||||
|
#include "stdinc.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)
|
||||||
|
{
|
||||||
|
const auto data = calloc(length, 1);
|
||||||
|
assert(data != nullptr);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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_;
|
||||||
|
}
|
||||||
|
}
|
70
src/utils/memory.hpp
Normal file
70
src/utils/memory.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// https://github.com/momo5502/open-iw5
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
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>
|
||||||
|
T* allocate()
|
||||||
|
{
|
||||||
|
return this->allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
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 allocator* get_allocator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static allocator mem_allocator_;
|
||||||
|
};
|
||||||
|
}
|
124
src/utils/string.cpp
Normal file
124
src/utils/string.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// iw6x-client
|
||||||
|
|
||||||
|
#include "stdinc.hpp"
|
||||||
|
|
||||||
|
namespace utils::string
|
||||||
|
{
|
||||||
|
const char* va(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static thread_local va_provider<8, 256> provider;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
const char* result = provider.get(fmt, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<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 CHAR(tolower(input));
|
||||||
|
});
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_upper(std::string text)
|
||||||
|
{
|
||||||
|
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||||
|
{
|
||||||
|
return CHAR(toupper(input));
|
||||||
|
});
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool starts_with(const std::string& text, const std::string& substring)
|
||||||
|
{
|
||||||
|
return text.find(substring) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < data.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
result.append(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(va("%02X", data[i] & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_clipboard_data()
|
||||||
|
{
|
||||||
|
if (OpenClipboard(0))
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
const auto clipboard_data = GetClipboardData(1u);
|
||||||
|
if (clipboard_data)
|
||||||
|
{
|
||||||
|
const auto cliptext = static_cast<char*>(GlobalLock(clipboard_data));
|
||||||
|
if (cliptext)
|
||||||
|
{
|
||||||
|
data.append(cliptext);
|
||||||
|
GlobalUnlock(clipboard_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseClipboard();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void strip(const char* in, char* out, int max)
|
||||||
|
{
|
||||||
|
if (!in || !out) return;
|
||||||
|
|
||||||
|
max--;
|
||||||
|
auto current = 0;
|
||||||
|
while (*in != 0 && current < max)
|
||||||
|
{
|
||||||
|
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
|
||||||
|
|
||||||
|
if (*in == '^' && (color_index != 7 || *(in + 1) == '7'))
|
||||||
|
{
|
||||||
|
++in;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out = *in;
|
||||||
|
++out;
|
||||||
|
++current;
|
||||||
|
}
|
||||||
|
|
||||||
|
++in;
|
||||||
|
}
|
||||||
|
*out = '\0';
|
||||||
|
}
|
||||||
|
}
|
88
src/utils/string.hpp
Normal file
88
src/utils/string.hpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "stdinc.hpp"
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||||
|
|
||||||
|
std::string get_clipboard_data();
|
||||||
|
|
||||||
|
void strip(const char* in, char* out, int max);
|
||||||
|
}
|
BIN
tools/windows/premake5.exe
Normal file
BIN
tools/windows/premake5.exe
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user