mirror of
https://github.com/alicealys/t5-gsc-utils.git
synced 2025-04-16 11:12:54 +00:00
Initial commit
This commit is contained in:
parent
768b1358c0
commit
cdd2c51568
150
.gitignore
vendored
Normal file
150
.gitignore
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
### Windows
|
||||
|
||||
version.hpp
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Shortcuts
|
||||
*.lnk
|
||||
|
||||
### OSX
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Visual Studio
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
build
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
### IDA
|
||||
*.id0
|
||||
*.id1
|
||||
*.id2
|
||||
*.nam
|
||||
*.til
|
||||
|
||||
### Custom user files
|
||||
# User scripts
|
||||
user*.bat
|
||||
*.code-workspace
|
30
.gitmodules
vendored
Normal file
30
.gitmodules
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
[submodule "minhook"]
|
||||
path = minhook
|
||||
url = https://github.com/TsudaKageyu/minhook.git
|
||||
[submodule "GSL"]
|
||||
path = GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "zlib"]
|
||||
path = zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
url = https://github.com/asmjit/asmjit.git
|
||||
[submodule "deps/asmjit"]
|
||||
path = deps/asmjit
|
||||
url = https://github.com/asmjit/asmjit.git
|
||||
[submodule "deps/json"]
|
||||
path = deps/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/minhook"]
|
||||
path = deps/minhook
|
||||
url = https://github.com/TsudaKageyu/minhook.git
|
||||
[submodule "deps/curl"]
|
||||
path = deps/curl
|
||||
url = https://github.com/curl/curl.git
|
1
deps/GSL
vendored
Submodule
1
deps/GSL
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d9fc52e20e32fcc804502480a781120b210afd41
|
1
deps/asmjit
vendored
Submodule
1
deps/asmjit
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 33a31f04e83845d15effbb9ca27a8f117205baf8
|
1
deps/curl
vendored
Submodule
1
deps/curl
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 134963a5efdc3906257c88ce62dba8d46c292908
|
1
deps/json
vendored
Submodule
1
deps/json
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b6d00d18974358ab1d55dcc3379d0ee3052deb4e
|
1
deps/minhook
vendored
Submodule
1
deps/minhook
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18
|
34
deps/premake/asmjit.lua
vendored
Normal file
34
deps/premake/asmjit.lua
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
asmjit = {
|
||||
source = path.join(dependencies.basePath, "asmjit"),
|
||||
}
|
||||
|
||||
function asmjit.import()
|
||||
links { "asmjit" }
|
||||
asmjit.includes()
|
||||
end
|
||||
|
||||
function asmjit.includes()
|
||||
includedirs {
|
||||
path.join(asmjit.source, "src")
|
||||
}
|
||||
|
||||
defines {
|
||||
"ASMJIT_STATIC"
|
||||
}
|
||||
end
|
||||
|
||||
function asmjit.project()
|
||||
project "asmjit"
|
||||
language "C++"
|
||||
|
||||
asmjit.includes()
|
||||
|
||||
files {
|
||||
path.join(asmjit.source, "src/**.cpp"),
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, asmjit)
|
73
deps/premake/curl.lua
vendored
Normal file
73
deps/premake/curl.lua
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
curl = {
|
||||
source = path.join(dependencies.basePath, "curl"),
|
||||
}
|
||||
|
||||
function curl.import()
|
||||
links { "curl" }
|
||||
|
||||
filter "toolset:msc*"
|
||||
links { "Crypt32.lib" }
|
||||
filter {}
|
||||
|
||||
curl.includes()
|
||||
end
|
||||
|
||||
function curl.includes()
|
||||
filter "toolset:msc*"
|
||||
includedirs {
|
||||
path.join(curl.source, "include"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"CURL_STRICTER",
|
||||
"CURL_STATICLIB",
|
||||
"CURL_DISABLE_LDAP",
|
||||
}
|
||||
filter {}
|
||||
end
|
||||
|
||||
function curl.project()
|
||||
if not os.istarget("windows") then
|
||||
return
|
||||
end
|
||||
|
||||
project "curl"
|
||||
language "C"
|
||||
|
||||
curl.includes()
|
||||
|
||||
includedirs {
|
||||
path.join(curl.source, "lib"),
|
||||
}
|
||||
|
||||
files {
|
||||
path.join(curl.source, "lib/**.c"),
|
||||
path.join(curl.source, "lib/**.h"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"BUILDING_LIBCURL",
|
||||
}
|
||||
|
||||
filter "toolset:msc*"
|
||||
|
||||
defines {
|
||||
"USE_SCHANNEL",
|
||||
"USE_WINDOWS_SSPI",
|
||||
"USE_THREADS_WIN32",
|
||||
}
|
||||
|
||||
filter "toolset:not msc*"
|
||||
|
||||
defines {
|
||||
"USE_GNUTLS",
|
||||
"USE_THREADS_POSIX",
|
||||
}
|
||||
|
||||
filter {}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, curl)
|
19
deps/premake/gsl.lua
vendored
Normal file
19
deps/premake/gsl.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
gsl = {
|
||||
source = path.join(dependencies.basePath, "GSL"),
|
||||
}
|
||||
|
||||
function gsl.import()
|
||||
gsl.includes()
|
||||
end
|
||||
|
||||
function gsl.includes()
|
||||
includedirs {
|
||||
path.join(gsl.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function gsl.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, gsl)
|
19
deps/premake/json.lua
vendored
Normal file
19
deps/premake/json.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
json = {
|
||||
source = path.join(dependencies.basePath, "json"),
|
||||
}
|
||||
|
||||
function json.import()
|
||||
json.includes()
|
||||
end
|
||||
|
||||
function json.includes()
|
||||
includedirs {
|
||||
path.join(json.source, "single_include/*")
|
||||
}
|
||||
end
|
||||
|
||||
function json.project()
|
||||
|
||||
end
|
||||
|
||||
table.insert(dependencies, json)
|
31
deps/premake/minhook.lua
vendored
Normal file
31
deps/premake/minhook.lua
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
minhook = {
|
||||
source = path.join(dependencies.basePath, "minhook"),
|
||||
}
|
||||
|
||||
function minhook.import()
|
||||
links { "minhook" }
|
||||
minhook.includes()
|
||||
end
|
||||
|
||||
function minhook.includes()
|
||||
includedirs {
|
||||
path.join(minhook.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function minhook.project()
|
||||
project "minhook"
|
||||
language "C"
|
||||
|
||||
minhook.includes()
|
||||
|
||||
files {
|
||||
path.join(minhook.source, "src/**.h"),
|
||||
path.join(minhook.source, "src/**.c"),
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, minhook)
|
43
deps/premake/minizip.lua
vendored
Normal file
43
deps/premake/minizip.lua
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
minizip = {
|
||||
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
|
||||
}
|
||||
|
||||
function minizip.import()
|
||||
links { "minizip" }
|
||||
zlib.import()
|
||||
minizip.includes()
|
||||
end
|
||||
|
||||
function minizip.includes()
|
||||
includedirs {
|
||||
minizip.source
|
||||
}
|
||||
|
||||
zlib.includes()
|
||||
end
|
||||
|
||||
function minizip.project()
|
||||
project "minizip"
|
||||
language "C"
|
||||
|
||||
minizip.includes()
|
||||
|
||||
files {
|
||||
path.join(minizip.source, "*.h"),
|
||||
path.join(minizip.source, "*.c"),
|
||||
}
|
||||
|
||||
removefiles {
|
||||
path.join(minizip.source, "miniunz.c"),
|
||||
path.join(minizip.source, "minizip.c"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"_CRT_SECURE_NO_DEPRECATE",
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, minizip)
|
39
deps/premake/zlib.lua
vendored
Normal file
39
deps/premake/zlib.lua
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
zlib = {
|
||||
source = path.join(dependencies.basePath, "zlib"),
|
||||
}
|
||||
|
||||
function zlib.import()
|
||||
links { "zlib" }
|
||||
zlib.includes()
|
||||
end
|
||||
|
||||
function zlib.includes()
|
||||
includedirs {
|
||||
zlib.source
|
||||
}
|
||||
|
||||
defines {
|
||||
"ZLIB_CONST",
|
||||
}
|
||||
end
|
||||
|
||||
function zlib.project()
|
||||
project "zlib"
|
||||
language "C"
|
||||
|
||||
zlib.includes()
|
||||
|
||||
files {
|
||||
path.join(zlib.source, "*.h"),
|
||||
path.join(zlib.source, "*.c"),
|
||||
}
|
||||
|
||||
defines {
|
||||
"_CRT_SECURE_NO_DEPRECATE",
|
||||
}
|
||||
|
||||
warnings "Off"
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, zlib)
|
1
deps/zlib
vendored
Submodule
1
deps/zlib
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 21767c654d31d2dccdde4330529775c6c5fd5389
|
3
generate.bat
Normal file
3
generate.bat
Normal file
@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call git submodule update --init --recursive
|
||||
tools\windows\premake5.exe vs2022
|
100
premake5.lua
Normal file
100
premake5.lua
Normal file
@ -0,0 +1,100 @@
|
||||
dependencies = {
|
||||
basePath = "./deps"
|
||||
}
|
||||
|
||||
function dependencies.load()
|
||||
dir = path.join(dependencies.basePath, "premake/*.lua")
|
||||
deps = os.matchfiles(dir)
|
||||
|
||||
for i, dep in pairs(deps) do
|
||||
dep = dep:gsub(".lua", "")
|
||||
require(dep)
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.imports()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.import()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dependencies.projects()
|
||||
for i, proj in pairs(dependencies) do
|
||||
if type(i) == 'number' then
|
||||
proj.project()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dependencies.load()
|
||||
|
||||
workspace "t5-gsc-utils"
|
||||
location "./build"
|
||||
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
||||
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||
targetname "%{prj.name}"
|
||||
|
||||
configurations { "Debug", "Release", }
|
||||
|
||||
language "C++"
|
||||
cppdialect "C++20"
|
||||
|
||||
architecture "x86"
|
||||
|
||||
systemversion "latest"
|
||||
symbols "On"
|
||||
staticruntime "On"
|
||||
editandcontinue "Off"
|
||||
warnings "Extra"
|
||||
characterset "ASCII"
|
||||
|
||||
flags
|
||||
{
|
||||
"NoIncrementalLink",
|
||||
"MultiProcessorCompile",
|
||||
}
|
||||
|
||||
filter "configurations:Release"
|
||||
optimize "Full"
|
||||
defines { "NDEBUG" }
|
||||
filter {}
|
||||
|
||||
filter "configurations:Debug"
|
||||
optimize "Debug"
|
||||
defines { "DEBUG", "_DEBUG" }
|
||||
filter {}
|
||||
|
||||
startproject "t5-gsc-utils"
|
||||
|
||||
project "t5-gsc-utils"
|
||||
kind "SharedLib"
|
||||
language "C++"
|
||||
|
||||
files
|
||||
{
|
||||
"./src/**.h",
|
||||
"./src/**.hpp",
|
||||
"./src/**.cpp",
|
||||
}
|
||||
|
||||
includedirs
|
||||
{
|
||||
"%{prj.location}/src",
|
||||
"./src",
|
||||
}
|
||||
|
||||
resincludedirs
|
||||
{
|
||||
"$(ProjectDir)src"
|
||||
}
|
||||
|
||||
pchheader "stdinc.hpp"
|
||||
pchsource "src/stdinc.cpp"
|
||||
buildoptions { "/Zm100 -Zm100" }
|
||||
|
||||
dependencies.imports()
|
||||
|
||||
group "Dependencies"
|
||||
dependencies.projects()
|
145
src/component/exception.cpp
Normal file
145
src/component/exception.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include "gsc.hpp"
|
||||
#include "json.hpp"
|
||||
#include "scripting.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/thread.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
|
||||
#include <exception/minidump.hpp>
|
||||
|
||||
namespace exception
|
||||
{
|
||||
namespace
|
||||
{
|
||||
thread_local struct
|
||||
{
|
||||
DWORD code = 0;
|
||||
PVOID address = nullptr;
|
||||
} exception_data;
|
||||
|
||||
void show_mouse_cursor()
|
||||
{
|
||||
while (ShowCursor(TRUE) < 0);
|
||||
}
|
||||
|
||||
void display_error_dialog()
|
||||
{
|
||||
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n"
|
||||
"A minidump has been written.\n\n",
|
||||
exception_data.code, exception_data.address);
|
||||
|
||||
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
|
||||
|
||||
utils::thread::suspend_other_threads();
|
||||
show_mouse_cursor();
|
||||
MessageBoxA(nullptr, error_str.data(), "Plutonium T6 ERROR", MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), exception_data.code);
|
||||
}
|
||||
|
||||
void reset_state()
|
||||
{
|
||||
display_error_dialog();
|
||||
}
|
||||
|
||||
size_t get_reset_state_stub()
|
||||
{
|
||||
static auto* stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.sub(esp, 0x10);
|
||||
a.or_(esp, 0x8);
|
||||
a.jmp(reset_state);
|
||||
});
|
||||
|
||||
return reinterpret_cast<size_t>(stub);
|
||||
}
|
||||
|
||||
std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
std::string info{};
|
||||
const auto line = [&info](const std::string& text)
|
||||
{
|
||||
info.append(text);
|
||||
info.append("\r\n");
|
||||
};
|
||||
|
||||
line("Plutonium T6 Crash Dump");
|
||||
line("");
|
||||
line("Timestamp: "s + utils::string::get_timestamp());
|
||||
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
|
||||
line(utils::string::va("Address: 0x%lX", exceptioninfo->ExceptionRecord->ExceptionAddress));
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996)
|
||||
OSVERSIONINFOEXA version_info;
|
||||
ZeroMemory(&version_info, sizeof(version_info));
|
||||
version_info.dwOSVersionInfoSize = sizeof(version_info);
|
||||
GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&version_info));
|
||||
#pragma warning(pop)
|
||||
|
||||
line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
const std::string crash_name = utils::string::va("minidumps/plutonium-t6-crash-%s.zip",
|
||||
utils::string::get_timestamp().data());
|
||||
|
||||
utils::compression::zip::archive zip_file{};
|
||||
zip_file.add("crash.dmp", create_minidump(exceptioninfo));
|
||||
zip_file.add("info.txt", generate_crash_info(exceptioninfo));
|
||||
zip_file.write(crash_name, "Plutonium T6 Crash Dump");
|
||||
}
|
||||
|
||||
bool is_harmless_error(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
const auto code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||
return code == STATUS_INTEGER_OVERFLOW || code == STATUS_FLOAT_OVERFLOW || code == STATUS_SINGLE_STEP;
|
||||
}
|
||||
|
||||
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
if (is_harmless_error(exceptioninfo))
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
write_minidump(exceptioninfo);
|
||||
|
||||
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
|
||||
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
|
||||
exceptioninfo->ContextRecord->Eip = get_reset_state_stub();
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI set_unhandled_exception_filter_stub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
||||
{
|
||||
// Don't register anything here...
|
||||
return &exception_filter;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
SetUnhandledExceptionFilter(exception_filter);
|
||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(exception::component)
|
230
src/component/gsc.cpp
Normal file
230
src/component/gsc.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "gsc.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour get_function_hook;
|
||||
utils::hook::detour get_method_hook;
|
||||
|
||||
std::unordered_map<std::string, std::function<script_value(const function_arguments& args)>> functions;
|
||||
std::unordered_map<std::string, std::function<script_value(const function_arguments& args)>> methods;
|
||||
|
||||
std::unordered_map<std::string, void*> function_wraps;
|
||||
std::unordered_map<std::string, void*> method_wraps;
|
||||
|
||||
std::vector<script_value> get_arguments()
|
||||
{
|
||||
std::vector<script_value> args;
|
||||
|
||||
for (auto i = 0; static_cast<unsigned int>(i) < game::scr_VmPub->outparamcount; i++)
|
||||
{
|
||||
const auto value = game::scr_VmPub->top[-i];
|
||||
args.push_back(value);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
void return_value(const scripting::script_value& value)
|
||||
{
|
||||
if (game::scr_VmPub->outparamcount)
|
||||
{
|
||||
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
|
||||
}
|
||||
|
||||
push_value(value);
|
||||
}
|
||||
|
||||
void call_function(const char* name)
|
||||
{
|
||||
if (functions.find(name) == functions.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto args = get_arguments();
|
||||
const auto& function = functions[name];
|
||||
|
||||
try
|
||||
{
|
||||
const auto value = function(args);
|
||||
return_value(value);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void call_method(const char* name, const game::scr_entref_t entref)
|
||||
{
|
||||
if (methods.find(name) == methods.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto args = get_arguments();
|
||||
const auto& method = methods[name];
|
||||
|
||||
try
|
||||
{
|
||||
const entity entity = game::Scr_GetEntityId(
|
||||
game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0);
|
||||
|
||||
std::vector<script_value> args_{};
|
||||
args_.push_back(entity);
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
args_.push_back(arg);
|
||||
}
|
||||
|
||||
const auto value = method(args_);
|
||||
return_value(value);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
game::Scr_Error(game::SCRIPTINSTANCE_SERVER, e.what(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void* wrap_function_call(const std::string& name)
|
||||
{
|
||||
const auto name_ = utils::memory::get_allocator()->duplicate_string(name);
|
||||
return utils::hook::assemble([name_](utils::hook::assembler& a)
|
||||
{
|
||||
a.pushad();
|
||||
a.push(name_);
|
||||
a.call(call_function);
|
||||
a.add(esp, 0x4);
|
||||
a.popad();
|
||||
|
||||
a.ret();
|
||||
});
|
||||
}
|
||||
|
||||
void* wrap_method_call(const std::string& name)
|
||||
{
|
||||
const auto name_ = utils::memory::get_allocator()->duplicate_string(name);
|
||||
return utils::hook::assemble([name_](utils::hook::assembler& a)
|
||||
{
|
||||
a.pushad();
|
||||
a.push(dword_ptr(esp, 0x24));
|
||||
a.push(name_);
|
||||
a.call(call_method);
|
||||
a.add(esp, 0x8);
|
||||
a.popad();
|
||||
|
||||
a.ret();
|
||||
});
|
||||
}
|
||||
|
||||
script_function get_function_stub(const char** name, int* type)
|
||||
{
|
||||
if (function_wraps.find(*name) != function_wraps.end())
|
||||
{
|
||||
return reinterpret_cast<script_function>(function_wraps[*name]);
|
||||
}
|
||||
|
||||
return get_function_hook.invoke<script_function>(name, type);
|
||||
}
|
||||
|
||||
script_function get_method_stub(const char** name, int* type)
|
||||
{
|
||||
if (method_wraps.find(*name) != method_wraps.end())
|
||||
{
|
||||
return reinterpret_cast<script_function>(method_wraps[*name]);
|
||||
}
|
||||
|
||||
return get_method_hook.invoke<script_function>(name, type);
|
||||
}
|
||||
|
||||
void print(int, const char* fmt, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buffer);
|
||||
}
|
||||
|
||||
utils::hook::detour scr_settings_hook;
|
||||
void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst)
|
||||
{
|
||||
scr_settings_hook.invoke<void>(0x55D010, developer_script, developer_script, 0, inst);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void add_function(const std::string& name, F f)
|
||||
{
|
||||
const auto wrap = wrap_function(f);
|
||||
functions[name] = wrap;
|
||||
const auto call_wrap = wrap_function_call(name);
|
||||
function_wraps[name] = call_wrap;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void add_method(const std::string& name, F f)
|
||||
{
|
||||
const auto wrap = wrap_function(f);
|
||||
methods[name] = wrap;
|
||||
const auto call_wrap = wrap_method_call(name);
|
||||
method_wraps[name] = call_wrap;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// Don't com_error on gsc errors
|
||||
utils::hook::nop(SELECT_VALUE(0, 0x4D9BB1), 5);
|
||||
utils::hook::jump(0x568B90, print);
|
||||
|
||||
scr_settings_hook.create(SELECT_VALUE(0x0, 0x55D010), scr_settings_stub);
|
||||
get_function_hook.create(SELECT_VALUE(0x0, 0x465E20), get_function_stub);
|
||||
get_method_hook.create(SELECT_VALUE(0x0, 0x555580), get_method_stub);
|
||||
|
||||
add_function("print_", [](const variadic_args& args)
|
||||
{
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
printf("%s\t", arg.to_string().data());
|
||||
}
|
||||
printf("\n");
|
||||
});
|
||||
|
||||
add_function("fileexists", utils::io::file_exists);
|
||||
add_function("writefile", utils::io::write_file);
|
||||
add_function("movefile", utils::io::move_file);
|
||||
add_function("filesize", utils::io::file_size);
|
||||
add_function("createdirectory", utils::io::create_directory);
|
||||
add_function("directoryexists", utils::io::directory_exists);
|
||||
add_function("directoryisempty", utils::io::directory_is_empty);
|
||||
add_function("listfiles", utils::io::list_files);
|
||||
add_function("removefile", utils::io::remove_file);
|
||||
add_function("readfile", static_cast<std::string(*)(const std::string&)>(utils::io::read_file));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(scripting::component)
|
54
src/component/gsc.hpp
Normal file
54
src/component/gsc.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "game/scripting/array.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
using script_function = void(*)(game::scr_entref_t);
|
||||
|
||||
template <class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f]([[maybe_unused]] const function_arguments& args)
|
||||
{
|
||||
f(args[I]...);
|
||||
return script_value{};
|
||||
};
|
||||
}
|
||||
|
||||
template <class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<script_value(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f]([[maybe_unused]] const function_arguments& args)
|
||||
{
|
||||
return f(args[I]...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename R, class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f]([[maybe_unused]] const function_arguments& args)
|
||||
{
|
||||
return script_value{f(args[I]...)};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename R, class... Args>
|
||||
auto wrap_function(const std::function<R(Args...)>& f)
|
||||
{
|
||||
return wrap_function(f, std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
template <class F>
|
||||
auto wrap_function(F f)
|
||||
{
|
||||
return wrap_function(std::function(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void add_function(const std::string& name, F f);
|
||||
|
||||
template <typename F>
|
||||
void add_method(const std::string& name, F f);
|
||||
}
|
159
src/component/scheduler.cpp
Normal file
159
src/component/scheduler.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.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 = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
std::thread thread;
|
||||
task_pipeline pipelines[pipeline::count];
|
||||
|
||||
void execute(const pipeline type)
|
||||
{
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
pipelines[type].execute();
|
||||
}
|
||||
|
||||
void execute_server()
|
||||
{
|
||||
execute(pipeline::server);
|
||||
}
|
||||
|
||||
void server_frame_stub(utils::hook::assembler& a)
|
||||
{
|
||||
a.pushad();
|
||||
a.call(execute_server);
|
||||
a.popad();
|
||||
|
||||
a.jmp(SELECT_VALUE(0x0, 0x0));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
thread = std::thread([]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
execute(pipeline::async);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//REGISTER_COMPONENT(scheduler::component)
|
21
src/component/scheduler.hpp
Normal file
21
src/component/scheduler.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace scheduler
|
||||
{
|
||||
enum pipeline
|
||||
{
|
||||
server,
|
||||
async,
|
||||
count,
|
||||
};
|
||||
|
||||
static const bool cond_continue = false;
|
||||
static const bool cond_end = true;
|
||||
|
||||
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::server,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void loop(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
void once(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
}
|
98
src/component/scripting.cpp
Normal file
98
src/component/scripting.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "game/scripting/event.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
|
||||
#include "scripting.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||
|
||||
std::unordered_map<std::string, game::BuiltinMethodDef> method_map;
|
||||
std::unordered_map<std::string, game::BuiltinFunctionDef> function_map;
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
||||
std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
|
||||
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour scr_add_class_field_hook;
|
||||
utils::hook::detour g_shutdown_game_hook;
|
||||
|
||||
void scr_add_class_field_stub(game::scriptInstance_t inst, unsigned int classnum, char const* name, unsigned int offset)
|
||||
{
|
||||
if (fields_table[classnum].find(name) == fields_table[classnum].end())
|
||||
{
|
||||
fields_table[classnum][name] = offset;
|
||||
}
|
||||
|
||||
scr_add_class_field_hook.invoke<void>(inst, classnum, name, offset);
|
||||
}
|
||||
|
||||
game::BuiltinMethodDef get_method(const std::string& name)
|
||||
{
|
||||
game::BuiltinMethodDef method{};
|
||||
|
||||
auto pName = name.data();
|
||||
int arg = 0;
|
||||
|
||||
const auto func = game::Scr_GetMethod(&pName, &arg, &arg, &arg);
|
||||
|
||||
if (func)
|
||||
{
|
||||
method.actionFunc = reinterpret_cast<script_function>(func);
|
||||
method.actionString = pName;
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
void load_functions()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
utils::hook::detour scr_load_script_hook;
|
||||
|
||||
std::vector<std::function<void()>> shutdown_callbacks;
|
||||
void g_shutdown_game_stub(const int free_scripts)
|
||||
{
|
||||
for (const auto& callback : shutdown_callbacks)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
|
||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||
}
|
||||
}
|
||||
|
||||
void on_shutdown(const std::function<void()>& callback)
|
||||
{
|
||||
shutdown_callbacks.push_back(callback);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
//g_shutdown_game_hook.create(SELECT(0x60DCF0, 0x688A40), g_shutdown_game_stub);
|
||||
|
||||
//scr_add_class_field_hook.create(SELECT(0x6B7620, 0x438AD0), scr_add_class_field_stub);
|
||||
//scr_post_load_scripts_hook.create(SELECT(0x642EB0, 0x425F80), post_load_scripts_stub);
|
||||
|
||||
//load_functions();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//REGISTER_COMPONENT(scripting::component)
|
22
src/component/scripting.hpp
Normal file
22
src/component/scripting.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||
|
||||
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
||||
extern std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
|
||||
|
||||
extern std::unordered_map<std::string, game::BuiltinMethodDef> method_map;
|
||||
extern std::unordered_map<std::string, game::BuiltinFunctionDef> function_map;
|
||||
|
||||
using script_function = void(*)(game::scr_entref_t);
|
||||
|
||||
script_function find_function_ptr(const std::string& name);
|
||||
std::string find_function(const char* pos);
|
||||
const char* find_function_start(const char* pos);
|
||||
std::optional<std::pair<std::string, std::string>> find_function_pair(const char* pos);
|
||||
|
||||
void on_shutdown(const std::function<void()>& callback);
|
||||
}
|
21
src/dllmain.cpp
Normal file
21
src/dllmain.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
||||
{
|
||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
if (game::plutonium::is_up_to_date())
|
||||
{
|
||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), game::plutonium::printf);
|
||||
}
|
||||
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
94
src/exception/minidump.cpp
Normal file
94
src/exception/minidump.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "minidump.hpp"
|
||||
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
namespace exception
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr MINIDUMP_TYPE get_minidump_type()
|
||||
{
|
||||
const auto type = MiniDumpIgnoreInaccessibleMemory //
|
||||
| MiniDumpWithHandleData //
|
||||
| MiniDumpScanMemory //
|
||||
| MiniDumpWithProcessThreadData //
|
||||
| MiniDumpWithFullMemoryInfo //
|
||||
| MiniDumpWithThreadInfo //
|
||||
| MiniDumpWithUnloadedModules;
|
||||
|
||||
return static_cast<MINIDUMP_TYPE>(type);
|
||||
}
|
||||
|
||||
std::string get_temp_filename()
|
||||
{
|
||||
char filename[MAX_PATH] = {0};
|
||||
char pathname[MAX_PATH] = {0};
|
||||
|
||||
GetTempPathA(sizeof(pathname), pathname);
|
||||
GetTempFileNameA(pathname, "Plutonium T6 -", 0, filename);
|
||||
return filename;
|
||||
}
|
||||
|
||||
HANDLE write_dump_to_temp_file(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION minidump_exception_info = {GetCurrentThreadId(), exceptioninfo, FALSE};
|
||||
|
||||
auto* const file_handle = CreateFileA(get_temp_filename().data(), GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
|
||||
nullptr);
|
||||
|
||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, get_minidump_type(),
|
||||
&minidump_exception_info,
|
||||
nullptr,
|
||||
nullptr))
|
||||
{
|
||||
MessageBoxA(nullptr, "There was an error creating the minidump! Hit OK to close the program.",
|
||||
"Minidump Error", MB_OK | MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), 123);
|
||||
}
|
||||
|
||||
return file_handle;
|
||||
}
|
||||
|
||||
std::string read_file(HANDLE file_handle)
|
||||
{
|
||||
FlushFileBuffers(file_handle);
|
||||
SetFilePointer(file_handle, 0, nullptr, FILE_BEGIN);
|
||||
|
||||
std::string buffer{};
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
char temp_bytes[0x2000];
|
||||
|
||||
do
|
||||
{
|
||||
if (!ReadFile(file_handle, temp_bytes, sizeof(temp_bytes), &bytes_read, nullptr))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.append(temp_bytes, bytes_read);
|
||||
}
|
||||
while (bytes_read == sizeof(temp_bytes));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
std::string create_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
|
||||
{
|
||||
auto* const file_handle = write_dump_to_temp_file(exceptioninfo);
|
||||
|
||||
const auto _ = gsl::finally([file_handle]()
|
||||
{
|
||||
CloseHandle(file_handle);
|
||||
});
|
||||
|
||||
return read_file(file_handle);
|
||||
}
|
||||
}
|
6
src/exception/minidump.hpp
Normal file
6
src/exception/minidump.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace exception
|
||||
{
|
||||
std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo);
|
||||
}
|
82
src/game/game.cpp
Normal file
82
src/game/game.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace game
|
||||
{
|
||||
gamemode current = *reinterpret_cast<int*>(0x401337) == 0x281488C0
|
||||
? gamemode::multiplayer
|
||||
: gamemode::singleplayer;
|
||||
|
||||
namespace environment
|
||||
{
|
||||
bool is_mp()
|
||||
{
|
||||
return current == gamemode::multiplayer;
|
||||
}
|
||||
|
||||
bool is_sp()
|
||||
{
|
||||
return current == gamemode::singleplayer;
|
||||
}
|
||||
}
|
||||
|
||||
void AddRefToValue(scriptInstance_t inst, const VariableValue* value)
|
||||
{
|
||||
AddRefToValue_(inst, value->type, value->u);
|
||||
}
|
||||
|
||||
unsigned int AllocVariable(scriptInstance_t inst)
|
||||
{
|
||||
static const auto func = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.mov(eax, 0);
|
||||
a.call(SELECT_VALUE(0, 0x8E49A0));
|
||||
a.ret();
|
||||
});
|
||||
|
||||
return utils::hook::invoke<unsigned int>(func, inst);
|
||||
}
|
||||
|
||||
VariableValue Scr_GetArrayIndexValue(scriptInstance_t inst, unsigned int name)
|
||||
{
|
||||
VariableValue value{};
|
||||
|
||||
if (name >= 0x10000)
|
||||
{
|
||||
if (name >= 0x17FFE)
|
||||
{
|
||||
value.type = 6;
|
||||
value.u.intValue = name - 0x800000;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.type = 1;
|
||||
value.u.intValue = name - 0x10000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value.type = 2;
|
||||
value.u.intValue = name;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned int Scr_GetSelf(scriptInstance_t inst, unsigned int threadId)
|
||||
{
|
||||
return game::scr_VarGlob->variableList[threadId + 1].u.o.u.self;
|
||||
}
|
||||
|
||||
namespace plutonium
|
||||
{
|
||||
bool is_up_to_date()
|
||||
{
|
||||
//const auto value = *reinterpret_cast<DWORD*>(0);
|
||||
//return value == 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
85
src/game/game.hpp
Normal file
85
src/game/game.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "structs.hpp"
|
||||
|
||||
#define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
|
||||
|
||||
namespace game
|
||||
{
|
||||
enum gamemode
|
||||
{
|
||||
none,
|
||||
multiplayer,
|
||||
singleplayer
|
||||
};
|
||||
|
||||
extern gamemode current;
|
||||
|
||||
namespace environment
|
||||
{
|
||||
bool is_mp();
|
||||
bool is_sp();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class symbol
|
||||
{
|
||||
public:
|
||||
symbol(const size_t sp_address, const size_t mp_address)
|
||||
: sp_object_(reinterpret_cast<T*>(sp_address))
|
||||
, mp_object_(reinterpret_cast<T*>(mp_address))
|
||||
{
|
||||
}
|
||||
|
||||
T* get() const
|
||||
{
|
||||
if (environment::is_mp())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mp_object_ == nullptr)
|
||||
{
|
||||
MessageBoxA(nullptr, "nullptr symbol", "", 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
return mp_object_;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (sp_object_ == nullptr)
|
||||
{
|
||||
MessageBoxA(nullptr, "nullptr symbol", "", 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
return sp_object_;
|
||||
}
|
||||
|
||||
operator T* () const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
private:
|
||||
T* sp_object_;
|
||||
T* mp_object_;
|
||||
};
|
||||
|
||||
void AddRefToValue(scriptInstance_t inst, const VariableValue* value);
|
||||
unsigned int AllocVariable(scriptInstance_t inst);
|
||||
|
||||
VariableValue Scr_GetArrayIndexValue(scriptInstance_t inst, unsigned int name);
|
||||
unsigned int Scr_GetSelf(scriptInstance_t inst, unsigned int threadId);
|
||||
|
||||
namespace plutonium
|
||||
{
|
||||
bool is_up_to_date();
|
||||
}
|
||||
}
|
||||
|
||||
#include "symbols.hpp"
|
324
src/game/scripting/array.cpp
Normal file
324
src/game/scripting/array.cpp
Normal file
@ -0,0 +1,324 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "array.hpp"
|
||||
#include "execution.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
array_value::array_value(unsigned int parent_id, unsigned int id)
|
||||
: id_(id)
|
||||
, parent_id_(parent_id)
|
||||
{
|
||||
if (!this->id_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto value = game::scr_VarGlob->variableList[this->id_];
|
||||
game::VariableValue variable{};
|
||||
variable.u = value.u.u;
|
||||
variable.type = value.w.type & 0x1F;
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
void array_value::operator=(const script_value& value)
|
||||
{
|
||||
if (!this->id_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& value_0 = value.get_raw();
|
||||
|
||||
const auto variable = &game::scr_VarGlob->variableList[this->id_];
|
||||
game::VariableValue variable_{};
|
||||
variable_.type = variable->w.type & 0x1F;
|
||||
variable_.u = variable->u.u;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_0);
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_.type, variable_.u);
|
||||
|
||||
variable->w.type = value_0.type & 0x1F;
|
||||
variable->u.u = value_0.u;
|
||||
|
||||
this->value_ = value_0;
|
||||
}
|
||||
|
||||
array::array(const unsigned int id)
|
||||
: id_(id)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
array::array(const array& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
array::array(array&& other) noexcept
|
||||
{
|
||||
this->id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
array::array()
|
||||
{
|
||||
this->id_ = game::Scr_AllocArray(game::SCRIPTINSTANCE_SERVER);
|
||||
}
|
||||
|
||||
array::~array()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
array& array::operator=(const array& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->id_ = other.id_;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
array& array::operator=(array&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void array::add() const
|
||||
{
|
||||
if (this->id_)
|
||||
{
|
||||
game::VariableValue value{};
|
||||
value.u.uintValue = this->id_;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
}
|
||||
}
|
||||
|
||||
void array::release() const
|
||||
{
|
||||
if (this->id_)
|
||||
{
|
||||
game::VariableValue value{};
|
||||
value.u.uintValue = this->id_;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<script_value> array::get_keys() const
|
||||
{
|
||||
std::vector<script_value> result;
|
||||
|
||||
auto current = game::scr_VarGlob->variableList[this->id_ + 1].nextSibling;
|
||||
|
||||
while (current)
|
||||
{
|
||||
const auto var = &game::scr_VarGlob->variableList[current + 0x8000];
|
||||
const auto key_value = game::Scr_GetArrayIndexValue(game::SCRIPTINSTANCE_SERVER, var->w.status >> 8);
|
||||
result.push_back(key_value);
|
||||
|
||||
const auto next_sibling = game::scr_VarGlob->variableList[current + 0x8000].nextSibling;
|
||||
if (!next_sibling)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = game::scr_VarGlob->variableList[next_sibling + 0x8000].hash.id;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int array::size() const
|
||||
{
|
||||
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
|
||||
}
|
||||
|
||||
unsigned int array::push(const script_value& value) const
|
||||
{
|
||||
this->set(this->size(), value);
|
||||
return this->size();
|
||||
}
|
||||
|
||||
void array::erase(const unsigned int index) const
|
||||
{
|
||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||
if (variable_id)
|
||||
{
|
||||
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
|
||||
}
|
||||
}
|
||||
|
||||
void array::erase(const std::string& key) const
|
||||
{
|
||||
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
if (variable_id)
|
||||
{
|
||||
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
|
||||
}
|
||||
}
|
||||
|
||||
script_value array::pop() const
|
||||
{
|
||||
const auto value = this->get(this->size() - 1);
|
||||
this->erase(this->size() - 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
script_value array::get(const std::string& key) const
|
||||
{
|
||||
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = game::scr_VarGlob->variableList[variable_id + 0x8000];
|
||||
game::VariableValue variable{};
|
||||
variable.u = value.u.u;
|
||||
variable.type = value.w.type & 0x1F;
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
script_value array::get(const unsigned int index) const
|
||||
{
|
||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = game::scr_VarGlob->variableList[variable_id + 0x8000];
|
||||
game::VariableValue variable{};
|
||||
variable.u = value.u.u;
|
||||
variable.type = value.w.type & 0x1F;
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
script_value array::get(const script_value& key) const
|
||||
{
|
||||
if (key.is<int>())
|
||||
{
|
||||
this->get(key.as<int>());
|
||||
}
|
||||
|
||||
if (key.is<std::string>())
|
||||
{
|
||||
this->get(key.as<std::string>());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void array::set(const std::string& key, const script_value& value) const
|
||||
{
|
||||
const auto& value_ = value.get_raw();
|
||||
const auto variable_id = this->get_value_id(key);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto variable = &game::scr_VarGlob->variableList[variable_id + 0x8000];
|
||||
game::VariableValue variable_{};
|
||||
variable_.type = variable->w.type & 0x1F;
|
||||
variable_.u = variable->u.u;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_);
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_.type, variable_.u);
|
||||
|
||||
variable->w.type |= value_.type;
|
||||
variable->u.u = value_.u;
|
||||
}
|
||||
|
||||
void array::set(const unsigned int index, const script_value& value) const
|
||||
{
|
||||
const auto& value_ = value.get_raw();
|
||||
const auto variable_id = this->get_value_id(index);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto variable_list = *game::scr_VarGlob;
|
||||
const auto variable = &game::scr_VarGlob->variableList[variable_id + 0x8000];
|
||||
game::VariableValue variable_{};
|
||||
variable_.type = variable->w.type & 0x1F;
|
||||
variable_.u = variable->u.u;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_);
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_.type, variable_.u);
|
||||
|
||||
variable->w.type |= value_.type;
|
||||
variable->u.u = value_.u;
|
||||
}
|
||||
|
||||
void array::set(const script_value& key, const script_value& _value) const
|
||||
{
|
||||
if (key.is<int>())
|
||||
{
|
||||
this->set(key.as<int>(), _value);
|
||||
}
|
||||
|
||||
if (key.is<std::string>())
|
||||
{
|
||||
this->set(key.as<std::string>(), _value);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int array::get_entity_id() const
|
||||
{
|
||||
return this->id_;
|
||||
}
|
||||
|
||||
unsigned int array::get_value_id(const std::string& key) const
|
||||
{
|
||||
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return game::GetNewVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
}
|
||||
|
||||
return variable_id;
|
||||
}
|
||||
|
||||
unsigned int array::get_value_id(const unsigned int index) const
|
||||
{
|
||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||
if (!variable_id)
|
||||
{
|
||||
return game::GetNewArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||
}
|
||||
|
||||
return variable_id;
|
||||
}
|
||||
|
||||
entity array::get_raw() const
|
||||
{
|
||||
return entity(this->id_);
|
||||
}
|
||||
}
|
83
src/game/scripting/array.hpp
Normal file
83
src/game/scripting/array.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class array_value : public script_value
|
||||
{
|
||||
public:
|
||||
array_value(unsigned int, unsigned int);
|
||||
void operator=(const script_value&);
|
||||
private:
|
||||
unsigned int id_;
|
||||
unsigned int parent_id_;
|
||||
};
|
||||
|
||||
class array final
|
||||
{
|
||||
public:
|
||||
array();
|
||||
array(const unsigned int);
|
||||
|
||||
array(const array& other);
|
||||
array(array&& other) noexcept;
|
||||
|
||||
~array();
|
||||
|
||||
array& operator=(const array& other);
|
||||
array& operator=(array&& other) noexcept;
|
||||
|
||||
std::vector<script_value> get_keys() const;
|
||||
unsigned int size() const;
|
||||
|
||||
unsigned int push(const script_value&) const;
|
||||
void erase(const unsigned int) const;
|
||||
void erase(const std::string&) const;
|
||||
script_value pop() const;
|
||||
|
||||
script_value get(const script_value&) const;
|
||||
script_value get(const std::string&) const;
|
||||
script_value get(const unsigned int) const;
|
||||
|
||||
void set(const script_value&, const script_value&) const;
|
||||
void set(const std::string&, const script_value&) const;
|
||||
void set(const unsigned int, const script_value&) const;
|
||||
|
||||
unsigned int get_entity_id() const;
|
||||
|
||||
unsigned int get_value_id(const std::string&) const;
|
||||
unsigned int get_value_id(const unsigned int) const;
|
||||
|
||||
entity get_raw() const;
|
||||
|
||||
array_value operator[](const int index) const
|
||||
{
|
||||
return {this->id_, this->get_value_id(index)};
|
||||
}
|
||||
|
||||
array_value operator[](const std::string& key) const
|
||||
{
|
||||
return {this->id_, this->get_value_id(key)};
|
||||
}
|
||||
|
||||
template <typename I = int, typename S = std::string>
|
||||
array_value operator[](const script_value& key) const
|
||||
{
|
||||
if (key.is<I>())
|
||||
{
|
||||
return {this->id_, this->get_value_id(key.as<I>())};
|
||||
}
|
||||
|
||||
if (key.is<S>())
|
||||
{
|
||||
return {this->id_, this->get_value_id(key.as<S>())};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void add() const;
|
||||
void release() const;
|
||||
|
||||
unsigned int id_{};
|
||||
};
|
||||
}
|
120
src/game/scripting/entity.cpp
Normal file
120
src/game/scripting/entity.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "entity.hpp"
|
||||
#include "script_value.hpp"
|
||||
#include "execution.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
entity::entity()
|
||||
: entity(0)
|
||||
{
|
||||
}
|
||||
|
||||
entity::entity(const entity& other) : entity(other.entity_id_)
|
||||
{
|
||||
}
|
||||
|
||||
entity::entity(entity&& other) noexcept
|
||||
{
|
||||
this->entity_id_ = other.entity_id_;
|
||||
other.entity_id_ = 0;
|
||||
}
|
||||
|
||||
entity::entity(const unsigned int entity_id)
|
||||
: entity_id_(entity_id)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
entity::~entity()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
entity& entity::operator=(const entity& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->entity_id_ = other.entity_id_;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
entity& entity::operator=(entity&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->entity_id_ = other.entity_id_;
|
||||
other.entity_id_ = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
unsigned int entity::get_entity_id() const
|
||||
{
|
||||
return this->entity_id_;
|
||||
}
|
||||
|
||||
game::scr_entref_t entity::get_entity_reference() const
|
||||
{
|
||||
if (!this->entity_id_)
|
||||
{
|
||||
const auto not_null = static_cast<uint16_t>(~0ui16);
|
||||
return game::scr_entref_t{not_null, not_null};
|
||||
}
|
||||
|
||||
game::scr_entref_t entref{};
|
||||
game::Scr_GetEntityIdRef(&entref, game::SCRIPTINSTANCE_SERVER, this->get_entity_id());
|
||||
return entref;
|
||||
}
|
||||
|
||||
bool entity::operator==(const entity& other) const noexcept
|
||||
{
|
||||
return this->get_entity_id() == other.get_entity_id();
|
||||
}
|
||||
|
||||
bool entity::operator!=(const entity& other) const noexcept
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
void entity::add() const
|
||||
{
|
||||
if (this->entity_id_)
|
||||
{
|
||||
game::VariableValue value;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
value.u.uintValue = this->entity_id_;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
}
|
||||
}
|
||||
|
||||
void entity::release() const
|
||||
{
|
||||
if (this->entity_id_)
|
||||
{
|
||||
game::VariableValue value;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
value.u.uintValue = this->entity_id_;
|
||||
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
|
||||
}
|
||||
}
|
||||
|
||||
void entity::set(const std::string& field, const script_value& value) const
|
||||
{
|
||||
set_entity_field(*this, field, value);
|
||||
}
|
||||
|
||||
template <>
|
||||
script_value entity::get<script_value>(const std::string& field) const
|
||||
{
|
||||
return get_entity_field(*this, field);
|
||||
}
|
||||
}
|
47
src/game/scripting/entity.hpp
Normal file
47
src/game/scripting/entity.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class entity final
|
||||
{
|
||||
public:
|
||||
entity();
|
||||
entity(unsigned int entity_id);
|
||||
|
||||
entity(const entity& other);
|
||||
entity(entity&& other) noexcept;
|
||||
|
||||
~entity();
|
||||
|
||||
entity& operator=(const entity& other);
|
||||
entity& operator=(entity&& other) noexcept;
|
||||
|
||||
void set(const std::string& field, const script_value& value) const;
|
||||
|
||||
template <typename T = script_value>
|
||||
T get(const std::string& field) const;
|
||||
|
||||
unsigned int get_entity_id() const;
|
||||
game::scr_entref_t get_entity_reference() const;
|
||||
|
||||
bool operator ==(const entity& other) const noexcept;
|
||||
bool operator !=(const entity& other) const noexcept;
|
||||
|
||||
private:
|
||||
unsigned int entity_id_;
|
||||
|
||||
void add() const;
|
||||
void release() const;
|
||||
};
|
||||
|
||||
template <>
|
||||
script_value entity::get(const std::string& field) const;
|
||||
|
||||
template <typename T>
|
||||
T entity::get(const std::string& field) const
|
||||
{
|
||||
return this->get<script_value>(field).as<T>();
|
||||
}
|
||||
}
|
13
src/game/scripting/event.hpp
Normal file
13
src/game/scripting/event.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "script_value.hpp"
|
||||
#include "entity.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
struct event
|
||||
{
|
||||
std::string name;
|
||||
entity entity{};
|
||||
std::vector<script_value> arguments;
|
||||
};
|
||||
}
|
160
src/game/scripting/execution.cpp
Normal file
160
src/game/scripting/execution.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "execution.hpp"
|
||||
#include "safe_execution.hpp"
|
||||
#include "stack_isolation.hpp"
|
||||
|
||||
#include "component/scripting.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
game::VariableValue* allocate_argument()
|
||||
{
|
||||
game::VariableValue* value_ptr = ++game::scr_VmPub->top;
|
||||
++game::scr_VmPub->inparamcount;
|
||||
return value_ptr;
|
||||
}
|
||||
|
||||
script_value get_return_value()
|
||||
{
|
||||
if (game::scr_VmPub->inparamcount == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
|
||||
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
||||
game::scr_VmPub->inparamcount = 0;
|
||||
|
||||
return script_value(game::scr_VmPub->top[1 - game::scr_VmPub->outparamcount]);
|
||||
}
|
||||
|
||||
int get_field_id(const int classnum, const std::string& field)
|
||||
{
|
||||
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
|
||||
{
|
||||
return scripting::fields_table[classnum][field];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void push_value(const script_value& value)
|
||||
{
|
||||
auto* value_ptr = allocate_argument();
|
||||
*value_ptr = value.get_raw();
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, value_ptr);
|
||||
}
|
||||
|
||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
|
||||
{
|
||||
stack_isolation _;
|
||||
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
const auto event_id = game::SL_GetString(event.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||
game::Scr_NotifyId(game::SCRIPTINSTANCE_SERVER, 0, entity.get_entity_id(), event_id, game::scr_VmPub->inparamcount);
|
||||
}
|
||||
|
||||
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
|
||||
{
|
||||
const auto id = entity.get_entity_id();
|
||||
|
||||
stack_isolation _;
|
||||
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
game::VariableValue value{};
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
value.u.uintValue = id;
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
|
||||
const auto local_id = game::AllocThread(game::SCRIPTINSTANCE_SERVER, id);
|
||||
const auto result = game::VM_Execute(game::SCRIPTINSTANCE_SERVER, local_id, pos, arguments.size());
|
||||
game::RemoveRefToObject(game::SCRIPTINSTANCE_SERVER, result);
|
||||
|
||||
return get_return_value();
|
||||
}
|
||||
|
||||
script_value get_custom_field(const entity& entity, const std::string& field)
|
||||
{
|
||||
const object object = entity.get_entity_id();
|
||||
return object.get(field);
|
||||
}
|
||||
|
||||
void set_custom_field(const entity& entity, const std::string& field, const script_value& value)
|
||||
{
|
||||
const object object = entity.get_entity_id();
|
||||
object.set(field, value);
|
||||
}
|
||||
|
||||
void set_entity_field(const entity& entity, const std::string& field, const script_value& value)
|
||||
{
|
||||
const auto entref = entity.get_entity_reference();
|
||||
const int id = get_field_id(entref.classnum, field);
|
||||
|
||||
if (id != -1)
|
||||
{
|
||||
stack_isolation _;
|
||||
push_value(value);
|
||||
|
||||
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
||||
game::scr_VmPub->inparamcount = 0;
|
||||
|
||||
if (!safe_execution::set_entity_field(entref, id))
|
||||
{
|
||||
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
set_custom_field(entity, field, value);
|
||||
}
|
||||
}
|
||||
|
||||
script_value get_entity_field(const entity& entity, const std::string& field)
|
||||
{
|
||||
const auto entref = entity.get_entity_reference();
|
||||
const auto id = get_field_id(entref.classnum, field);
|
||||
|
||||
if (id != -1)
|
||||
{
|
||||
stack_isolation _;
|
||||
|
||||
game::VariableValue value{};
|
||||
if (!safe_execution::get_entity_field(entref, id, &value))
|
||||
{
|
||||
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
||||
}
|
||||
|
||||
const auto __ = gsl::finally([&value]()
|
||||
{
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return get_custom_field(entity, field);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int make_object()
|
||||
{
|
||||
unsigned int index = 0;
|
||||
const auto id = game::AllocVariable(game::SCRIPTINSTANCE_SERVER);
|
||||
const auto variable = &game::scr_VarGlob->variableList[id + 1];
|
||||
variable->w.type = game::SCRIPT_STRUCT;
|
||||
variable->u.o.refCount = 0;
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
22
src/game/scripting/execution.hpp
Normal file
22
src/game/scripting/execution.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "array.hpp"
|
||||
#include "object.hpp"
|
||||
#include "function.hpp"
|
||||
#include "thread.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
void push_value(const script_value& value);
|
||||
|
||||
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
|
||||
|
||||
void set_entity_field(const entity& entity, const std::string& field, const script_value& value);
|
||||
script_value get_entity_field(const entity& entity, const std::string& field);
|
||||
|
||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
||||
|
||||
unsigned int make_object();
|
||||
}
|
42
src/game/scripting/function.cpp
Normal file
42
src/game/scripting/function.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "function.hpp"
|
||||
#include "execution.hpp"
|
||||
#include "../../component/scripting.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
function::function(const char* pos)
|
||||
: pos_(pos)
|
||||
{
|
||||
}
|
||||
|
||||
script_value function::get_raw() const
|
||||
{
|
||||
game::VariableValue value;
|
||||
value.type = game::SCRIPT_FUNCTION;
|
||||
value.u.codePosValue = this->pos_;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
const char* function::get_pos() const
|
||||
{
|
||||
return this->pos_;
|
||||
}
|
||||
|
||||
std::string function::get_name() const
|
||||
{
|
||||
if (scripting::script_function_table_rev.find(this->pos_) != scripting::script_function_table_rev.end())
|
||||
{
|
||||
const auto& func = scripting::script_function_table_rev[this->pos_];
|
||||
return utils::string::va("%s::%s", func.first.data(), func.second.data());
|
||||
}
|
||||
|
||||
return "unknown function";
|
||||
}
|
||||
|
||||
script_value function::call(const entity& self, const arguments& arguments) const
|
||||
{
|
||||
return exec_ent_thread(self, this->pos_, arguments);
|
||||
}
|
||||
}
|
48
src/game/scripting/function.hpp
Normal file
48
src/game/scripting/function.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "entity.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class function
|
||||
{
|
||||
public:
|
||||
function(const char*);
|
||||
|
||||
script_value get_raw() const;
|
||||
const char* get_pos() const;
|
||||
std::string get_name() const;
|
||||
|
||||
script_value call(const entity& self, const arguments& arguments) const;
|
||||
|
||||
script_value operator()(const entity& self, const arguments& arguments) const
|
||||
{
|
||||
return this->call(self, arguments);
|
||||
}
|
||||
|
||||
script_value operator()(const arguments& arguments) const
|
||||
{
|
||||
return this->call(*game::levelEntityId, arguments);
|
||||
}
|
||||
|
||||
script_value operator()() const
|
||||
{
|
||||
return this->call(*game::levelEntityId, {});
|
||||
}
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->call(*game::levelEntityId, {arguments...});
|
||||
}
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(const entity& self, T... arguments) const
|
||||
{
|
||||
return this->call(self, {arguments...});
|
||||
}
|
||||
|
||||
private:
|
||||
const char* pos_;
|
||||
};
|
||||
}
|
233
src/game/scripting/object.cpp
Normal file
233
src/game/scripting/object.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "object.hpp"
|
||||
#include "execution.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
object_value::object_value(unsigned int parent_id, unsigned int id)
|
||||
: id_(id)
|
||||
, parent_id_(parent_id)
|
||||
{
|
||||
/*if (!this->id_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto value = game::scr_VarGlob->childVariableValue[this->id_];
|
||||
game::VariableValue variable;
|
||||
variable.u = value.u.u;
|
||||
variable.type = (game::scriptType_e)value.type;
|
||||
|
||||
this->value_ = variable;*/
|
||||
}
|
||||
|
||||
void object_value::operator=(const script_value& /*value*/)
|
||||
{
|
||||
/*if (!this->id_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto value = _value.get_raw();
|
||||
|
||||
const auto variable = &game::scr_VarGlob->childVariableValue[this->id_];
|
||||
game::VariableValue variable_{};
|
||||
variable_.type = variable->type;
|
||||
variable_.u = variable->u.u;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable->type, variable->u.u);
|
||||
|
||||
variable->type = gsl::narrow_cast<char>(value.type);
|
||||
variable->u.u = value.u;
|
||||
|
||||
this->value_ = value;*/
|
||||
}
|
||||
|
||||
object::object(const unsigned int id)
|
||||
: id_(id)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
object::object(const object& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
object::object(object&& other) noexcept
|
||||
{
|
||||
this->id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
object::object()
|
||||
{
|
||||
this->id_ = make_object();
|
||||
}
|
||||
|
||||
object::object(std::unordered_map<std::string, script_value> values)
|
||||
{
|
||||
this->id_ = make_object();
|
||||
|
||||
for (const auto& value : values)
|
||||
{
|
||||
this->set(value.first, value.second);
|
||||
}
|
||||
}
|
||||
|
||||
object::~object()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
object& object::operator=(const object& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->id_ = other.id_;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
object& object::operator=(object&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void object::add() const
|
||||
{
|
||||
if (this->id_)
|
||||
{
|
||||
game::VariableValue value{};
|
||||
value.u.uintValue = this->id_;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
}
|
||||
}
|
||||
|
||||
void object::release() const
|
||||
{
|
||||
if (this->id_)
|
||||
{
|
||||
game::VariableValue value{};
|
||||
value.u.uintValue = this->id_;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> object::get_keys() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
/*auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild;
|
||||
|
||||
while (current)
|
||||
{
|
||||
const auto var = game::scr_VarGlob->childVariableValue[current];
|
||||
const auto string_id = (unsigned __int8)var.name_lo + (var.k.keys.name_hi << 8);
|
||||
|
||||
if (string_id < 0x34BC)
|
||||
{
|
||||
const auto string = reinterpret_cast<const char**>(SELECT_VALUE(0x2DACC28, 0x2D7CF28))[string_id];
|
||||
result.push_back(string);
|
||||
}
|
||||
|
||||
current = var.nextSibling;
|
||||
}*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned int object::size() const
|
||||
{
|
||||
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
|
||||
}
|
||||
|
||||
void object::erase(const std::string& key) const
|
||||
{
|
||||
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
if (variable_id)
|
||||
{
|
||||
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
|
||||
}
|
||||
}
|
||||
|
||||
script_value object::get(const std::string& /*key*/) const
|
||||
{
|
||||
/*const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = game::scr_VarGlob->childVariableValue[variable_id];
|
||||
game::VariableValue variable;
|
||||
variable.u = value.u.u;
|
||||
variable.type = (game::scriptType_e)value.type;
|
||||
|
||||
return variable;*/
|
||||
return {};
|
||||
}
|
||||
|
||||
void object::set(const std::string& /*key*/, const script_value& /*value*/) const
|
||||
{
|
||||
/*const auto value = value_.get_raw();
|
||||
const auto variable_id = this->get_value_id(key);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id];
|
||||
game::VariableValue variable_{};
|
||||
variable_.type = variable->type;
|
||||
variable_.u = variable->u.u;
|
||||
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_.type, variable_.u);
|
||||
|
||||
variable->type = gsl::narrow_cast<char>(value.type);
|
||||
variable->u.u = value.u;*/
|
||||
}
|
||||
|
||||
unsigned int object::get_entity_id() const
|
||||
{
|
||||
return this->id_;
|
||||
}
|
||||
|
||||
unsigned int object::get_value_id(const std::string& key) const
|
||||
{
|
||||
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
|
||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
|
||||
if (!variable_id)
|
||||
{
|
||||
return game::GetNewVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||
}
|
||||
|
||||
return variable_id;
|
||||
}
|
||||
|
||||
entity object::get_raw() const
|
||||
{
|
||||
return entity(this->id_);
|
||||
}
|
||||
}
|
55
src/game/scripting/object.hpp
Normal file
55
src/game/scripting/object.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class object_value : public script_value
|
||||
{
|
||||
public:
|
||||
object_value(unsigned int, unsigned int);
|
||||
void operator=(const script_value&);
|
||||
private:
|
||||
unsigned int id_;
|
||||
unsigned int parent_id_;
|
||||
};
|
||||
|
||||
class object final
|
||||
{
|
||||
public:
|
||||
object();
|
||||
object(const unsigned int);
|
||||
|
||||
object(std::unordered_map<std::string, script_value>);
|
||||
|
||||
object(const object& other);
|
||||
object(object&& other) noexcept;
|
||||
|
||||
~object();
|
||||
|
||||
object& operator=(const object& other);
|
||||
object& operator=(object&& other) noexcept;
|
||||
|
||||
std::vector<std::string> get_keys() const;
|
||||
unsigned int size() const;
|
||||
void erase(const std::string&) const;
|
||||
|
||||
script_value get(const std::string&) const;
|
||||
void set(const std::string&, const script_value&) const;
|
||||
|
||||
unsigned int get_entity_id() const;
|
||||
unsigned int get_value_id(const std::string&) const;
|
||||
|
||||
entity get_raw() const;
|
||||
|
||||
object_value operator[](const std::string& key) const
|
||||
{
|
||||
return {this->id_, this->get_value_id(key)};
|
||||
}
|
||||
|
||||
private:
|
||||
void add() const;
|
||||
void release() const;
|
||||
|
||||
unsigned int id_{};
|
||||
};
|
||||
}
|
67
src/game/scripting/safe_execution.cpp
Normal file
67
src/game/scripting/safe_execution.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "safe_execution.hpp"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4611)
|
||||
|
||||
namespace scripting::safe_execution
|
||||
{
|
||||
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref)
|
||||
{
|
||||
__try
|
||||
{
|
||||
function(entref);
|
||||
return true;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool call(const script_function function, const game::scr_entref_t& entref)
|
||||
{
|
||||
*game::g_script_error_level += 1;
|
||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0))
|
||||
{
|
||||
*game::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto result = execute_with_seh(function, entref);
|
||||
*game::g_script_error_level -= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool set_entity_field(const game::scr_entref_t& entref, const int offset)
|
||||
{
|
||||
*game::g_script_error_level += 1;
|
||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0))
|
||||
{
|
||||
*game::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
game::Scr_SetObjectField(entref.classnum, entref.entnum, offset);
|
||||
|
||||
*game::g_script_error_level -= 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_entity_field(const game::scr_entref_t& entref, const int offset, game::VariableValue* value)
|
||||
{
|
||||
*game::g_script_error_level += 1;
|
||||
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0))
|
||||
{
|
||||
value->type = game::SCRIPT_NONE;
|
||||
value->u.intValue = 0;
|
||||
*game::g_script_error_level -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = game::GetEntityFieldValue(game::SCRIPTINSTANCE_SERVER, entref.classnum, entref.entnum, 0, offset);
|
||||
|
||||
*game::g_script_error_level -= 1;
|
||||
return true;
|
||||
}
|
||||
}
|
14
src/game/scripting/safe_execution.hpp
Normal file
14
src/game/scripting/safe_execution.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting::safe_execution
|
||||
{
|
||||
using script_function = void(*)(game::scr_entref_t);
|
||||
|
||||
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref);
|
||||
|
||||
bool call(script_function function, const game::scr_entref_t& entref);
|
||||
|
||||
bool set_entity_field(const game::scr_entref_t& entref, int offset);
|
||||
bool get_entity_field(const game::scr_entref_t& entref, int offset, game::VariableValue* value);
|
||||
}
|
386
src/game/scripting/script_value.cpp
Normal file
386
src/game/scripting/script_value.cpp
Normal file
@ -0,0 +1,386 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "script_value.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "array.hpp"
|
||||
#include "function.hpp"
|
||||
#include "object.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
/***********************************************
|
||||
* Constructors
|
||||
**********************************************/
|
||||
|
||||
script_value::script_value(const game::VariableValue& value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(void* value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_INTEGER;
|
||||
variable.u.intValue = reinterpret_cast<uintptr_t>(value);
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const int value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_INTEGER;
|
||||
variable.u.intValue = value;
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const unsigned int value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_INTEGER;
|
||||
variable.u.uintValue = value;
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const bool value)
|
||||
: script_value(static_cast<unsigned>(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const float value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_FLOAT;
|
||||
variable.u.floatValue = value;
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const double value)
|
||||
: script_value(static_cast<float>(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_STRING;
|
||||
variable.u.stringValue = game::SL_GetString(value, 0, game::SCRIPTINSTANCE_SERVER);
|
||||
|
||||
const auto _ = gsl::finally([&variable]()
|
||||
{
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable.type, variable.u);
|
||||
});
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const std::string& value)
|
||||
: script_value(value.data())
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const entity& value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_OBJECT;
|
||||
variable.u.pointerValue = value.get_entity_id();
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const array& value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_OBJECT;
|
||||
variable.u.pointerValue = value.get_entity_id();
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const object& value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_OBJECT;
|
||||
variable.u.pointerValue = value.get_entity_id();
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const function& value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_FUNCTION;
|
||||
variable.u.codePosValue = value.get_pos();
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
script_value::script_value(const vector& value)
|
||||
{
|
||||
game::VariableValue variable{};
|
||||
variable.type = game::SCRIPT_VECTOR;
|
||||
variable.u.vectorValue = game::Scr_AllocVector(game::SCRIPTINSTANCE_SERVER, value);
|
||||
|
||||
const auto _ = gsl::finally([&variable]()
|
||||
{
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable.type, variable.u);
|
||||
});
|
||||
|
||||
this->value_ = variable;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Integer
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<int>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_INTEGER;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<unsigned int>() const
|
||||
{
|
||||
return this->is<int>();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<bool>() const
|
||||
{
|
||||
return this->is<int>();
|
||||
}
|
||||
|
||||
template <>
|
||||
int script_value::get() const
|
||||
{
|
||||
return this->get_raw().u.intValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned int script_value::get() const
|
||||
{
|
||||
return this->get_raw().u.uintValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::get() const
|
||||
{
|
||||
return this->get_raw().u.uintValue != 0;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Float
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<float>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_FLOAT;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<double>() const
|
||||
{
|
||||
return this->is<float>();
|
||||
}
|
||||
|
||||
template <>
|
||||
float script_value::get() const
|
||||
{
|
||||
return this->get_raw().u.floatValue;
|
||||
}
|
||||
|
||||
template <>
|
||||
double script_value::get() const
|
||||
{
|
||||
return static_cast<double>(this->get_raw().u.floatValue);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* String
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<const char*>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_STRING
|
||||
|| this->get_raw().type == game::SCRIPT_ISTRING;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<std::string>() const
|
||||
{
|
||||
return this->is<const char*>();
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* script_value::get() const
|
||||
{
|
||||
return game::SL_ConvertToString(static_cast<unsigned int>(this->get_raw().u.stringValue),
|
||||
game::SCRIPTINSTANCE_SERVER);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string script_value::get() const
|
||||
{
|
||||
return this->get<const char*>();
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Entity
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<entity>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_OBJECT;
|
||||
}
|
||||
|
||||
template <>
|
||||
entity script_value::get() const
|
||||
{
|
||||
return entity(this->get_raw().u.pointerValue);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Array
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<array>() const
|
||||
{
|
||||
if (this->get_raw().type != game::SCRIPT_OBJECT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto id = this->get_raw().u.uintValue;
|
||||
const auto type = game::scr_VarGlob->variableList[id + 1].w.type & 0x1F;
|
||||
|
||||
return type == game::SCRIPT_ARRAY;
|
||||
}
|
||||
|
||||
template <>
|
||||
array script_value::get() const
|
||||
{
|
||||
return array(this->get_raw().u.uintValue);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Struct
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<object>() const
|
||||
{
|
||||
if (this->get_raw().type != game::SCRIPT_OBJECT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto id = this->get_raw().u.uintValue;
|
||||
const auto type = game::scr_VarGlob->variableList[id + 1].w.type & 0x1F;
|
||||
|
||||
return type == game::SCRIPT_STRUCT || type == game::SCRIPT_ENTITY;
|
||||
}
|
||||
|
||||
template <>
|
||||
object script_value::get() const
|
||||
{
|
||||
return object(this->get_raw().u.uintValue);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Function
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<function>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_FUNCTION;
|
||||
}
|
||||
|
||||
template <>
|
||||
function script_value::get() const
|
||||
{
|
||||
return function(this->get_raw().u.codePosValue);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************
|
||||
* Vector
|
||||
**********************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<vector>() const
|
||||
{
|
||||
return this->get_raw().type == game::SCRIPT_VECTOR;
|
||||
}
|
||||
|
||||
template <>
|
||||
vector script_value::get() const
|
||||
{
|
||||
return this->get_raw().u.vectorValue;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
*
|
||||
**********************************************/
|
||||
|
||||
const game::VariableValue& script_value::get_raw() const
|
||||
{
|
||||
return this->value_.get();
|
||||
}
|
||||
|
||||
std::string script_value::to_string() const
|
||||
{
|
||||
if (this->is<int>())
|
||||
{
|
||||
return utils::string::va("%i", this->as<int>());
|
||||
}
|
||||
|
||||
if (this->is<float>())
|
||||
{
|
||||
return utils::string::va("%f", this->as<float>());
|
||||
}
|
||||
|
||||
if (this->is<std::string>())
|
||||
{
|
||||
return this->as<std::string>();
|
||||
}
|
||||
|
||||
if (this->is<vector>())
|
||||
{
|
||||
const auto vec = this->as<vector>();
|
||||
return utils::string::va("(%g, %g, %g)",
|
||||
vec.get_x(),
|
||||
vec.get_y(),
|
||||
vec.get_z()
|
||||
);
|
||||
}
|
||||
|
||||
if (this->is<function>())
|
||||
{
|
||||
const auto func = this->as<function>();
|
||||
const auto& name = func.get_name();
|
||||
return utils::string::va("[[ %s ]]", name.data());
|
||||
}
|
||||
|
||||
return this->type_name();
|
||||
}
|
||||
|
||||
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
|
||||
: values_(args)
|
||||
, value_(value)
|
||||
, index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
function_arguments::function_arguments(const arguments& values)
|
||||
: values_(values)
|
||||
{
|
||||
}
|
||||
}
|
262
src/game/scripting/script_value.hpp
Normal file
262
src/game/scripting/script_value.hpp
Normal file
@ -0,0 +1,262 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "variable_value.hpp"
|
||||
#include "vector.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class entity;
|
||||
class array;
|
||||
class object;
|
||||
class function;
|
||||
class script_value;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<int, std::string> typenames =
|
||||
{
|
||||
{0, "undefined"},
|
||||
{1, "object"},
|
||||
{2, "string"},
|
||||
{3, "localized string"},
|
||||
{4, "vector"},
|
||||
{5, "float"},
|
||||
{6, "int"},
|
||||
{7, "codepos"},
|
||||
{8, "precodepos"},
|
||||
{9, "function"},
|
||||
{10, "stack"},
|
||||
{11, "animation"},
|
||||
{12, "developer codepos"},
|
||||
{13, "thread"},
|
||||
{14, "thread"},
|
||||
{15, "thread"},
|
||||
{16, "thread"},
|
||||
{17, "struct"},
|
||||
{18, "removed entity"},
|
||||
{19, "entity"},
|
||||
{20, "array"},
|
||||
{21, "removed thread"},
|
||||
};
|
||||
|
||||
std::string get_typename(const game::VariableValue& value)
|
||||
{
|
||||
auto type_ = 0;
|
||||
if (value.type == game::SCRIPT_OBJECT)
|
||||
{
|
||||
type_ = game::scr_VarGlob->variableList[value.u.uintValue].w.type & 0x1F;
|
||||
}
|
||||
else
|
||||
{
|
||||
type_ = value.type;
|
||||
}
|
||||
|
||||
if (typenames.find(type_) != typenames.end())
|
||||
{
|
||||
return typenames[type_];
|
||||
}
|
||||
|
||||
printf("UNKNOWN TYPE %i\n", type_);
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string get_c_typename()
|
||||
{
|
||||
auto& info = typeid(T);
|
||||
|
||||
if (info == typeid(std::string))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (info == typeid(const char*))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (info == typeid(entity))
|
||||
{
|
||||
return "entity";
|
||||
}
|
||||
|
||||
if (info == typeid(array))
|
||||
{
|
||||
return "array";
|
||||
}
|
||||
|
||||
if (info == typeid(object))
|
||||
{
|
||||
return "struct";
|
||||
}
|
||||
|
||||
if (info == typeid(function))
|
||||
{
|
||||
return "function";
|
||||
}
|
||||
|
||||
if (info == typeid(vector))
|
||||
{
|
||||
return "vector";
|
||||
}
|
||||
|
||||
return info.name();
|
||||
}
|
||||
}
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
|
||||
class script_value
|
||||
{
|
||||
public:
|
||||
script_value() = default;
|
||||
script_value(const game::VariableValue& value);
|
||||
|
||||
script_value(void* value);
|
||||
|
||||
script_value(int value);
|
||||
script_value(unsigned int value);
|
||||
script_value(bool value);
|
||||
|
||||
script_value(float value);
|
||||
script_value(double value);
|
||||
|
||||
script_value(const char* value);
|
||||
script_value(const std::string& value);
|
||||
|
||||
script_value(const entity& value);
|
||||
script_value(const array& value);
|
||||
script_value(const object& value);
|
||||
|
||||
script_value(const function& value);
|
||||
|
||||
script_value(const vector& value);
|
||||
|
||||
template <typename T>
|
||||
bool is() const;
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
if (!this->is<T>())
|
||||
{
|
||||
const auto type = get_typename(this->get_raw());
|
||||
const auto c_type = get_c_typename<T>();
|
||||
throw std::runtime_error(std::string("has type '" + type + "' but should be '" + c_type + "'"));
|
||||
}
|
||||
|
||||
return get<T>();
|
||||
}
|
||||
|
||||
std::string type_name() const
|
||||
{
|
||||
return get_typename(this->get_raw());
|
||||
}
|
||||
|
||||
template <template<class, class> class C, class T, typename ArrayType = array>
|
||||
script_value(const C<T, std::allocator<T>>& container)
|
||||
{
|
||||
ArrayType array_{};
|
||||
|
||||
for (const auto& value : container)
|
||||
{
|
||||
array_.push(value);
|
||||
}
|
||||
|
||||
game::VariableValue value{};
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
value.u.pointerValue = array_.get_entity_id();
|
||||
|
||||
this->value_ = value;
|
||||
}
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->as<function>().call({arguments...});
|
||||
}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
const game::VariableValue& get_raw() const;
|
||||
|
||||
variable_value value_{};
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T get() const;
|
||||
|
||||
};
|
||||
|
||||
class variadic_args : public arguments
|
||||
{
|
||||
};
|
||||
|
||||
class function_argument
|
||||
{
|
||||
public:
|
||||
function_argument(const arguments& args, const script_value& value, const int index);
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
try
|
||||
{
|
||||
return this->value_.as<T>();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(utils::string::va("parameter %d %s",
|
||||
this->index_ + 1, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
variadic_args as() const
|
||||
{
|
||||
variadic_args args{};
|
||||
for (auto i = this->index_; i < static_cast<int>(this->values_.size()); i++)
|
||||
{
|
||||
args.push_back(this->values_[i]);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
template <>
|
||||
script_value as() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T() const
|
||||
{
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
arguments values_{};
|
||||
script_value value_{};
|
||||
int index_{};
|
||||
};
|
||||
|
||||
class function_arguments
|
||||
{
|
||||
public:
|
||||
function_arguments(const arguments& values);
|
||||
|
||||
function_argument operator[](const int index) const
|
||||
{
|
||||
if (index >= static_cast<int>(values_.size()))
|
||||
{
|
||||
throw std::runtime_error(utils::string::va("parameter %d does not exist", index));
|
||||
}
|
||||
|
||||
return {values_, values_[index], index};
|
||||
}
|
||||
private:
|
||||
arguments values_{};
|
||||
};
|
||||
}
|
27
src/game/scripting/stack_isolation.cpp
Normal file
27
src/game/scripting/stack_isolation.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "stack_isolation.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
stack_isolation::stack_isolation()
|
||||
{
|
||||
this->in_param_count_ = game::scr_VmPub->inparamcount;
|
||||
this->out_param_count_ = game::scr_VmPub->outparamcount;
|
||||
this->top_ = game::scr_VmPub->top;
|
||||
this->max_stack_ = game::scr_VmPub->maxstack;
|
||||
|
||||
game::scr_VmPub->top = this->stack_;
|
||||
game::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
||||
game::scr_VmPub->inparamcount = 0;
|
||||
game::scr_VmPub->outparamcount = 0;
|
||||
}
|
||||
|
||||
stack_isolation::~stack_isolation()
|
||||
{
|
||||
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
|
||||
game::scr_VmPub->inparamcount = this->in_param_count_;
|
||||
game::scr_VmPub->outparamcount = this->out_param_count_;
|
||||
game::scr_VmPub->top = this->top_;
|
||||
game::scr_VmPub->maxstack = this->max_stack_;
|
||||
}
|
||||
}
|
25
src/game/scripting/stack_isolation.hpp
Normal file
25
src/game/scripting/stack_isolation.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class stack_isolation final
|
||||
{
|
||||
public:
|
||||
stack_isolation();
|
||||
~stack_isolation();
|
||||
|
||||
stack_isolation(stack_isolation&&) = delete;
|
||||
stack_isolation(const stack_isolation&) = delete;
|
||||
stack_isolation& operator=(stack_isolation&&) = delete;
|
||||
stack_isolation& operator=(const stack_isolation&) = delete;
|
||||
|
||||
private:
|
||||
game::VariableValue stack_[512]{};
|
||||
|
||||
game::VariableValue* max_stack_;
|
||||
game::VariableValue* top_;
|
||||
unsigned int in_param_count_;
|
||||
unsigned int out_param_count_;
|
||||
};
|
||||
}
|
67
src/game/scripting/thread.cpp
Normal file
67
src/game/scripting/thread.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "thread.hpp"
|
||||
#include "execution.hpp"
|
||||
#include "../../component/scripting.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
thread::thread(unsigned int id)
|
||||
: id_(id)
|
||||
, type_(game::scr_VarGlob->variableList[id].w.type & 0x7F)
|
||||
{
|
||||
}
|
||||
|
||||
script_value thread::get_raw() const
|
||||
{
|
||||
game::VariableValue value;
|
||||
value.type = game::SCRIPT_OBJECT;
|
||||
value.u.uintValue = this->id_;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned int thread::get_entity_id() const
|
||||
{
|
||||
return this->id_;
|
||||
}
|
||||
|
||||
unsigned int thread::get_type() const
|
||||
{
|
||||
return this->type_;
|
||||
}
|
||||
|
||||
unsigned int thread::get_wait_time() const
|
||||
{
|
||||
return game::scr_VarGlob->variableList[this->id_].w.waitTime >> 8;
|
||||
}
|
||||
|
||||
unsigned int thread::get_notify_name_id() const
|
||||
{
|
||||
return game::scr_VarGlob->variableList[this->id_].w.notifyName >> 8;
|
||||
}
|
||||
|
||||
unsigned int thread::get_self() const
|
||||
{
|
||||
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
|
||||
}
|
||||
|
||||
std::string thread::get_notify_name() const
|
||||
{
|
||||
return game::SL_ConvertToString(this->get_notify_name_id(), game::SCRIPTINSTANCE_SERVER);
|
||||
}
|
||||
|
||||
const char* thread::get_pos() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* thread::get_start_pos() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void thread::kill() const
|
||||
{
|
||||
|
||||
}
|
||||
}
|
32
src/game/scripting/thread.hpp
Normal file
32
src/game/scripting/thread.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "entity.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class thread
|
||||
{
|
||||
public:
|
||||
thread(unsigned int);
|
||||
|
||||
script_value get_raw() const;
|
||||
|
||||
unsigned int get_entity_id() const;
|
||||
unsigned int get_type() const;
|
||||
|
||||
unsigned int get_self() const;
|
||||
unsigned int get_wait_time() const;
|
||||
unsigned int get_notify_name_id() const;
|
||||
std::string get_notify_name() const;
|
||||
|
||||
const char* get_pos() const;
|
||||
const char* get_start_pos() const;
|
||||
|
||||
void kill() const;
|
||||
|
||||
private:
|
||||
unsigned int id_{};
|
||||
unsigned int type_{};
|
||||
|
||||
};
|
||||
}
|
68
src/game/scripting/variable_value.cpp
Normal file
68
src/game/scripting/variable_value.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "variable_value.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
variable_value::variable_value(const game::VariableValue& value)
|
||||
{
|
||||
this->assign(value);
|
||||
}
|
||||
|
||||
variable_value::variable_value(const variable_value& other) noexcept
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
variable_value::variable_value(variable_value&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
variable_value& variable_value::operator=(const variable_value& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->assign(other.value_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable_value& variable_value::operator=(variable_value&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->value_ = other.value_;
|
||||
other.value_.type = game::SCRIPT_NONE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
variable_value::~variable_value()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
const game::VariableValue& variable_value::get() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
void variable_value::assign(const game::VariableValue& value)
|
||||
{
|
||||
this->value_ = value;
|
||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &this->value_);
|
||||
}
|
||||
|
||||
void variable_value::release()
|
||||
{
|
||||
if (this->value_.type != game::SCRIPT_NONE)
|
||||
{
|
||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, this->value_.type, this->value_.u);
|
||||
this->value_.type = game::SCRIPT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
27
src/game/scripting/variable_value.hpp
Normal file
27
src/game/scripting/variable_value.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class variable_value
|
||||
{
|
||||
public:
|
||||
variable_value() = default;
|
||||
variable_value(const game::VariableValue& value);
|
||||
variable_value(const variable_value& other) noexcept;
|
||||
variable_value(variable_value&& other) noexcept;
|
||||
|
||||
variable_value& operator=(const variable_value& other) noexcept;
|
||||
variable_value& operator=(variable_value&& other) noexcept;
|
||||
|
||||
~variable_value();
|
||||
|
||||
const game::VariableValue& get() const;
|
||||
|
||||
private:
|
||||
void assign(const game::VariableValue& value);
|
||||
void release();
|
||||
|
||||
game::VariableValue value_{{0}, game::SCRIPT_NONE};
|
||||
};
|
||||
}
|
85
src/game/scripting/vector.cpp
Normal file
85
src/game/scripting/vector.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "vector.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
vector::vector(const float* value)
|
||||
{
|
||||
for (auto i = 0; i < 3; ++i)
|
||||
{
|
||||
this->value_[i] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
vector::vector(const game::vec3_t& value)
|
||||
: vector(&value[0])
|
||||
{
|
||||
}
|
||||
|
||||
vector::vector(const float x, const float y, const float z)
|
||||
{
|
||||
this->value_[0] = x;
|
||||
this->value_[1] = y;
|
||||
this->value_[2] = z;
|
||||
}
|
||||
|
||||
vector::operator game::vec3_t& ()
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
vector::operator const game::vec3_t& () const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
game::vec_t& vector::operator[](const size_t i)
|
||||
{
|
||||
if (i >= 3)
|
||||
{
|
||||
throw std::runtime_error("Out of bounds.");
|
||||
}
|
||||
|
||||
return this->value_[i];
|
||||
}
|
||||
|
||||
const game::vec_t& vector::operator[](const size_t i) const
|
||||
{
|
||||
if (i >= 3)
|
||||
{
|
||||
throw std::runtime_error("Out of bounds.");
|
||||
}
|
||||
|
||||
return this->value_[i];
|
||||
}
|
||||
|
||||
float vector::get_x() const
|
||||
{
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
float vector::get_y() const
|
||||
{
|
||||
return this->operator[](1);
|
||||
}
|
||||
|
||||
float vector::get_z() const
|
||||
{
|
||||
return this->operator[](2);
|
||||
}
|
||||
|
||||
void vector::set_x(const float value)
|
||||
{
|
||||
this->operator[](0) = value;
|
||||
}
|
||||
|
||||
void vector::set_y(const float value)
|
||||
{
|
||||
this->operator[](1) = value;
|
||||
}
|
||||
|
||||
void vector::set_z(const float value)
|
||||
{
|
||||
this->operator[](2) = value;
|
||||
}
|
||||
}
|
31
src/game/scripting/vector.hpp
Normal file
31
src/game/scripting/vector.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace scripting
|
||||
{
|
||||
class vector final
|
||||
{
|
||||
public:
|
||||
vector() = default;
|
||||
vector(const float* value);
|
||||
vector(const game::vec3_t& value);
|
||||
vector(float x, float y, float z);
|
||||
|
||||
operator game::vec3_t& ();
|
||||
operator const game::vec3_t& () const;
|
||||
|
||||
game::vec_t& operator[](size_t i);
|
||||
const game::vec_t& operator[](size_t i) const;
|
||||
|
||||
float get_x() const;
|
||||
float get_y() const;
|
||||
float get_z() const;
|
||||
|
||||
void set_x(float value);
|
||||
void set_y(float value);
|
||||
void set_z(float value);
|
||||
|
||||
private:
|
||||
game::vec3_t value_{ 0 };
|
||||
};
|
||||
}
|
742
src/game/structs.hpp
Normal file
742
src/game/structs.hpp
Normal file
@ -0,0 +1,742 @@
|
||||
#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];
|
||||
|
||||
enum scriptInstance_t
|
||||
{
|
||||
SCRIPTINSTANCE_SERVER,
|
||||
SCRIPTINSTANCE_CLIENT,
|
||||
};
|
||||
|
||||
struct scr_entref_t
|
||||
{
|
||||
unsigned short entnum;
|
||||
unsigned short classnum;
|
||||
int client;
|
||||
};
|
||||
|
||||
struct BuiltinMethodDef
|
||||
{
|
||||
const char* actionString;
|
||||
unsigned int constId;
|
||||
int min_args;
|
||||
int max_args;
|
||||
void(__cdecl* actionFunc)(scr_entref_t);
|
||||
int type;
|
||||
};
|
||||
|
||||
struct BuiltinFunctionDef
|
||||
{
|
||||
const char* actionString;
|
||||
unsigned int constId;
|
||||
int min_args;
|
||||
int max_args;
|
||||
void(__cdecl* actionFunc)();
|
||||
int type;
|
||||
};
|
||||
|
||||
enum scriptType_e
|
||||
{
|
||||
SCRIPT_NONE = 0,
|
||||
SCRIPT_OBJECT = 1,
|
||||
SCRIPT_STRING = 2,
|
||||
SCRIPT_ISTRING = 3,
|
||||
SCRIPT_VECTOR = 4,
|
||||
SCRIPT_FLOAT = 5,
|
||||
SCRIPT_INTEGER = 6,
|
||||
SCRIPT_CODEPOS = 7,
|
||||
SCRIPT_END = 8,
|
||||
SCRIPT_FUNCTION = 9,
|
||||
SCRIPT_THREAD = 13,
|
||||
SCRIPT_NOTIFY_THREAD = 14,
|
||||
SCRIPT_TIME_THREAD = 15,
|
||||
SCRIPT_STRUCT = 17,
|
||||
SCRIPT_ENTITY = 19,
|
||||
SCRIPT_ARRAY = 20,
|
||||
SCRIPT_FREE = 0x17,
|
||||
};
|
||||
|
||||
struct VariableStackBuffer
|
||||
{
|
||||
const char* pos;
|
||||
unsigned __int16 size;
|
||||
unsigned __int16 bufLen;
|
||||
unsigned __int16 localId;
|
||||
char time;
|
||||
char buf[1];
|
||||
};
|
||||
|
||||
union VariableUnion
|
||||
{
|
||||
int intValue;
|
||||
unsigned int uintValue;
|
||||
float floatValue;
|
||||
unsigned int stringValue;
|
||||
const float* vectorValue;
|
||||
const char* codePosValue;
|
||||
unsigned int pointerValue;
|
||||
VariableStackBuffer* stackValue;
|
||||
unsigned int entityOffset;
|
||||
};
|
||||
|
||||
struct VariableValue
|
||||
{
|
||||
VariableUnion u;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct function_stack_t
|
||||
{
|
||||
char* pos;
|
||||
VariableValue* top;
|
||||
unsigned int localId;
|
||||
unsigned int localVarCount;
|
||||
VariableValue* startTop;
|
||||
};
|
||||
|
||||
struct function_frame_t
|
||||
{
|
||||
function_stack_t fs;
|
||||
};
|
||||
|
||||
struct scrVmPub_t
|
||||
{
|
||||
unsigned int* localVars;
|
||||
VariableValue* maxstack;
|
||||
int function_count;
|
||||
function_frame_t* function_frame;
|
||||
VariableValue* top;
|
||||
bool abort_on_error;
|
||||
bool terminal_error;
|
||||
bool block_execution;
|
||||
unsigned int inparamcount;
|
||||
unsigned int outparamcount;
|
||||
function_frame_t function_frame_start[32];
|
||||
VariableValue stack[2048];
|
||||
void(__cdecl* notifyListeners[1])(unsigned int, unsigned int);
|
||||
};
|
||||
|
||||
static_assert(offsetof(scrVmPub_t, top) == 16);
|
||||
static_assert(offsetof(scrVmPub_t, inparamcount) == 24);
|
||||
|
||||
struct scrVarPub_t
|
||||
{
|
||||
const char* fieldBuffer;
|
||||
unsigned __int16 canonicalStrCount;
|
||||
bool developer;
|
||||
bool developer_script;
|
||||
bool evaluate;
|
||||
const char* error_message;
|
||||
int error_index;
|
||||
unsigned int time;
|
||||
unsigned int timeArrayId;
|
||||
unsigned int pauseArrayId;
|
||||
unsigned int levelId;
|
||||
unsigned int gameId;
|
||||
unsigned int animId;
|
||||
unsigned int freeEntList;
|
||||
unsigned int tempVariable;
|
||||
bool bInited;
|
||||
unsigned __int16 savecount;
|
||||
unsigned int checksum;
|
||||
unsigned int entId;
|
||||
unsigned int entFieldName;
|
||||
void* programHunkUser;
|
||||
const char* programBuffer;
|
||||
const char* endScriptBuffer;
|
||||
unsigned __int16* saveIdMap;
|
||||
unsigned __int16* saveIdMapRev;
|
||||
unsigned int numScriptThreads;
|
||||
unsigned int numScriptValues;
|
||||
unsigned int numScriptObjects;
|
||||
const char* varUsagePos;
|
||||
int ext_threadcount;
|
||||
int totalObjectRefCount;
|
||||
volatile int totalVectorRefCount;
|
||||
};
|
||||
|
||||
union Variable_u
|
||||
{
|
||||
unsigned int prev;
|
||||
unsigned int prevSibling;
|
||||
};
|
||||
|
||||
struct Variable
|
||||
{
|
||||
unsigned int id;
|
||||
Variable_u u;
|
||||
};
|
||||
|
||||
union ObjectInfo_u
|
||||
{
|
||||
unsigned __int16 entnum;
|
||||
unsigned __int16 size;
|
||||
unsigned int nextEntId;
|
||||
unsigned int self;
|
||||
};
|
||||
|
||||
struct ObjectInfo
|
||||
{
|
||||
unsigned __int16 refCount;
|
||||
ObjectInfo_u u;
|
||||
};
|
||||
|
||||
union VariableValueInternal_u
|
||||
{
|
||||
unsigned int next;
|
||||
VariableUnion u;
|
||||
ObjectInfo o;
|
||||
};
|
||||
|
||||
union VariableValueInternal_w
|
||||
{
|
||||
unsigned int status;
|
||||
unsigned int type;
|
||||
unsigned int name;
|
||||
unsigned int classnum;
|
||||
unsigned int notifyName;
|
||||
unsigned int waitTime;
|
||||
unsigned int parentLocalId;
|
||||
};
|
||||
|
||||
union VariableValueInternal_v
|
||||
{
|
||||
unsigned int next;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
struct VariableValueInternal
|
||||
{
|
||||
Variable hash;
|
||||
VariableValueInternal_u u;
|
||||
VariableValueInternal_w w;
|
||||
VariableValueInternal_v v;
|
||||
unsigned int nextSibling;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VariableValueInternal) == 28);
|
||||
static_assert(offsetof(VariableValueInternal, hash) == 0);
|
||||
static_assert(offsetof(VariableValueInternal, u) == 8);
|
||||
static_assert(offsetof(VariableValueInternal, w) == 16);
|
||||
|
||||
struct scrVarGlob_t
|
||||
{
|
||||
VariableValueInternal* variableList;
|
||||
};
|
||||
|
||||
struct scr_classStruct_t
|
||||
{
|
||||
unsigned __int16 id;
|
||||
unsigned __int16 entArrayId;
|
||||
char charId;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
enum gclientFlag
|
||||
{
|
||||
NOCLIP = 1 << 0,
|
||||
UFO = 1 << 1,
|
||||
};
|
||||
|
||||
enum entityFlag
|
||||
{
|
||||
FL_GODMODE = 1 << 0,
|
||||
FL_DEMI_GODMODE = 1 << 1,
|
||||
FL_NOTARGET = 1 << 2,
|
||||
FL_SUPPORTS_LINKTO = 1 << 12,
|
||||
}; // TODO: Finish
|
||||
|
||||
struct gclient_s
|
||||
{
|
||||
char __pad0[0x18];
|
||||
int eflags;
|
||||
char __pad1[0x5668];
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct gentity_s
|
||||
{
|
||||
int number;
|
||||
char __pad0[0x150];
|
||||
gclient_s* client; // 340
|
||||
char __pad1[0x30];
|
||||
int flags; // 392
|
||||
char __pad2[0x190];
|
||||
};
|
||||
|
||||
static_assert(sizeof(gentity_s) == 0x31C);
|
||||
|
||||
enum clientState_t
|
||||
{
|
||||
CS_FREE,
|
||||
CS_ZOMBIE,
|
||||
CS_RECONNECTING,
|
||||
CS_CONNECTED,
|
||||
CS_CLIENTLOADING,
|
||||
CS_ACTIVE,
|
||||
};
|
||||
|
||||
enum netsrc_t
|
||||
{
|
||||
NS_CLIENT1 = 0x0,
|
||||
NS_CLIENT2 = 0x1,
|
||||
NS_CLIENT3 = 0x2,
|
||||
NS_CLIENT4 = 0x3,
|
||||
NS_SERVER = 0x4,
|
||||
NS_PACKET = 0x5,
|
||||
NS_NULL = -1,
|
||||
};
|
||||
|
||||
enum netadrtype_t
|
||||
{
|
||||
NA_BOT = 0x0,
|
||||
NA_BAD = 0x1,
|
||||
NA_LOOPBACK = 0x2,
|
||||
NA_BROADCAST = 0x3,
|
||||
NA_IP = 0x4,
|
||||
};
|
||||
|
||||
struct netadr_t
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned char ip[4];
|
||||
unsigned int inaddr;
|
||||
};
|
||||
unsigned __int16 port;
|
||||
netadrtype_t type;
|
||||
netsrc_t localNetID;
|
||||
unsigned __int16 serverID;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netadr_t) == 0x14);
|
||||
|
||||
struct netProfileInfo_t
|
||||
{
|
||||
unsigned char __pad0[0x5E0];
|
||||
};
|
||||
|
||||
struct netchan_t
|
||||
{
|
||||
int outgoingSequence;
|
||||
netsrc_t sock;
|
||||
int dropped;
|
||||
int incomingSequence;
|
||||
netadr_t remoteAddress;
|
||||
int qport;
|
||||
int fragmentSequence;
|
||||
int fragmentLength;
|
||||
unsigned char* fragmentBuffer;
|
||||
int fragmentBufferSize;
|
||||
int unsentFragments;
|
||||
int unsentOnLoan;
|
||||
int unsentFragmentStart;
|
||||
int unsentLength;
|
||||
unsigned char* unsentBuffer;
|
||||
int unsentBufferSize;
|
||||
int reliable_fragments;
|
||||
unsigned char fragment_send_count[128];
|
||||
unsigned int fragment_ack[4];
|
||||
int lowest_send_count;
|
||||
netProfileInfo_t prof;
|
||||
};
|
||||
|
||||
static_assert(sizeof(netchan_t) == 0x6C8);
|
||||
|
||||
struct PredictedVehicleDef
|
||||
{
|
||||
bool fullPhysics;
|
||||
vec3_t origin;
|
||||
vec3_t angles;
|
||||
vec3_t tVel;
|
||||
vec3_t aVel;
|
||||
int serverTime;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PredictedVehicleDef) == 0x38);
|
||||
|
||||
struct clientHeader_t
|
||||
{
|
||||
clientState_t state;
|
||||
int sendAsActive;
|
||||
int deltaMessage;
|
||||
int rateDelayed;
|
||||
int hasAckedBaselineData;
|
||||
int hugeSnapshotSent;
|
||||
netchan_t netchan;
|
||||
vec3_t predictedOrigin;
|
||||
int predictedOriginServerTime;
|
||||
int migrationState;
|
||||
PredictedVehicleDef predictedVehicle;
|
||||
};
|
||||
|
||||
static_assert(sizeof(clientHeader_t) == 0x72C);
|
||||
|
||||
struct client_s
|
||||
{
|
||||
clientHeader_t header;
|
||||
const char* dropReason;
|
||||
char userinfo[1024];
|
||||
unsigned char __pad0[0x3F75C];
|
||||
int bIsTestClient;
|
||||
unsigned char __pad1[0xDEF0];
|
||||
};
|
||||
|
||||
static_assert(sizeof(client_s) == 0x4E180);
|
||||
|
||||
struct cmd_function_t
|
||||
{
|
||||
cmd_function_t* next;
|
||||
const char* name;
|
||||
const char* autoCompleteDir;
|
||||
const char* autoCompleteExt;
|
||||
void(__cdecl* function)();
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct CmdArgs
|
||||
{
|
||||
int nesting;
|
||||
int localClientNum[8];
|
||||
int controllerIndex[8];
|
||||
void* itemDef[8];
|
||||
int argshift[8];
|
||||
int argc[8];
|
||||
const char** argv[8];
|
||||
char textPool[8192];
|
||||
const char* argvPool[512];
|
||||
int usedTextPool[8];
|
||||
int totalUsedArgvPool;
|
||||
int totalUsedTextPool;
|
||||
};
|
||||
|
||||
enum dvarType_t
|
||||
{
|
||||
DVAR_TYPE_INVALID = 0x0,
|
||||
DVAR_TYPE_BOOL = 0x1,
|
||||
DVAR_TYPE_FLOAT = 0x2,
|
||||
DVAR_TYPE_FLOAT_2 = 0x3,
|
||||
DVAR_TYPE_FLOAT_3 = 0x4,
|
||||
DVAR_TYPE_FLOAT_4 = 0x5,
|
||||
DVAR_TYPE_INT = 0x6,
|
||||
DVAR_TYPE_ENUM = 0x7,
|
||||
DVAR_TYPE_STRING = 0x8,
|
||||
DVAR_TYPE_COLOR = 0x9,
|
||||
DVAR_TYPE_INT64 = 0xA,
|
||||
DVAR_TYPE_LINEAR_COLOR_RGB = 0xB,
|
||||
DVAR_TYPE_COLOR_XYZ = 0xC,
|
||||
DVAR_TYPE_COUNT = 0xD,
|
||||
};
|
||||
|
||||
union DvarValue
|
||||
{
|
||||
bool enabled;
|
||||
int integer;
|
||||
unsigned int unsignedInt;
|
||||
__int64 integer64;
|
||||
unsigned __int64 unsignedInt64;
|
||||
float value;
|
||||
vec4_t vector;
|
||||
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 DvarLimits
|
||||
{
|
||||
$A37BA207B3DDD6345C554D4661813EDD enumeration;
|
||||
$9CA192F9DB66A3CB7E01DE78A0DEA53D integer;
|
||||
$251C2428A496074035CACA7AAF3D55BD value;
|
||||
$251C2428A496074035CACA7AAF3D55BD vector;
|
||||
};
|
||||
|
||||
struct dvar_t
|
||||
{
|
||||
const char* name;
|
||||
const char* description;
|
||||
int hash;
|
||||
unsigned int flags;
|
||||
dvarType_t type;
|
||||
bool modified;
|
||||
DvarValue current;
|
||||
DvarValue latched;
|
||||
DvarValue reset;
|
||||
DvarLimits domain;
|
||||
dvar_t* hashNext;
|
||||
};
|
||||
|
||||
struct GSC_OBJ
|
||||
{
|
||||
char magic[8];
|
||||
unsigned int source_crc;
|
||||
unsigned int include_offset;
|
||||
unsigned int animtree_offset;
|
||||
unsigned int cseg_offset;
|
||||
unsigned int stringtablefixup_offset;
|
||||
unsigned int exports_offset;
|
||||
unsigned int imports_offset;
|
||||
unsigned int fixup_offset;
|
||||
unsigned int profile_offset;
|
||||
unsigned int cseg_size;
|
||||
unsigned __int16 name;
|
||||
unsigned __int16 stringtablefixup_count;
|
||||
unsigned __int16 exports_count;
|
||||
unsigned __int16 imports_count;
|
||||
unsigned __int16 fixup_count;
|
||||
unsigned __int16 profile_count;
|
||||
char include_count;
|
||||
char animtree_count;
|
||||
char flags;
|
||||
};
|
||||
|
||||
struct GSC_EXPORT_ITEM
|
||||
{
|
||||
unsigned int checksum;
|
||||
unsigned int address;
|
||||
unsigned __int16 name;
|
||||
char param_count;
|
||||
char flags;
|
||||
};
|
||||
|
||||
// gsc-tool
|
||||
enum class opcode : std::uint8_t
|
||||
{
|
||||
OP_End = 0x0,
|
||||
OP_Return = 0x1,
|
||||
OP_GetUndefined = 0x2,
|
||||
OP_GetZero = 0x3,
|
||||
OP_GetByte = 0x4,
|
||||
OP_GetNegByte = 0x5,
|
||||
OP_GetUnsignedShort = 0x6,
|
||||
OP_GetNegUnsignedShort = 0x7,
|
||||
OP_GetInteger = 0x8,
|
||||
OP_GetFloat = 0x9,
|
||||
OP_GetString = 0xA,
|
||||
OP_GetIString = 0xB,
|
||||
OP_GetVector = 0xC,
|
||||
OP_GetLevelObject = 0xD,
|
||||
OP_GetAnimObject = 0xE,
|
||||
OP_GetSelf = 0xF,
|
||||
OP_GetLevel = 0x10,
|
||||
OP_GetGame = 0x11,
|
||||
OP_GetAnim = 0x12,
|
||||
OP_GetAnimation = 0x13,
|
||||
OP_GetGameRef = 0x14,
|
||||
OP_GetFunction = 0x15,
|
||||
OP_CreateLocalVariable = 0x16,
|
||||
OP_SafeCreateLocalVariables = 0x17,
|
||||
OP_RemoveLocalVariables = 0x18,
|
||||
OP_EvalLocalVariableCached = 0x19,
|
||||
OP_EvalArray = 0x1A,
|
||||
OP_EvalLocalArrayRefCached = 0x1B,
|
||||
OP_EvalArrayRef = 0x1C,
|
||||
OP_ClearArray = 0x1D,
|
||||
OP_EmptyArray = 0x1E,
|
||||
OP_GetSelfObject = 0x1F,
|
||||
OP_EvalFieldVariable = 0x20,
|
||||
OP_EvalFieldVariableRef = 0x21,
|
||||
OP_ClearFieldVariable = 0x22,
|
||||
OP_SafeSetVariableFieldCached = 0x23,
|
||||
OP_SafeSetWaittillVariableFieldCached = 0x24,
|
||||
OP_ClearParams = 0x25,
|
||||
OP_CheckClearParams = 0x26,
|
||||
OP_EvalLocalVariableRefCached = 0x27,
|
||||
OP_SetVariableField = 0x28,
|
||||
OP_CallBuiltin = 0x29,
|
||||
OP_CallBuiltinMethod = 0x2A,
|
||||
OP_Wait = 0x2B,
|
||||
OP_WaitTillFrameEnd = 0x2C,
|
||||
OP_PreScriptCall = 0x2D,
|
||||
OP_ScriptFunctionCall = 0x2E,
|
||||
OP_ScriptFunctionCallPointer = 0x2F,
|
||||
OP_ScriptMethodCall = 0x30,
|
||||
OP_ScriptMethodCallPointer = 0x31,
|
||||
OP_ScriptThreadCall = 0x32,
|
||||
OP_ScriptThreadCallPointer = 0x33,
|
||||
OP_ScriptMethodThreadCall = 0x34,
|
||||
OP_ScriptMethodThreadCallPointer = 0x35,
|
||||
OP_DecTop = 0x36,
|
||||
OP_CastFieldObject = 0x37,
|
||||
OP_CastBool = 0x38,
|
||||
OP_BoolNot = 0x39,
|
||||
OP_BoolComplement = 0x3A,
|
||||
OP_JumpOnFalse = 0x3B,
|
||||
OP_JumpOnTrue = 0x3C,
|
||||
OP_JumpOnFalseExpr = 0x3D,
|
||||
OP_JumpOnTrueExpr = 0x3E,
|
||||
OP_Jump = 0x3F,
|
||||
OP_JumpBack = 0x40,
|
||||
OP_Inc = 0x41,
|
||||
OP_Dec = 0x42,
|
||||
OP_Bit_Or = 0x43,
|
||||
OP_Bit_Xor = 0x44,
|
||||
OP_Bit_And = 0x45,
|
||||
OP_Equal = 0x46,
|
||||
OP_NotEqual = 0x47,
|
||||
OP_LessThan = 0x48,
|
||||
OP_GreaterThan = 0x49,
|
||||
OP_LessThanOrEqualTo = 0x4A,
|
||||
OP_GreaterThanOrEqualTo = 0x4B,
|
||||
OP_ShiftLeft = 0x4C,
|
||||
OP_ShiftRight = 0x4D,
|
||||
OP_Plus = 0x4E,
|
||||
OP_Minus = 0x4F,
|
||||
OP_Multiply = 0x50,
|
||||
OP_Divide = 0x51,
|
||||
OP_Modulus = 0x52,
|
||||
OP_SizeOf = 0x53,
|
||||
OP_WaitTillMatch = 0x54,
|
||||
OP_WaitTill = 0x55,
|
||||
OP_Notify = 0x56,
|
||||
OP_EndOn = 0x57,
|
||||
OP_VoidCodePos = 0x58,
|
||||
OP_Switch = 0x59,
|
||||
OP_EndSwitch = 0x5A,
|
||||
OP_Vector = 0x5B,
|
||||
OP_GetHash = 0x5C,
|
||||
OP_RealWait = 0x5D,
|
||||
OP_VectorConstant = 0x5E,
|
||||
OP_IsDefined = 0x5F,
|
||||
OP_VectorScale = 0x60,
|
||||
OP_AnglesToUp = 0x61,
|
||||
OP_AnglesToRight = 0x62,
|
||||
OP_AnglesToForward = 0x63,
|
||||
OP_AngleClamp180 = 0x64,
|
||||
OP_VectorToAngles = 0x65,
|
||||
OP_Abs = 0x66,
|
||||
OP_GetTime = 0x67,
|
||||
OP_GetDvar = 0x68,
|
||||
OP_GetDvarInt = 0x69,
|
||||
OP_GetDvarFloat = 0x6A,
|
||||
OP_GetDvarVector = 0x6B,
|
||||
OP_GetDvarColorRed = 0x6C,
|
||||
OP_GetDvarColorGreen = 0x6D,
|
||||
OP_GetDvarColorBlue = 0x6E,
|
||||
OP_GetDvarColorAlpha = 0x6F,
|
||||
OP_FirstArrayKey = 0x70,
|
||||
OP_NextArrayKey = 0x71,
|
||||
OP_ProfileStart = 0x72,
|
||||
OP_ProfileStop = 0x73,
|
||||
OP_SafeDecTop = 0x74,
|
||||
OP_Nop = 0x75,
|
||||
OP_Abort = 0x76,
|
||||
OP_Object = 0x77,
|
||||
OP_ThreadObject = 0x78,
|
||||
OP_EvalLocalVariable = 0x79,
|
||||
OP_EvalLocalVariableRef = 0x7A,
|
||||
OP_DevblockBegin = 0x7B,
|
||||
OP_DevblockEnd = 0x7C,
|
||||
OP_Breakpoint = 0x7D,
|
||||
OP_Count = 0x7E,
|
||||
};
|
||||
|
||||
enum XAssetType
|
||||
{
|
||||
ASSET_TYPE_XMODELPIECES = 0x0,
|
||||
ASSET_TYPE_PHYSPRESET = 0x1,
|
||||
ASSET_TYPE_PHYSCONSTRAINTS = 0x2,
|
||||
ASSET_TYPE_DESTRUCTIBLEDEF = 0x3,
|
||||
ASSET_TYPE_XANIMPARTS = 0x4,
|
||||
ASSET_TYPE_XMODEL = 0x5,
|
||||
ASSET_TYPE_MATERIAL = 0x6,
|
||||
ASSET_TYPE_TECHNIQUE_SET = 0x7,
|
||||
ASSET_TYPE_IMAGE = 0x8,
|
||||
ASSET_TYPE_SOUND = 0x9,
|
||||
ASSET_TYPE_SOUND_PATCH = 0xA,
|
||||
ASSET_TYPE_CLIPMAP = 0xB,
|
||||
ASSET_TYPE_CLIPMAP_PVS = 0xC,
|
||||
ASSET_TYPE_COMWORLD = 0xD,
|
||||
ASSET_TYPE_GAMEWORLD_SP = 0xE,
|
||||
ASSET_TYPE_GAMEWORLD_MP = 0xF,
|
||||
ASSET_TYPE_MAP_ENTS = 0x10,
|
||||
ASSET_TYPE_GFXWORLD = 0x11,
|
||||
ASSET_TYPE_LIGHT_DEF = 0x12,
|
||||
ASSET_TYPE_UI_MAP = 0x13,
|
||||
ASSET_TYPE_FONT = 0x14,
|
||||
ASSET_TYPE_FONTICON = 0x15,
|
||||
ASSET_TYPE_MENULIST = 0x16,
|
||||
ASSET_TYPE_MENU = 0x17,
|
||||
ASSET_TYPE_LOCALIZE_ENTRY = 0x18,
|
||||
ASSET_TYPE_WEAPON = 0x19,
|
||||
ASSET_TYPE_WEAPONDEF = 0x1A,
|
||||
ASSET_TYPE_WEAPON_VARIANT = 0x1B,
|
||||
ASSET_TYPE_WEAPON_FULL = 0x1C,
|
||||
ASSET_TYPE_ATTACHMENT = 0x1D,
|
||||
ASSET_TYPE_ATTACHMENT_UNIQUE = 0x1E,
|
||||
ASSET_TYPE_WEAPON_CAMO = 0x1F,
|
||||
ASSET_TYPE_SNDDRIVER_GLOBALS = 0x20,
|
||||
ASSET_TYPE_FX = 0x21,
|
||||
ASSET_TYPE_IMPACT_FX = 0x22,
|
||||
ASSET_TYPE_AITYPE = 0x23,
|
||||
ASSET_TYPE_MPTYPE = 0x24,
|
||||
ASSET_TYPE_MPBODY = 0x25,
|
||||
ASSET_TYPE_MPHEAD = 0x26,
|
||||
ASSET_TYPE_CHARACTER = 0x27,
|
||||
ASSET_TYPE_XMODELALIAS = 0x28,
|
||||
ASSET_TYPE_RAWFILE = 0x29,
|
||||
ASSET_TYPE_STRINGTABLE = 0x2A,
|
||||
ASSET_TYPE_LEADERBOARD = 0x2B,
|
||||
ASSET_TYPE_XGLOBALS = 0x2C,
|
||||
ASSET_TYPE_DDL = 0x2D,
|
||||
ASSET_TYPE_GLASSES = 0x2E,
|
||||
ASSET_TYPE_EMBLEMSET = 0x2F,
|
||||
ASSET_TYPE_SCRIPTPARSETREE = 0x30,
|
||||
ASSET_TYPE_KEYVALUEPAIRS = 0x31,
|
||||
ASSET_TYPE_VEHICLEDEF = 0x32,
|
||||
ASSET_TYPE_MEMORYBLOCK = 0x33,
|
||||
ASSET_TYPE_ADDON_MAP_ENTS = 0x34,
|
||||
ASSET_TYPE_TRACER = 0x35,
|
||||
ASSET_TYPE_SKINNEDVERTS = 0x36,
|
||||
ASSET_TYPE_QDB = 0x37,
|
||||
ASSET_TYPE_SLUG = 0x38,
|
||||
ASSET_TYPE_FOOTSTEP_TABLE = 0x39,
|
||||
ASSET_TYPE_FOOTSTEPFX_TABLE = 0x3A,
|
||||
ASSET_TYPE_ZBARRIER = 0x3B,
|
||||
ASSET_TYPE_COUNT = 0x3C,
|
||||
ASSET_TYPE_STRING = 0x3C,
|
||||
ASSET_TYPE_ASSETLIST = 0x3D,
|
||||
ASSET_TYPE_REPORT = 0x3E,
|
||||
ASSET_TYPE_DEPEND = 0x3F,
|
||||
ASSET_TYPE_FULL_COUNT = 0x40,
|
||||
};
|
||||
|
||||
struct ScriptParseTree
|
||||
{
|
||||
const char* name;
|
||||
int len;
|
||||
GSC_OBJ* obj;
|
||||
};
|
||||
|
||||
struct objFileInfo_t
|
||||
{
|
||||
GSC_OBJ* activeVersion;
|
||||
char __pad[0x24];
|
||||
};
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
ScriptParseTree* scriptParseTree;
|
||||
};
|
||||
}
|
126
src/game/symbols.hpp
Normal file
126
src/game/symbols.hpp
Normal file
@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#define WEAK __declspec(selectany)
|
||||
|
||||
namespace game
|
||||
{
|
||||
// Functions
|
||||
|
||||
WEAK symbol<int(const char* str)> BG_StringHashValue{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_InsertText{0x0, 0x0};
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x0, 0x0};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x0, 0x0};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x0, 0x0};
|
||||
WEAK symbol<const char*(int index)> Cmd_Argv{0x0, 0x0};
|
||||
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(int clientNum)> ClientUserInfoChanged{0x0, 0x0};
|
||||
|
||||
WEAK symbol<const dvar_t*(const char*)> Dvar_FindVar{0x0, 0x0};
|
||||
WEAK symbol<int(const dvar_t*)> Dvar_GetInt{0x0, 0x0};
|
||||
WEAK symbol<dvar_t*(const char* dvarName, int value, int min, int max,
|
||||
unsigned int flags, const char* description)> Dvar_RegisterInt{0x0, 0x0};
|
||||
|
||||
WEAK symbol<XAssetHeader(XAssetType type, const char* name, bool errorIfMissing, int waitTime)> DB_FindXAssetHeader{0x0, 0x0};
|
||||
|
||||
WEAK symbol<char*(const char*)> I_CleanStr{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void*(const char** pName, int* min_args, int* max_args)> Player_GetMethod{0x0, 0x0};
|
||||
WEAK symbol<void*(const char** pName, int* type, int* min_args, int* max_args)> Scr_GetCommonFunction{0x0, 0x0};
|
||||
WEAK symbol<void*(const char** pName, int* type, int* min_args, int* max_args)> Scr_GetMethod{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(scriptInstance_t inst, gentity_s* ent)> Scr_AddEntity{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, float value)> Scr_AddFloat{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* value)> Scr_AddString{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, float* value)> Scr_AddVector{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int id)> Scr_AddObject{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(scriptInstance_t inst)> Scr_ClearOutParams{0x0, 0x588680};
|
||||
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst)> AllocObject{0x0, 0x0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int id)> AllocThread{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int id)> RemoveRefToObject{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const float* vectorValue)> RemoveRefToVector{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const int type, VariableUnion value)> AddRefToValue_{0x0, 0x6706B0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const int type, VariableUnion value)> RemoveRefToValue{0x0, 0x4249C0};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int parentId, unsigned int index)> RemoveVariableValue{0x0, 0x0};
|
||||
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int parentId, unsigned int id)> FindVariable{0x0, 0x5DF7E0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int parentId, unsigned int id)> FindArrayVariable{0x0, 0x509230};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int parentId, unsigned int id)> GetVariable{0x0, 0x0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int parentId, unsigned int name)> GetNewVariable{0x0, 0x5B3750};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x0, 0x468BC0};
|
||||
WEAK symbol<VariableValueInternal_u*(scriptInstance_t inst, unsigned int id)> GetVariableValueAddress{0x0, 0x651390};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int id, const VariableValue* value)> SetNewVariableValue{0x0, 0x4DCA30};
|
||||
|
||||
WEAK symbol<void(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x0, 0x0};
|
||||
WEAK symbol<VariableValue(scriptInstance_t inst, unsigned int classnum, int entnum, int clientNum, int offset)> GetEntityFieldValue{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(gentity_s* ent, unsigned int stringValue, unsigned int paramcount)> Scr_Notify{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, int clientNum, unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x0, 0x0};
|
||||
WEAK symbol<void(int entnum, unsigned int classnum, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyNum{0x0, 0x0};
|
||||
|
||||
WEAK symbol<unsigned int(const char* str, unsigned int user, scriptInstance_t inst)> SL_GetString{0x0, 0x4B1770};
|
||||
WEAK symbol<const char*(unsigned int stringValue, scriptInstance_t inst)> SL_ConvertToString{0x0, 0x624C70};
|
||||
WEAK symbol<unsigned int(const char* str, bool is_static)> SL_GetCanonicalString{0x0, 0x0};
|
||||
|
||||
WEAK symbol<int(scriptInstance_t inst)> Scr_GetNumParam{0x0, 0x0};
|
||||
WEAK symbol<gentity_s*(scriptInstance_t inst, int index)> Scr_GetEntity{0x0, 0x0};
|
||||
WEAK symbol<float(scriptInstance_t inst, int index)> Scr_GetFloat{0x0, 0x0};
|
||||
WEAK symbol<int(scriptInstance_t inst, int index)> Scr_GetInt{0x0, 0x0};
|
||||
WEAK symbol<const char*(scriptInstance_t inst, int index)> Scr_GetString{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, int index, float* out)> Scr_GetVector{0x0, 0x0};
|
||||
WEAK symbol<const float*(scriptInstance_t inst, const float* v)> Scr_AllocVector{0x0, 0x4C4440};
|
||||
WEAK symbol<int(scriptInstance_t inst, const char* filename,
|
||||
const char* name, unsigned int* checksum, bool errorIfMissing)> Scr_GetFunctionHandle{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int classnum, char const* name, unsigned int offset)> Scr_AddClassField{0x0, 0x0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, int entnum, unsigned int classnum, int clientNum)> Scr_GetEntityId{0x0, 0x6A07D0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int localId)> GetStartLocalId{0x0, 0x0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int localId)> Scr_TerminateRunningThread{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* error, bool force_terminal)> Scr_Error{0x0, 0x6245E0};
|
||||
WEAK symbol<void(scriptInstance_t inst, unsigned int paramIndex, const char* error)> Scr_ParamError{0x0, 0x0};
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* error)> Scr_ObjectError{0x0, 0x0};
|
||||
WEAK symbol<scr_entref_t*(scr_entref_t* refref, scriptInstance_t inst, unsigned int entId)> Scr_GetEntityIdRef{0x0, 0x6A53C0};
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst)> Scr_AllocArray{0x0, 0x5B8400};
|
||||
|
||||
WEAK symbol<gentity_s*(scr_entref_t entref)> GetPlayerEntity{0x0, 0x0};
|
||||
|
||||
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_GameDropClient{0x0, 0x0};
|
||||
WEAK symbol<bool(int clientNum)> SV_IsTestClient{0x0, 0x0};
|
||||
WEAK symbol<void(int clientNum, int type, const char* command)> SV_GameSendServerCommand{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void*(int valueIndex)> Sys_GetValue{0x0, 0x0};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x0, 0x9D05C4};
|
||||
WEAK symbol<int(jmp_buf* Buf, int a2)> _setjmp{0x0, 0x9CED5C};
|
||||
|
||||
// Variables
|
||||
|
||||
WEAK symbol<int> g_script_error_level{0x0, 0x3DD4A18};
|
||||
WEAK symbol<jmp_buf> g_script_error{0x0, 0x3DD3998};
|
||||
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0x0, 0x3DCB338};
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x0, 0x3DCB180};
|
||||
WEAK symbol<scrVarPub_t> scr_VarPub{0x0, 0x3DCB280};
|
||||
WEAK symbol<int> scr_starttime{0x0, 0x0};
|
||||
WEAK symbol<function_stack_t> fs{0x0, 0x0};
|
||||
|
||||
WEAK symbol<unsigned short> sv_configstrings{0x0, 0x0};
|
||||
|
||||
WEAK symbol<scr_classStruct_t*> g_classMap{0x0, 0x0};
|
||||
|
||||
WEAK symbol<gentity_s> g_entities{0x0, 0x0};
|
||||
WEAK symbol<unsigned int> levelEntityId{0x0, 0x0};
|
||||
|
||||
WEAK symbol<client_s> svs_clients{0x0, 0x0};
|
||||
|
||||
namespace plutonium
|
||||
{
|
||||
WEAK symbol<int(const char* fmt, ...)> printf{0x0, 0x0};
|
||||
}
|
||||
}
|
35
src/loader/component_interface.hpp
Normal file
35
src/loader/component_interface.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
class component_interface
|
||||
{
|
||||
public:
|
||||
virtual ~component_interface()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_start()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_load()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void pre_destroy()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_unpack()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool is_supported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
127
src/loader/component_loader.cpp
Normal file
127
src/loader/component_loader.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "component_loader.hpp"
|
||||
|
||||
void component_loader::register_component(std::unique_ptr<component_interface>&& component_)
|
||||
{
|
||||
get_components().push_back(std::move(component_));
|
||||
}
|
||||
|
||||
bool component_loader::post_start()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
handled = true;
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_start();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool component_loader::post_load()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return true;
|
||||
handled = true;
|
||||
|
||||
clean();
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_load();
|
||||
}
|
||||
}
|
||||
catch (premature_shutdown_trigger&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void component_loader::post_unpack()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->post_unpack();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::pre_destroy()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled) return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
component_->pre_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void component_loader::clean()
|
||||
{
|
||||
auto& components = get_components();
|
||||
for (auto i = components.begin(); i != components.end();)
|
||||
{
|
||||
if (!(*i)->is_supported())
|
||||
{
|
||||
(*i)->pre_destroy();
|
||||
i = components.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* component_loader::load_import(const std::string& library, const std::string& function)
|
||||
{
|
||||
void* function_ptr = nullptr;
|
||||
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
auto* const component_function_ptr = component_->load_import(library, function);
|
||||
if (component_function_ptr)
|
||||
{
|
||||
function_ptr = component_function_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return function_ptr;
|
||||
}
|
||||
|
||||
void component_loader::trigger_premature_shutdown()
|
||||
{
|
||||
throw premature_shutdown_trigger();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<component_interface>>& component_loader::get_components()
|
||||
{
|
||||
using component_vector = std::vector<std::unique_ptr<component_interface>>;
|
||||
using component_vector_container = std::unique_ptr<component_vector, std::function<void(component_vector*)>>;
|
||||
|
||||
static component_vector_container components(new component_vector, [](component_vector* component_vector)
|
||||
{
|
||||
pre_destroy();
|
||||
delete component_vector;
|
||||
});
|
||||
|
||||
return *components;
|
||||
}
|
61
src/loader/component_loader.hpp
Normal file
61
src/loader/component_loader.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "component_interface.hpp"
|
||||
|
||||
class component_loader final
|
||||
{
|
||||
public:
|
||||
class premature_shutdown_trigger final : public std::exception
|
||||
{
|
||||
[[nodiscard]] const char* what() const noexcept override
|
||||
{
|
||||
return "Premature shutdown requested";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class installer final
|
||||
{
|
||||
static_assert(std::is_base_of<component_interface, T>::value, "component has invalid base class");
|
||||
|
||||
public:
|
||||
installer()
|
||||
{
|
||||
register_component(std::make_unique<T>());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static T* get()
|
||||
{
|
||||
for (const auto& component_ : get_components())
|
||||
{
|
||||
if (typeid(*component_.get()) == typeid(T))
|
||||
{
|
||||
return reinterpret_cast<T*>(component_.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void register_component(std::unique_ptr<component_interface>&& component);
|
||||
|
||||
static bool post_start();
|
||||
static bool post_load();
|
||||
static void post_unpack();
|
||||
static void pre_destroy();
|
||||
static void clean();
|
||||
|
||||
static void* load_import(const std::string& library, const std::string& function);
|
||||
|
||||
static void trigger_premature_shutdown();
|
||||
|
||||
private:
|
||||
static std::vector<std::unique_ptr<component_interface>>& get_components();
|
||||
};
|
||||
|
||||
#define REGISTER_COMPONENT(name) \
|
||||
namespace \
|
||||
{ \
|
||||
static component_loader::installer<name> __component; \
|
||||
}
|
1
src/stdinc.cpp
Normal file
1
src/stdinc.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include <stdinc.hpp>
|
55
src/stdinc.hpp
Normal file
55
src/stdinc.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#pragma warning(disable: 6011)
|
||||
#pragma warning(disable: 6054)
|
||||
#pragma warning(disable: 6387)
|
||||
#pragma warning(disable: 26451)
|
||||
#pragma warning(disable: 26812)
|
||||
#pragma warning(disable: 28182)
|
||||
|
||||
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <csetjmp>
|
||||
#include <atlcomcli.h>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#include <MinHook.h>
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
#include <asmjit/core/jitruntime.h>
|
||||
#include <asmjit/x86/x86assembler.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#pragma comment(lib, "urlmon.lib" )
|
||||
#pragma comment(lib, "iphlpapi.lib")
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
|
||||
using namespace std::literals;
|
170
src/utils/compression.cpp
Normal file
170
src/utils/compression.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "memory.hpp"
|
||||
#include "compression.hpp"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <zip.h>
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include "io.hpp"
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class zlib_stream
|
||||
{
|
||||
public:
|
||||
zlib_stream()
|
||||
{
|
||||
memset(&stream_, 0, sizeof(stream_));
|
||||
valid_ = inflateInit(&stream_) == Z_OK;
|
||||
}
|
||||
|
||||
zlib_stream(zlib_stream&&) = delete;
|
||||
zlib_stream(const zlib_stream&) = delete;
|
||||
zlib_stream& operator=(zlib_stream&&) = delete;
|
||||
zlib_stream& operator=(const zlib_stream&) = delete;
|
||||
|
||||
~zlib_stream()
|
||||
{
|
||||
if (valid_)
|
||||
{
|
||||
inflateEnd(&stream_);
|
||||
}
|
||||
}
|
||||
|
||||
z_stream& get()
|
||||
{
|
||||
return stream_; //
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return valid_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool valid_{false};
|
||||
z_stream stream_{};
|
||||
};
|
||||
}
|
||||
|
||||
std::string decompress(const std::string& data)
|
||||
{
|
||||
std::string buffer{};
|
||||
zlib_stream stream_container{};
|
||||
if (!stream_container.is_valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int ret{};
|
||||
size_t offset = 0;
|
||||
static thread_local uint8_t dest[CHUNK] = {0};
|
||||
auto& stream = stream_container.get();
|
||||
|
||||
do
|
||||
{
|
||||
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||
stream.avail_in = static_cast<uInt>(input_size);
|
||||
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||
offset += stream.avail_in;
|
||||
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(dest);
|
||||
stream.next_out = dest;
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||
}
|
||||
while (stream.avail_out == 0);
|
||||
}
|
||||
while (ret != Z_STREAM_END);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string compress(const std::string& data)
|
||||
{
|
||||
std::string result{};
|
||||
auto length = compressBound(static_cast<uLong>(data.size()));
|
||||
result.resize(length);
|
||||
|
||||
if (compress2(reinterpret_cast<Bytef*>(result.data()), &length,
|
||||
reinterpret_cast<const Bytef*>(data.data()), static_cast<uLong>(data.size()),
|
||||
Z_BEST_COMPRESSION) != Z_OK)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize(length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace zip
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool add_file(zipFile& zip_file, const std::string& filename, const std::string& data)
|
||||
{
|
||||
const auto zip_64 = data.size() > 0xffffffff ? 1 : 0;
|
||||
if (ZIP_OK != zipOpenNewFileInZip64(zip_file, filename.data(), nullptr, nullptr, 0, nullptr, 0, nullptr,
|
||||
Z_DEFLATED, Z_BEST_COMPRESSION, zip_64))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&zip_file]()
|
||||
{
|
||||
zipCloseFileInZip(zip_file);
|
||||
});
|
||||
|
||||
return ZIP_OK == zipWriteInFileInZip(zip_file, data.data(), static_cast<unsigned>(data.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void archive::add(std::string filename, std::string data)
|
||||
{
|
||||
this->files_[std::move(filename)] = std::move(data);
|
||||
}
|
||||
|
||||
bool archive::write(const std::string& filename, const std::string& comment)
|
||||
{
|
||||
// Hack to create the directory :3
|
||||
io::write_file(filename, {});
|
||||
io::remove_file(filename);
|
||||
|
||||
auto* zip_file = zipOpen64(filename.data(), 0);
|
||||
if (!zip_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&zip_file, &comment]()
|
||||
{
|
||||
zipClose(zip_file, comment.empty() ? nullptr : comment.data());
|
||||
});
|
||||
|
||||
for (const auto& file : this->files_)
|
||||
{
|
||||
if (!add_file(zip_file, file.first, file.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
28
src/utils/compression.hpp
Normal file
28
src/utils/compression.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#define CHUNK 16384u
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
std::string compress(const std::string& data);
|
||||
std::string decompress(const std::string& data);
|
||||
}
|
||||
|
||||
namespace zip
|
||||
{
|
||||
class archive
|
||||
{
|
||||
public:
|
||||
void add(std::string filename, std::string data);
|
||||
bool write(const std::string& filename, const std::string& comment = {});
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> files_;
|
||||
};
|
||||
}
|
||||
};
|
46
src/utils/concurrency.hpp
Normal file
46
src/utils/concurrency.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace utils::concurrency
|
||||
{
|
||||
template <typename T, typename MutexType = std::mutex>
|
||||
class container
|
||||
{
|
||||
public:
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor) const
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor)
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor) const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor)
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
T& get_raw() { return object_; }
|
||||
const T& get_raw() const { return object_; }
|
||||
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
};
|
||||
}
|
54
src/utils/flags.cpp
Normal file
54
src/utils/flags.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include <stdinc.hpp>
|
||||
|
||||
#include "flags.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
namespace utils::flags
|
||||
{
|
||||
void parse_flags(std::vector<std::string>& flags)
|
||||
{
|
||||
int num_args;
|
||||
auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||
|
||||
flags.clear();
|
||||
|
||||
if (argv)
|
||||
{
|
||||
for (auto i = 0; i < num_args; ++i)
|
||||
{
|
||||
std::wstring wide_flag(argv[i]);
|
||||
if (wide_flag[0] == L'-')
|
||||
{
|
||||
wide_flag.erase(wide_flag.begin());
|
||||
flags.emplace_back(string::convert(wide_flag));
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(argv);
|
||||
}
|
||||
}
|
||||
|
||||
bool has_flag(const std::string& flag)
|
||||
{
|
||||
static auto parsed = false;
|
||||
static std::vector<std::string> enabled_flags;
|
||||
|
||||
if (!parsed)
|
||||
{
|
||||
parse_flags(enabled_flags);
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
for (const auto& entry : enabled_flags)
|
||||
{
|
||||
if (string::to_lower(entry) == string::to_lower(flag))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
6
src/utils/flags.hpp
Normal file
6
src/utils/flags.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace utils::flags
|
||||
{
|
||||
bool has_flag(const std::string& flag);
|
||||
}
|
208
src/utils/hook.cpp
Normal file
208
src/utils/hook.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "hook.hpp"
|
||||
#include "string.hpp"
|
||||
// iw6x-client
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
namespace
|
||||
{
|
||||
[[maybe_unused]] class _
|
||||
{
|
||||
public:
|
||||
_()
|
||||
{
|
||||
if (MH_Initialize() != MH_OK)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize MinHook");
|
||||
}
|
||||
}
|
||||
|
||||
~_()
|
||||
{
|
||||
MH_Uninitialize();
|
||||
}
|
||||
} __;
|
||||
}
|
||||
|
||||
asmjit::Error assembler::call(void* target)
|
||||
{
|
||||
return Assembler::call(size_t(target));
|
||||
}
|
||||
|
||||
asmjit::Error assembler::jmp(void* target)
|
||||
{
|
||||
return Assembler::jmp(size_t(target));
|
||||
}
|
||||
|
||||
detour::detour(const size_t place, void* target) : detour(reinterpret_cast<void*>(place), target)
|
||||
{
|
||||
}
|
||||
|
||||
detour::detour(void* place, void* target)
|
||||
{
|
||||
this->create(place, target);
|
||||
}
|
||||
|
||||
detour::~detour()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void detour::enable() const
|
||||
{
|
||||
MH_EnableHook(this->place_);
|
||||
}
|
||||
|
||||
void detour::disable() const
|
||||
{
|
||||
MH_DisableHook(this->place_);
|
||||
}
|
||||
|
||||
void detour::create(void* place, void* target)
|
||||
{
|
||||
this->clear();
|
||||
this->place_ = place;
|
||||
|
||||
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
|
||||
{
|
||||
throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_));
|
||||
}
|
||||
|
||||
this->enable();
|
||||
}
|
||||
|
||||
void detour::create(const size_t place, void* target)
|
||||
{
|
||||
this->create(reinterpret_cast<void*>(place), target);
|
||||
}
|
||||
|
||||
void detour::clear()
|
||||
{
|
||||
if (this->place_)
|
||||
{
|
||||
MH_RemoveHook(this->place_);
|
||||
}
|
||||
|
||||
this->place_ = nullptr;
|
||||
this->original_ = nullptr;
|
||||
}
|
||||
|
||||
void* detour::get_original() const
|
||||
{
|
||||
return this->original_;
|
||||
}
|
||||
|
||||
void nop(void* place, const size_t length)
|
||||
{
|
||||
DWORD old_protect{};
|
||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
std::memset(place, 0x90, length);
|
||||
|
||||
VirtualProtect(place, length, old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, length);
|
||||
}
|
||||
|
||||
void nop(const size_t place, const size_t length)
|
||||
{
|
||||
nop(reinterpret_cast<void*>(place), length);
|
||||
}
|
||||
|
||||
void copy(void* place, const void* data, const size_t length)
|
||||
{
|
||||
DWORD old_protect{};
|
||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
std::memmove(place, data, length);
|
||||
|
||||
VirtualProtect(place, length, old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, length);
|
||||
}
|
||||
|
||||
void copy(const size_t place, const void* data, const size_t length)
|
||||
{
|
||||
copy(reinterpret_cast<void*>(place), data, length);
|
||||
}
|
||||
|
||||
bool is_relatively_far(const void* pointer, const void* data, int offset)
|
||||
{
|
||||
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
||||
const auto small_diff = int32_t(diff);
|
||||
return diff != int64_t(small_diff);
|
||||
}
|
||||
|
||||
void call(void* pointer, void* data)
|
||||
{
|
||||
if (is_relatively_far(pointer, data))
|
||||
{
|
||||
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||
}
|
||||
|
||||
auto* patch_pointer = PBYTE(pointer);
|
||||
set<uint8_t>(patch_pointer, 0xE8);
|
||||
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
||||
}
|
||||
|
||||
void call(const size_t pointer, void* data)
|
||||
{
|
||||
return call(reinterpret_cast<void*>(pointer), data);
|
||||
}
|
||||
|
||||
void call(const size_t pointer, const size_t data)
|
||||
{
|
||||
return call(pointer, reinterpret_cast<void*>(data));
|
||||
}
|
||||
|
||||
void set(std::uintptr_t address, std::vector<std::uint8_t>&& bytes)
|
||||
{
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
auto* place = reinterpret_cast<void*>(address);
|
||||
VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
memcpy(place, bytes.data(), bytes.size());
|
||||
VirtualProtect(place, bytes.size(), oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, bytes.size());
|
||||
}
|
||||
|
||||
void set(std::uintptr_t address, void* buffer, size_t size)
|
||||
{
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
auto* place = reinterpret_cast<void*>(address);
|
||||
VirtualProtect(place, size, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
memcpy(place, buffer, size);
|
||||
VirtualProtect(place, size, oldProtect, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, size);
|
||||
}
|
||||
|
||||
void jump(std::uintptr_t address, void* destination)
|
||||
{
|
||||
if (!address) return;
|
||||
|
||||
std::uint8_t* bytes = new std::uint8_t[5];
|
||||
*bytes = 0xE9;
|
||||
*reinterpret_cast<std::uint32_t*>(bytes + 1) = CalculateRelativeJMPAddress(address, destination);
|
||||
|
||||
set(address, bytes, 5);
|
||||
|
||||
delete[] bytes;
|
||||
}
|
||||
|
||||
void* assemble(const std::function<void(assembler&)>& asm_function)
|
||||
{
|
||||
static asmjit::JitRuntime runtime;
|
||||
|
||||
asmjit::CodeHolder code;
|
||||
code.init(runtime.environment());
|
||||
|
||||
assembler a(&code);
|
||||
|
||||
asm_function(a);
|
||||
|
||||
void* result = nullptr;
|
||||
runtime.add(&result, &code);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
134
src/utils/hook.hpp
Normal file
134
src/utils/hook.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#define CalculateRelativeJMPAddress(X, Y) (((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||
|
||||
#include <asmjit/core/jitruntime.h>
|
||||
#include <asmjit/x86/x86assembler.h>
|
||||
|
||||
using namespace asmjit::x86;
|
||||
|
||||
namespace utils::hook
|
||||
{
|
||||
class assembler : public Assembler
|
||||
{
|
||||
public:
|
||||
using Assembler::Assembler;
|
||||
using Assembler::call;
|
||||
using Assembler::jmp;
|
||||
|
||||
asmjit::Error call(void* target);
|
||||
asmjit::Error jmp(void* target);
|
||||
};
|
||||
|
||||
class detour
|
||||
{
|
||||
public:
|
||||
detour() = default;
|
||||
detour(void* place, void* target);
|
||||
detour(size_t place, void* target);
|
||||
~detour();
|
||||
|
||||
detour(detour&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
detour& operator= (detour&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->~detour();
|
||||
|
||||
this->place_ = other.place_;
|
||||
this->original_ = other.original_;
|
||||
|
||||
other.place_ = nullptr;
|
||||
other.original_ = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
detour(const detour&) = delete;
|
||||
detour& operator= (const detour&) = delete;
|
||||
|
||||
void enable() const;
|
||||
void disable() const;
|
||||
|
||||
void create(void* place, void* target);
|
||||
void create(size_t place, void* target);
|
||||
void clear();
|
||||
|
||||
template <typename T>
|
||||
T* get() const
|
||||
{
|
||||
return static_cast<T*>(this->get_original());
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T invoke(Args... args)
|
||||
{
|
||||
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||
}
|
||||
|
||||
[[nodiscard]] void* get_original() const;
|
||||
|
||||
private:
|
||||
void* place_{};
|
||||
void* original_{};
|
||||
};
|
||||
|
||||
void nop(void* place, size_t length);
|
||||
void nop(size_t place, size_t length);
|
||||
|
||||
void copy(void* place, const void* data, size_t length);
|
||||
void copy(size_t place, const void* data, size_t length);
|
||||
|
||||
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||
|
||||
void call(void* pointer, void* data);
|
||||
void call(size_t pointer, void* data);
|
||||
void call(size_t pointer, size_t data);
|
||||
|
||||
void jump(std::uintptr_t address, void* destination);
|
||||
|
||||
void* assemble(const std::function<void(assembler&)>& asm_function);
|
||||
|
||||
template <typename T>
|
||||
T extract(void* address)
|
||||
{
|
||||
const auto data = static_cast<uint8_t*>(address);
|
||||
const auto offset = *reinterpret_cast<int32_t*>(data);
|
||||
return reinterpret_cast<T>(data + offset + 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(void* place, T value)
|
||||
{
|
||||
DWORD old_protect;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
|
||||
*static_cast<T*>(place) = value;
|
||||
|
||||
VirtualProtect(place, sizeof(T), old_protect, &old_protect);
|
||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void set(const size_t place, T value)
|
||||
{
|
||||
return set<T>(reinterpret_cast<void*>(place), value);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(size_t func, Args... args)
|
||||
{
|
||||
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static T invoke(void* func, Args... args)
|
||||
{
|
||||
return static_cast<T(*)(Args ...)>(func)(args...);
|
||||
}
|
||||
}
|
101
src/utils/http.cpp
Normal file
101
src/utils/http.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "http.hpp"
|
||||
#include <curl/curl.h>
|
||||
#include <gsl/gsl>
|
||||
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
namespace utils::http
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct progress_helper
|
||||
{
|
||||
const std::function<void(size_t)>* callback{};
|
||||
std::exception_ptr exception{};
|
||||
};
|
||||
|
||||
int progress_callback(void* clientp, const curl_off_t /*dltotal*/, const curl_off_t dlnow, const curl_off_t /*ultotal*/, const curl_off_t /*ulnow*/)
|
||||
{
|
||||
auto* helper = static_cast<progress_helper*>(clientp);
|
||||
|
||||
try
|
||||
{
|
||||
if (*helper->callback)
|
||||
{
|
||||
(*helper->callback)(dlnow);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
helper->exception = std::current_exception();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp)
|
||||
{
|
||||
auto* buffer = static_cast<std::string*>(userp);
|
||||
|
||||
const auto total_size = size * nmemb;
|
||||
buffer->append(static_cast<char*>(contents), total_size);
|
||||
return total_size;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> get_data(const std::string& url, const headers& headers, const std::function<void(size_t)>& callback)
|
||||
{
|
||||
curl_slist* header_list = nullptr;
|
||||
auto* curl = curl_easy_init();
|
||||
if (!curl)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto _ = gsl::finally([&]()
|
||||
{
|
||||
curl_slist_free_all(header_list);
|
||||
curl_easy_cleanup(curl);
|
||||
});
|
||||
|
||||
for (const auto& header : headers)
|
||||
{
|
||||
auto data = header.first + ": " + header.second;
|
||||
header_list = curl_slist_append(header_list, data.data());
|
||||
}
|
||||
|
||||
std::string buffer{};
|
||||
progress_helper helper{};
|
||||
helper.callback = &callback;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.data());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
|
||||
if (curl_easy_perform(curl) == CURLE_OK)
|
||||
{
|
||||
return {std::move(buffer)};
|
||||
}
|
||||
|
||||
if (helper.exception)
|
||||
{
|
||||
std::rethrow_exception(helper.exception);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
||||
{
|
||||
return std::async(std::launch::async, [url, headers]()
|
||||
{
|
||||
return get_data(url, headers);
|
||||
});
|
||||
}
|
||||
}
|
13
src/utils/http.hpp
Normal file
13
src/utils/http.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <future>
|
||||
|
||||
namespace utils::http
|
||||
{
|
||||
using headers = std::unordered_map<std::string, std::string>;
|
||||
|
||||
std::optional<std::string> get_data(const std::string& url, const headers& headers = {}, const std::function<void(size_t)>& callback = {});
|
||||
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
|
||||
}
|
123
src/utils/io.cpp
Normal file
123
src/utils/io.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include <stdinc.hpp>
|
||||
#include <fstream>
|
||||
#include "io.hpp"
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file)
|
||||
{
|
||||
return DeleteFileA(file.data()) == TRUE;
|
||||
}
|
||||
|
||||
bool move_file(const std::string& src, const std::string& target)
|
||||
{
|
||||
return MoveFileA(src.data(), target.data()) == TRUE;
|
||||
}
|
||||
|
||||
bool file_exists(const std::string& file)
|
||||
{
|
||||
return std::ifstream(file).good();
|
||||
}
|
||||
|
||||
bool write_file(const std::string& file, const std::string& data, const bool append)
|
||||
{
|
||||
const auto pos = file.find_last_of("/\\");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
create_directory(file.substr(0, pos));
|
||||
}
|
||||
|
||||
std::ofstream stream(
|
||||
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : 0));
|
||||
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.write(data.data(), data.size());
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string read_file(const std::string& file)
|
||||
{
|
||||
std::string data;
|
||||
read_file(file, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool read_file(const std::string& file, std::string* data)
|
||||
{
|
||||
if (!data) return false;
|
||||
data->clear();
|
||||
|
||||
if (file_exists(file))
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream.is_open()) return false;
|
||||
|
||||
stream.seekg(0, std::ios::end);
|
||||
const std::streamsize size = stream.tellg();
|
||||
stream.seekg(0, std::ios::beg);
|
||||
|
||||
if (size > -1)
|
||||
{
|
||||
data->resize(static_cast<uint32_t>(size));
|
||||
stream.read(const_cast<char*>(data->data()), size);
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t file_size(const std::string& file)
|
||||
{
|
||||
if (file_exists(file))
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
|
||||
if (stream.good())
|
||||
{
|
||||
stream.seekg(0, std::ios::end);
|
||||
return static_cast<size_t>(stream.tellg());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool create_directory(const std::string& directory)
|
||||
{
|
||||
return std::filesystem::create_directories(directory);
|
||||
}
|
||||
|
||||
bool directory_exists(const std::string& directory)
|
||||
{
|
||||
return std::filesystem::is_directory(directory);
|
||||
}
|
||||
|
||||
bool directory_is_empty(const std::string& directory)
|
||||
{
|
||||
return std::filesystem::is_empty(directory);
|
||||
}
|
||||
|
||||
std::vector<std::string> list_files(const std::string& directory)
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
|
||||
for (auto& file : std::filesystem::directory_iterator(directory))
|
||||
{
|
||||
files.push_back(file.path().generic_string());
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
std::filesystem::copy(src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive);
|
||||
}
|
||||
}
|
21
src/utils/io.hpp
Normal file
21
src/utils/io.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file);
|
||||
bool move_file(const std::string& src, const std::string& target);
|
||||
bool file_exists(const std::string& file);
|
||||
bool write_file(const std::string& file, const std::string& data, bool append = false);
|
||||
bool read_file(const std::string& file, std::string* data);
|
||||
std::string read_file(const std::string& file);
|
||||
size_t file_size(const std::string& file);
|
||||
bool create_directory(const std::string& directory);
|
||||
bool directory_exists(const std::string& directory);
|
||||
bool directory_is_empty(const std::string& directory);
|
||||
std::vector<std::string> list_files(const std::string& directory);
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
}
|
109
src/utils/memory.cpp
Normal file
109
src/utils/memory.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
memory::allocator memory::mem_allocator_;
|
||||
|
||||
memory::allocator::~allocator()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void memory::allocator::clear()
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
for (auto& data : this->pool_)
|
||||
{
|
||||
memory::free(data);
|
||||
}
|
||||
|
||||
this->pool_.clear();
|
||||
}
|
||||
|
||||
void memory::allocator::free(void* data)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||
if (j != this->pool_.end())
|
||||
{
|
||||
memory::free(data);
|
||||
this->pool_.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
void memory::allocator::free(const void* data)
|
||||
{
|
||||
this->free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
void* memory::allocator::allocate(const size_t length)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::allocate(length);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool memory::allocator::empty() const
|
||||
{
|
||||
return this->pool_.empty();
|
||||
}
|
||||
|
||||
char* memory::allocator::duplicate_string(const std::string& string)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::duplicate_string(string);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void* memory::allocate(const size_t length)
|
||||
{
|
||||
return calloc(length, 1);
|
||||
}
|
||||
|
||||
char* memory::duplicate_string(const std::string& string)
|
||||
{
|
||||
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||
std::memcpy(new_string, string.data(), string.size());
|
||||
return new_string;
|
||||
}
|
||||
|
||||
void memory::free(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
::free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void memory::free(const void* data)
|
||||
{
|
||||
free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
bool memory::is_set(const void* mem, const char chr, const size_t length)
|
||||
{
|
||||
const auto mem_arr = static_cast<const char*>(mem);
|
||||
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (mem_arr[i] != chr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
memory::allocator* memory::get_allocator()
|
||||
{
|
||||
return &memory::mem_allocator_;
|
||||
}
|
||||
}
|
75
src/utils/memory.hpp
Normal file
75
src/utils/memory.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class memory final
|
||||
{
|
||||
public:
|
||||
class allocator final
|
||||
{
|
||||
public:
|
||||
~allocator();
|
||||
|
||||
void clear();
|
||||
|
||||
void free(void* data);
|
||||
|
||||
void free(const void* data);
|
||||
|
||||
void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate()
|
||||
{
|
||||
return this->allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
bool empty() const;
|
||||
|
||||
char* duplicate_string(const std::string& string);
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::vector<void*> pool_;
|
||||
};
|
||||
|
||||
static void* allocate(size_t length);
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate()
|
||||
{
|
||||
return allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* allocate_array(const size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
static char* duplicate_string(const std::string& string);
|
||||
|
||||
static void free(void* data);
|
||||
static void free(const void* data);
|
||||
|
||||
static bool is_set(const void* mem, char chr, size_t length);
|
||||
|
||||
static bool is_bad_read_ptr(const void* ptr);
|
||||
static bool is_bad_code_ptr(const void* ptr);
|
||||
static bool is_rdata_ptr(void* ptr);
|
||||
|
||||
static allocator* get_allocator();
|
||||
|
||||
private:
|
||||
static allocator mem_allocator_;
|
||||
};
|
||||
}
|
172
src/utils/string.cpp
Normal file
172
src/utils/string.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "string.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
const char* va(const char* fmt, ...)
|
||||
{
|
||||
static thread_local va_provider<8, 256> provider;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
const char* result = provider.get(fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const char delim)
|
||||
{
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
std::vector<std::string> elems;
|
||||
|
||||
while (std::getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::string to_lower(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||
{
|
||||
return static_cast<char>(tolower(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string to_upper(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||
{
|
||||
return static_cast<char>(toupper(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool starts_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
return text.find(substring) == 0;
|
||||
}
|
||||
|
||||
bool ends_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
if (substring.size() > text.size()) return false;
|
||||
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
|
||||
}
|
||||
|
||||
bool is_numeric(const std::string& text)
|
||||
{
|
||||
return std::to_string(atoi(text.data())) == text;
|
||||
}
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (unsigned int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(va("%02X", data[i] & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void strip(const char* in, char* out, int max)
|
||||
{
|
||||
if (!in || !out) return;
|
||||
|
||||
max--;
|
||||
auto current = 0;
|
||||
while (*in != 0 && current < max)
|
||||
{
|
||||
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
|
||||
|
||||
if (*in == '^' && (color_index != 7 || *(in + 1) == '7'))
|
||||
{
|
||||
++in;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out = *in;
|
||||
++out;
|
||||
++current;
|
||||
}
|
||||
|
||||
++in;
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
std::string convert(const std::wstring& wstr)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(wstr.size());
|
||||
|
||||
for (const auto& chr : wstr)
|
||||
{
|
||||
result.push_back(static_cast<char>(chr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring convert(const std::string& str)
|
||||
{
|
||||
std::wstring result;
|
||||
result.reserve(str.size());
|
||||
|
||||
for (const auto& chr : str)
|
||||
{
|
||||
result.push_back(static_cast<wchar_t>(chr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty())
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
||||
{
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string get_timestamp()
|
||||
{
|
||||
tm ltime{};
|
||||
char timestamp[MAX_PATH] = { 0 };
|
||||
const auto time = _time64(nullptr);
|
||||
|
||||
_localtime64_s(<ime, &time);
|
||||
strftime(timestamp, sizeof(timestamp) - 1, "%Y-%m-%d-%H-%M-%S", <ime);
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
}
|
101
src/utils/string.hpp
Normal file
101
src/utils/string.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
template <class Type, size_t n>
|
||||
size_t ARRAYSIZE(Type(&)[n]) { return n; }
|
||||
#endif
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
template <size_t Buffers, size_t MinBufferSize>
|
||||
class va_provider final
|
||||
{
|
||||
public:
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||
|
||||
va_provider() : current_buffer_(0)
|
||||
{
|
||||
}
|
||||
|
||||
char* get(const char* format, const va_list ap)
|
||||
{
|
||||
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
|
||||
auto entry = &this->string_pool_[this->current_buffer_];
|
||||
|
||||
if (!entry->size || !entry->buffer)
|
||||
{
|
||||
throw std::runtime_error("String pool not initialized");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return nullptr; // Error
|
||||
|
||||
entry->double_size();
|
||||
}
|
||||
|
||||
return entry->buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
class entry final
|
||||
{
|
||||
public:
|
||||
explicit entry(const size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||
{
|
||||
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
~entry()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
this->size = 0;
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1);
|
||||
}
|
||||
|
||||
void double_size()
|
||||
{
|
||||
this->size *= 2;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
size_t size;
|
||||
char* buffer;
|
||||
};
|
||||
|
||||
size_t current_buffer_;
|
||||
entry string_pool_[Buffers];
|
||||
};
|
||||
|
||||
const char* va(const char* fmt, ...);
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delim);
|
||||
|
||||
std::string to_lower(std::string text);
|
||||
std::string to_upper(std::string text);
|
||||
bool starts_with(const std::string& text, const std::string& substring);
|
||||
bool ends_with(const std::string& text, const std::string& substring);
|
||||
bool is_numeric(const std::string& text);
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||
|
||||
void strip(const char* in, char* out, int max);
|
||||
|
||||
std::string convert(const std::wstring& wstr);
|
||||
std::wstring convert(const std::string& str);
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to);
|
||||
|
||||
std::string get_timestamp();
|
||||
}
|
89
src/utils/thread.cpp
Normal file
89
src/utils/thread.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include <stdinc.hpp>
|
||||
#include "thread.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#include <TlHelp32.h>
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
namespace utils::thread
|
||||
{
|
||||
std::vector<DWORD> get_thread_ids()
|
||||
{
|
||||
auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([h]()
|
||||
{
|
||||
CloseHandle(h);
|
||||
});
|
||||
|
||||
THREADENTRY32 entry{};
|
||||
entry.dwSize = sizeof(entry);
|
||||
if (!Thread32First(h, &entry))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<DWORD> ids{};
|
||||
|
||||
do
|
||||
{
|
||||
const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID)
|
||||
+ sizeof(entry.th32OwnerProcessID);
|
||||
entry.dwSize = sizeof(entry);
|
||||
|
||||
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId())
|
||||
{
|
||||
ids.emplace_back(entry.th32ThreadID);
|
||||
}
|
||||
}
|
||||
while (Thread32Next(h, &entry));
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
void for_each_thread(const std::function<void(HANDLE)>& callback)
|
||||
{
|
||||
const auto ids = get_thread_ids();
|
||||
|
||||
for (const auto& id : ids)
|
||||
{
|
||||
auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
|
||||
if (thread != nullptr)
|
||||
{
|
||||
const auto _ = gsl::finally([thread]()
|
||||
{
|
||||
CloseHandle(thread);
|
||||
});
|
||||
|
||||
callback(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void suspend_other_threads()
|
||||
{
|
||||
for_each_thread([](const HANDLE thread)
|
||||
{
|
||||
if (GetThreadId(thread) != GetCurrentThreadId())
|
||||
{
|
||||
SuspendThread(thread);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void resume_other_threads()
|
||||
{
|
||||
for_each_thread([](const HANDLE thread)
|
||||
{
|
||||
if (GetThreadId(thread) != GetCurrentThreadId())
|
||||
{
|
||||
ResumeThread(thread);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
19
src/utils/thread.hpp
Normal file
19
src/utils/thread.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <thread>
|
||||
|
||||
namespace utils::thread
|
||||
{
|
||||
template <typename ...Args>
|
||||
std::thread create_named_thread(const std::string& name, Args&&... args)
|
||||
{
|
||||
auto t = std::thread(std::forward<Args>(args)...);
|
||||
set_name(t, name);
|
||||
return t;
|
||||
}
|
||||
|
||||
std::vector<DWORD> get_thread_ids();
|
||||
void for_each_thread(const std::function<void(HANDLE)>& callback);
|
||||
|
||||
void suspend_other_threads();
|
||||
void resume_other_threads();
|
||||
}
|
BIN
tools/windows/premake5.exe
Normal file
BIN
tools/windows/premake5.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user