mirror of
https://github.com/fedddddd/iw5-gsc-utils.git
synced 2025-04-21 05:15:44 +00:00
Initial commit
This commit is contained in:
parent
daf26438b0
commit
dc150bb940
63
.gitattributes
vendored
63
.gitattributes
vendored
@ -1,2 +1,63 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
|
150
.gitignore
vendored
Normal file
150
.gitignore
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
### Windows
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### OSX
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear on external disk
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Visual Studio
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
build
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
### IDA
|
||||||
|
*.id0
|
||||||
|
*.id1
|
||||||
|
*.id2
|
||||||
|
*.nam
|
||||||
|
*.til
|
||||||
|
|
||||||
|
### Custom user files
|
||||||
|
# User scripts
|
||||||
|
user*.bat
|
||||||
|
|
||||||
|
# Premake binary
|
||||||
|
#premake5.exe
|
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[submodule "deps/minhook"]
|
||||||
|
path = deps/minhook
|
||||||
|
url = https://github.com/TsudaKageyu/minhook.git
|
||||||
|
[submodule "deps/GSL"]
|
||||||
|
path = deps/GSL
|
||||||
|
url = https://github.com/microsoft/GSL.git
|
||||||
|
[submodule "deps/sol2"]
|
||||||
|
path = deps/sol2
|
||||||
|
url = https://github.com/ThePhD/sol2.git
|
||||||
|
[submodule "deps/lua"]
|
||||||
|
path = deps/lua
|
||||||
|
url = https://github.com/lua/lua.git
|
111
README.md
Normal file
111
README.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# iw5-script
|
||||||
|
|
||||||
|
Lua scripting support for Plutonium IW5
|
||||||
|
|
||||||
|
Works the same as it does in [IW6x](https://github.com/XLabsProject/iw6x-client/wiki/Scripting)
|
||||||
|
|
||||||
|
# How
|
||||||
|
|
||||||
|
* Download the latest version from the Releases tab
|
||||||
|
* Copy it to `%localappdata%/Plutonium/storage/iw5/plugins/`
|
||||||
|
* Create a `__init__.lua` file in a folder with a name of your choice in `%localappdata%/Plutonium/storage/iw5/scripts/`
|
||||||
|
* Example `%localappdata%/Plutonium/storage/iw5/scripts/myscript/__init__.lua`
|
||||||
|
* Run the server (preferably with the `-no-scripting` flag to disable ChaiScript)
|
||||||
|
|
||||||
|
Below are some features that are not available or documented in IW6x
|
||||||
|
|
||||||
|
# Chat notifies
|
||||||
|
```lua
|
||||||
|
level:onnotify("say", function(player, message)
|
||||||
|
print(player.name .. " said: " .. message)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```lua
|
||||||
|
level:onnotify("connected", function(player)
|
||||||
|
player:onnotify("say", function(message)
|
||||||
|
print(player.name .. " said: " .. message)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Player damage/killed callbacks
|
||||||
|
|
||||||
|
Callbacks can be added using the `game:onplayerkilled` or `game:onplayerdamage` functions:
|
||||||
|
|
||||||
|
Damage can be changed by returning it
|
||||||
|
|
||||||
|
Returning anything other than a number will not do anything (must be an integer)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
game:onplayerdamage(function(_self, inflictor, attacker, damage, dflags, mod, weapon, point, dir, hitloc)
|
||||||
|
damage = 0
|
||||||
|
|
||||||
|
return damage
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
```lua
|
||||||
|
game:onplayerkilled(function(_self, inflictor, attacker, damage, mod, weapon, dir, hitloc, timeoffset, deathanimduration)
|
||||||
|
print(attacker.name .. " killed " .. _self.name)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Arrays
|
||||||
|
GSC arrays are supported and can be accessed similarly to gsc:
|
||||||
|
```lua
|
||||||
|
local ents = game:getentarray()
|
||||||
|
|
||||||
|
for i = 1, #ents do
|
||||||
|
print(ents[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Structs
|
||||||
|
GSC structs are also supported similarly as the arrays.
|
||||||
|
|
||||||
|
To get an entity's struct use the `getstruct` method:
|
||||||
|
```lua
|
||||||
|
local levelstruct = level:getstruct()
|
||||||
|
|
||||||
|
levelstruct.inGracePeriod = 10000
|
||||||
|
```
|
||||||
|
Structs in other variables like arrays are automatically converted:
|
||||||
|
```lua
|
||||||
|
level:onnotify("connected", function(player)
|
||||||
|
player:onnotify("spawned_player", function()
|
||||||
|
player.pers.killstreaks[1].streakName = "ac130"
|
||||||
|
player.pers.killstreaks[1].available = 1
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: you cannot create new struct fields but only modify or read existing ones, same thing for arrays
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
|
||||||
|
You can call (will not work for every function) functions and methods within the game's gsc scripts using the
|
||||||
|
|
||||||
|
`scriptcall(filename, function, ...)` method:
|
||||||
|
```lua
|
||||||
|
level:onnotify("connected", function(player)
|
||||||
|
player:onnotify("spawned_player", function()
|
||||||
|
local hudelem = player:scriptcall("maps/mp/gametypes/_hud_utils", "createFontString", 1)
|
||||||
|
|
||||||
|
hudelem:scriptcall("maps/mp/gametypes/_hud_util", "setPoint", "CENTER", nil, 100, 100)
|
||||||
|
hudelem.label = "&Hello world"
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
Functions in variables such as structs or arrays will be automatically converted to a lua function.
|
||||||
|
|
||||||
|
The first argument must always be the entity to call the function on (level, player...)
|
||||||
|
```lua
|
||||||
|
local levelstruct = level:getstruct()
|
||||||
|
|
||||||
|
level:onnotify("connected", function(player)
|
||||||
|
player:onnotify("spawned_player", function()
|
||||||
|
levelstruct.killstreakFuncs["ac130"](player)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
```
|
1
deps/GSL
vendored
Submodule
1
deps/GSL
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c1cbb41b428f15e53454682a45f03ea31f1da0a7
|
1
deps/minhook
vendored
Submodule
1
deps/minhook
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 423d1e45af2ed2719a5c31e990e935ef301ed9c3
|
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)
|
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)
|
2
generate.bat
Normal file
2
generate.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
tools\windows\premake5.exe vs2019
|
90
premake5.lua
Normal file
90
premake5.lua
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
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 "iw5-gsc-utils"
|
||||||
|
location "./build"
|
||||||
|
objdir "%{wks.location}/obj/%{cfg.buildcfg}"
|
||||||
|
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||||
|
targetname "%{prj.name}"
|
||||||
|
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
architecture "x86"
|
||||||
|
|
||||||
|
buildoptions "/std:c++latest"
|
||||||
|
systemversion "latest"
|
||||||
|
|
||||||
|
flags
|
||||||
|
{
|
||||||
|
"NoIncrementalLink",
|
||||||
|
"MultiProcessorCompile",
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations { "Debug", "Release", }
|
||||||
|
|
||||||
|
symbols "On"
|
||||||
|
|
||||||
|
configuration "Release"
|
||||||
|
optimize "Full"
|
||||||
|
defines { "NDEBUG" }
|
||||||
|
configuration{}
|
||||||
|
|
||||||
|
configuration "Debug"
|
||||||
|
optimize "Debug"
|
||||||
|
defines { "DEBUG", "_DEBUG" }
|
||||||
|
configuration {}
|
||||||
|
|
||||||
|
startproject "iw5-gsc-utils"
|
||||||
|
|
||||||
|
project "iw5-gsc-utils"
|
||||||
|
kind "SharedLib"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
pchheader "stdinc.hpp"
|
||||||
|
pchsource "src/stdinc.cpp"
|
||||||
|
|
||||||
|
includedirs
|
||||||
|
{
|
||||||
|
"src"
|
||||||
|
}
|
||||||
|
|
||||||
|
files
|
||||||
|
{
|
||||||
|
"src/**.h",
|
||||||
|
"src/**.hpp",
|
||||||
|
"src/**.cpp"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies.imports()
|
||||||
|
|
||||||
|
group "Dependencies"
|
||||||
|
dependencies.projects()
|
244
src/component/gsc.cpp
Normal file
244
src/component/gsc.cpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/event.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "game/scripting/functions.hpp"
|
||||||
|
|
||||||
|
namespace gsc
|
||||||
|
{
|
||||||
|
using function_args = std::vector<scripting::script_value>;
|
||||||
|
|
||||||
|
using builtin_function = void(*)();
|
||||||
|
using builtin_method = void(*)(game::scr_entref_t);
|
||||||
|
|
||||||
|
using script_function = std::function<scripting::script_value(function_args)>;
|
||||||
|
using script_method = std::function<scripting::script_value(game::scr_entref_t, function_args)>;
|
||||||
|
|
||||||
|
std::unordered_map<unsigned, script_function> functions;
|
||||||
|
std::unordered_map<unsigned, script_method> methods;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void gsc_function_stub(game::scr_entref_t ent)
|
||||||
|
{
|
||||||
|
/*const auto function = scripting::script_value(*game::scr_VmPub->top);
|
||||||
|
|
||||||
|
if (!function.is<std::string>())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto function_str = function.as<std::string>();
|
||||||
|
|
||||||
|
if (gsc_functions.find(function_str) != gsc_functions.end())
|
||||||
|
{
|
||||||
|
std::vector<scripting::script_value> arguments;
|
||||||
|
|
||||||
|
for (auto i = 1; i < game::scr_VmPub->outparamcount; i++)
|
||||||
|
{
|
||||||
|
const auto value = game::scr_VmPub->top[-i];
|
||||||
|
|
||||||
|
arguments.emplace_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = gsc_functions[function_str](ent, arguments);
|
||||||
|
if (*reinterpret_cast<const int*>(&value))
|
||||||
|
{
|
||||||
|
game::Scr_ClearOutParams();
|
||||||
|
scripting::push_value(value);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string method_name(unsigned int id)
|
||||||
|
{
|
||||||
|
const auto map = *game::plutonium::method_map_rev;
|
||||||
|
|
||||||
|
for (const auto& function : map)
|
||||||
|
{
|
||||||
|
if (function.second == id)
|
||||||
|
{
|
||||||
|
return function.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string function_name(unsigned int id)
|
||||||
|
{
|
||||||
|
const auto map = *game::plutonium::function_map_rev;
|
||||||
|
|
||||||
|
for (const auto& function : map)
|
||||||
|
{
|
||||||
|
if (function.second == id)
|
||||||
|
{
|
||||||
|
return function.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function_args get_arguments()
|
||||||
|
{
|
||||||
|
function_args args;
|
||||||
|
|
||||||
|
const auto top = game::scr_VmPub->top;
|
||||||
|
|
||||||
|
for (auto i = 0; i < game::scr_VmPub->outparamcount; i++)
|
||||||
|
{
|
||||||
|
const auto value = game::scr_VmPub->top[i];
|
||||||
|
args.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto function_map_start = 0x200;
|
||||||
|
auto method_map_start = 0x8400;
|
||||||
|
|
||||||
|
void call_function(unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= 0x200)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto result = functions[id](get_arguments());
|
||||||
|
scripting::push_value(result);
|
||||||
|
}
|
||||||
|
catch (std::exception e)
|
||||||
|
{
|
||||||
|
printf("************** Script execution error **************\n");
|
||||||
|
printf("Error executing function %s\n", function_name(id).data());
|
||||||
|
printf("%s\n", e.what());
|
||||||
|
printf("****************************************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reinterpret_cast<builtin_function*>(0x1D6EB34)[id]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_method(game::scr_entref_t ent, unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= 0x8400)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto result = methods[id](ent, get_arguments());
|
||||||
|
scripting::push_value(result);
|
||||||
|
}
|
||||||
|
catch (std::exception e)
|
||||||
|
{
|
||||||
|
printf("************** Script execution error **************\n");
|
||||||
|
printf("Error executing method %s\n", method_name(id).data());
|
||||||
|
printf("%s\n", e.what());
|
||||||
|
printf("****************************************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reinterpret_cast<builtin_method*>(0x1D4F258)[id](ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void call_builtin_stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax
|
||||||
|
|
||||||
|
mov eax, 0x20B4A5C
|
||||||
|
mov [eax], esi
|
||||||
|
|
||||||
|
mov eax, 0x20B4A90
|
||||||
|
mov [eax], edx
|
||||||
|
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
pushad
|
||||||
|
push eax
|
||||||
|
call call_function
|
||||||
|
pop eax
|
||||||
|
popad
|
||||||
|
|
||||||
|
push 0x56C900
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void call_builtin_method_stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
push ecx
|
||||||
|
push ebx
|
||||||
|
call call_method
|
||||||
|
pop ebx
|
||||||
|
pop ecx
|
||||||
|
popad
|
||||||
|
|
||||||
|
push ebx
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
push 0x56CBE9
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace function
|
||||||
|
{
|
||||||
|
void add(const std::string& name, const script_function& func)
|
||||||
|
{
|
||||||
|
const auto index = function_map_start++;
|
||||||
|
|
||||||
|
functions[index] = func;
|
||||||
|
(*game::plutonium::function_map_rev)[name] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace method
|
||||||
|
{
|
||||||
|
void add(const std::string& name, const script_method& func)
|
||||||
|
{
|
||||||
|
const auto index = method_map_start++;
|
||||||
|
|
||||||
|
methods[index] = func;
|
||||||
|
(*game::plutonium::method_map_rev)[name] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
function::add("lol", [](function_args args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
const auto str = args[0].as<std::string>();
|
||||||
|
|
||||||
|
return str;
|
||||||
|
});
|
||||||
|
|
||||||
|
method::add("test", [](game::scr_entref_t, function_args args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
printf("here\n");
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
|
utils::hook::jump(0x56C8EB, call_builtin_stub);
|
||||||
|
utils::hook::jump(0x56CBDC, call_builtin_method_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(gsc::component)
|
58
src/component/notifies.cpp
Normal file
58
src/component/notifies.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/entity.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "notifies.hpp"
|
||||||
|
|
||||||
|
namespace notifies
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::hook::detour client_command_hook;
|
||||||
|
|
||||||
|
utils::hook::detour scr_player_killed_hook;
|
||||||
|
utils::hook::detour scr_player_damage_hook;
|
||||||
|
|
||||||
|
void client_command_stub(int clientNum)
|
||||||
|
{
|
||||||
|
char cmd[1024] = { 0 };
|
||||||
|
|
||||||
|
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
||||||
|
|
||||||
|
if (cmd == "say"s)
|
||||||
|
{
|
||||||
|
std::string message = game::ConcatArgs(1);
|
||||||
|
message.erase(0, 1);
|
||||||
|
|
||||||
|
scheduler::once([message, clientNum]()
|
||||||
|
{
|
||||||
|
const scripting::entity level{*game::levelEntityId};
|
||||||
|
const auto _player = scripting::call("getEntByNum", {clientNum});
|
||||||
|
|
||||||
|
if (_player.get_raw().type == game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
const auto player = _player.as<scripting::entity>();
|
||||||
|
|
||||||
|
scripting::notify(level, "say", {player, message});
|
||||||
|
scripting::notify(player, "say", {message});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return client_command_hook.invoke<void>(clientNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
client_command_hook.create(0x502CB0, client_command_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(notifies::component)
|
5
src/component/notifies.hpp
Normal file
5
src/component/notifies.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace notifies
|
||||||
|
{
|
||||||
|
}
|
83
src/component/scheduler.cpp
Normal file
83
src/component/scheduler.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::queue<std::function<void()>> tasks;
|
||||||
|
|
||||||
|
struct task
|
||||||
|
{
|
||||||
|
std::function<bool()> handler;
|
||||||
|
std::chrono::milliseconds interval{};
|
||||||
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
|
};
|
||||||
|
|
||||||
|
utils::concurrent_list<task> callbacks;
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
for (auto callback : callbacks)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - callback->last_call;
|
||||||
|
|
||||||
|
if (diff < callback->interval) continue;
|
||||||
|
|
||||||
|
callback->last_call = now;
|
||||||
|
|
||||||
|
const auto res = callback->handler();
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
callbacks.remove(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_frame()
|
||||||
|
{
|
||||||
|
execute();
|
||||||
|
reinterpret_cast<void (*)()>(0x50C1E0)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
task task;
|
||||||
|
task.handler = callback;
|
||||||
|
task.interval = delay;
|
||||||
|
task.last_call = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
callbacks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(const std::function<void()>& callback, const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return false;
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void once(const std::function<void()>& callback, const std::chrono::milliseconds delay)
|
||||||
|
{
|
||||||
|
schedule([callback]()
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
return true;
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
utils::hook::call(0x50CEDC, server_frame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(scheduler::component)
|
10
src/component/scheduler.hpp
Normal file
10
src/component/scheduler.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace scheduler
|
||||||
|
{
|
||||||
|
void schedule(const std::function<bool()>& callback, std::chrono::milliseconds delay = 0ms);
|
||||||
|
void loop(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
|
||||||
|
void once(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
}
|
139
src/component/scripting.cpp
Normal file
139
src/component/scripting.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include "game/scripting/event.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "game/scripting/functions.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||||
|
std::unordered_map<std::string, std::unordered_map<std::string, char*>> script_function_table;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::hook::detour vm_notify_hook;
|
||||||
|
utils::hook::detour scr_add_class_field_hook;
|
||||||
|
|
||||||
|
utils::hook::detour scr_load_level_hook;
|
||||||
|
utils::hook::detour g_shutdown_game_hook;
|
||||||
|
|
||||||
|
utils::hook::detour scr_emit_function_hook;
|
||||||
|
utils::hook::detour scr_end_load_scripts_hook;
|
||||||
|
|
||||||
|
void vm_notify_stub(const unsigned int notify_list_owner_id, const unsigned int string_value,
|
||||||
|
game::VariableValue* top)
|
||||||
|
{
|
||||||
|
const auto* name = game::SL_ConvertToString(string_value);
|
||||||
|
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
event e;
|
||||||
|
e.name = name;
|
||||||
|
e.entity = notify_list_owner_id;
|
||||||
|
|
||||||
|
for (auto* value = top; value->type != game::SCRIPT_END; --value)
|
||||||
|
{
|
||||||
|
e.arguments.emplace_back(*value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.name == "connected")
|
||||||
|
{
|
||||||
|
scripting::clear_entity_fields(e.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scr_add_class_field_stub(unsigned int classnum, unsigned int _name, unsigned int canonicalString, unsigned int offset)
|
||||||
|
{
|
||||||
|
const auto name = game::SL_ConvertToString(_name);
|
||||||
|
|
||||||
|
if (fields_table[classnum].find(name) == fields_table[classnum].end())
|
||||||
|
{
|
||||||
|
fields_table[classnum][name] = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_add_class_field_hook.invoke<void>(classnum, _name, canonicalString, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scr_load_level_stub()
|
||||||
|
{
|
||||||
|
scr_load_level_hook.invoke<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void g_shutdown_game_stub(const int free_scripts)
|
||||||
|
{
|
||||||
|
g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* function_pos(unsigned int filename, unsigned int name)
|
||||||
|
{
|
||||||
|
const auto scripts_pos = *reinterpret_cast<int*>(0x1D6EB14);
|
||||||
|
|
||||||
|
const auto v2 = game::FindVariable(scripts_pos, filename);
|
||||||
|
|
||||||
|
const auto v3 = game::FindObject(scripts_pos, v2);
|
||||||
|
const auto v4 = game::FindVariable(v3, name);
|
||||||
|
|
||||||
|
if (!v2 || !v3 || !v4)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::hook::invoke<char*>(0x5659C0, v3, v4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scr_emit_function_stub(unsigned int filename, unsigned int threadName, char* codePos)
|
||||||
|
{
|
||||||
|
const auto* name = game::SL_ConvertToString(filename);
|
||||||
|
const auto filename_id = atoi(name);
|
||||||
|
|
||||||
|
for (const auto& entry : scripting::file_list)
|
||||||
|
{
|
||||||
|
if (entry.first == filename_id)
|
||||||
|
{
|
||||||
|
if (script_function_table.find(entry.second) == script_function_table.end())
|
||||||
|
{
|
||||||
|
script_function_table[entry.second] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& token : scripting::token_map)
|
||||||
|
{
|
||||||
|
if (token.second == threadName)
|
||||||
|
{
|
||||||
|
const auto pos = function_pos(filename, threadName);
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
{
|
||||||
|
script_function_table[entry.second][token.first] = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_emit_function_hook.invoke<void>(filename, threadName, codePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
scr_load_level_hook.create(0x527AF0, scr_load_level_stub);
|
||||||
|
g_shutdown_game_hook.create(0x50C100, g_shutdown_game_stub);
|
||||||
|
|
||||||
|
scr_add_class_field_hook.create(0x567CD0, scr_add_class_field_stub);
|
||||||
|
//vm_notify_hook.create(0x569720, vm_notify_stub);
|
||||||
|
|
||||||
|
//scr_emit_function_hook.create(0x561400, scr_emit_function_stub);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(scripting::component)
|
7
src/component/scripting.hpp
Normal file
7
src/component/scripting.hpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#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, char*>> script_function_table;
|
||||||
|
}
|
12
src/dllmain.cpp
Normal file
12
src/dllmain.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||||
|
{
|
||||||
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
component_loader::post_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
6
src/game/game.cpp
Normal file
6
src/game/game.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
34
src/game/game.hpp
Normal file
34
src/game/game.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class symbol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
symbol(const size_t dedi)
|
||||||
|
: dedi_(reinterpret_cast<T*>(dedi))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return dedi_;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T* () const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return this->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* dedi_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "symbols.hpp"
|
115
src/game/scripting/entity.cpp
Normal file
115
src/game/scripting/entity.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#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};
|
||||||
|
}
|
||||||
|
|
||||||
|
return game::Scr_GetEntityIdRef(this->get_entity_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
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::AddRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void entity::release() const
|
||||||
|
{
|
||||||
|
if (this->entity_id_)
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value entity::call(const std::string& name, const std::vector<script_value>& arguments) const
|
||||||
|
{
|
||||||
|
return call_function(name, *this, arguments);
|
||||||
|
}
|
||||||
|
}
|
49
src/game/scripting/entity.hpp
Normal file
49
src/game/scripting/entity.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
script_value call(const std::string& name, const std::vector<script_value>& arguments = {}) 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;
|
||||||
|
};
|
||||||
|
}
|
278
src/game/scripting/execution.cpp
Normal file
278
src/game/scripting/execution.cpp
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "execution.hpp"
|
||||||
|
#include "safe_execution.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
#include "event.hpp"
|
||||||
|
|
||||||
|
#include "component/scripting.hpp"
|
||||||
|
#include "component/scheduler.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::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 scr_notify_id(int id, unsigned int stringValue, unsigned int paramcount)
|
||||||
|
{
|
||||||
|
if (game::scr_VmPub->outparamcount)
|
||||||
|
{
|
||||||
|
game::Scr_ClearOutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto v6 = game::scr_VmPub->top;
|
||||||
|
auto v7 = game::scr_VmPub->inparamcount - paramcount;
|
||||||
|
auto v8 = &game::scr_VmPub->top[-paramcount];
|
||||||
|
|
||||||
|
if (id)
|
||||||
|
{
|
||||||
|
const auto v9 = v8->type;
|
||||||
|
v8->type = game::scriptType_e::SCRIPT_END;
|
||||||
|
|
||||||
|
game::scr_VmPub->inparamcount = 0;
|
||||||
|
game::VM_Notify(id, stringValue, game::scr_VmPub->top);
|
||||||
|
|
||||||
|
v8->type = v9;
|
||||||
|
v6 = game::scr_VmPub->top;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; v6 != v8; game::scr_VmPub->top = v6)
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(v6->type, v6->u);
|
||||||
|
v6 = game::scr_VmPub->top - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::scr_VmPub->inparamcount = v7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_value(const script_value& value)
|
||||||
|
{
|
||||||
|
auto* value_ptr = allocate_argument();
|
||||||
|
*value_ptr = value.get_raw();
|
||||||
|
|
||||||
|
game::AddRefToValue(value_ptr->type, value_ptr->u);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
scr_notify_id(entity.get_entity_id(), event_id, game::scr_VmPub->inparamcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value call_function(const std::string& name, const entity& entity,
|
||||||
|
const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
const auto entref = entity.get_entity_reference();
|
||||||
|
|
||||||
|
const auto is_method_call = *reinterpret_cast<const int*>(&entref) != -1;
|
||||||
|
const auto function = find_function(name, !is_method_call);
|
||||||
|
if (!function)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unknown function '" + name + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
||||||
|
{
|
||||||
|
push_value(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
|
||||||
|
game::scr_VmPub->inparamcount = 0;
|
||||||
|
|
||||||
|
if (!safe_execution::call(function, entref))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error executing function '" + name + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_return_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value call_function(const std::string& name, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
return call_function(name, entity(), arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
script_value call(const std::string& name, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
return call_function(name, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
const auto id = entity.get_entity_id();
|
||||||
|
|
||||||
|
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
|
||||||
|
{
|
||||||
|
push_value(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::AddReftoObject(id);
|
||||||
|
|
||||||
|
const auto local_id = game::AllocThread(id);
|
||||||
|
const auto result = game::VM_Execute(local_id, pos, arguments.size());
|
||||||
|
|
||||||
|
const auto value = get_return_value();
|
||||||
|
|
||||||
|
game::RemoveRefToValue(game::scr_VmPub->top->type, game::scr_VmPub->top->u);
|
||||||
|
game::scr_VmPub->top->type = (game::scriptType_e)0;
|
||||||
|
|
||||||
|
--game::scr_VmPub->top;
|
||||||
|
--game::scr_VmPub->inparamcount;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value call_script_function(const entity& entity, const std::string& filename,
|
||||||
|
const std::string& function, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("File '" + filename + "' not found");
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto functions = scripting::script_function_table[filename];
|
||||||
|
|
||||||
|
if (functions.find(function) == functions.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto pos = functions.at(function);
|
||||||
|
|
||||||
|
return exec_ent_thread(entity, pos, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<unsigned int, std::unordered_map<std::string, script_value>> custom_fields;
|
||||||
|
|
||||||
|
script_value get_custom_field(const entity& entity, const std::string& field)
|
||||||
|
{
|
||||||
|
auto fields = custom_fields[entity.get_entity_id()];
|
||||||
|
const auto _field = fields.find(field);
|
||||||
|
if (_field != fields.end())
|
||||||
|
{
|
||||||
|
return _field->second;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_custom_field(const entity& entity, const std::string& field, const script_value& value)
|
||||||
|
{
|
||||||
|
const auto id = entity.get_entity_id();
|
||||||
|
|
||||||
|
if (custom_fields[id].find(field) != custom_fields[id].end())
|
||||||
|
{
|
||||||
|
custom_fields[id][field] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_fields[id].insert(std::make_pair(field, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_entity_fields(const entity& entity)
|
||||||
|
{
|
||||||
|
const auto id = entity.get_entity_id();
|
||||||
|
|
||||||
|
if (custom_fields.find(id) != custom_fields.end())
|
||||||
|
{
|
||||||
|
custom_fields[id].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_custom_fields()
|
||||||
|
{
|
||||||
|
custom_fields.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(value.type, value.u);
|
||||||
|
});
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return get_custom_field(entity, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
37
src/game/scripting/execution.hpp
Normal file
37
src/game/scripting/execution.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "entity.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
void push_value(const script_value& value);
|
||||||
|
|
||||||
|
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
|
||||||
|
script_value call_function(const std::string& name, const entity& entity,
|
||||||
|
const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
|
template <typename T = script_value>
|
||||||
|
T call(const std::string& name, const std::vector<script_value>& arguments = {});
|
||||||
|
|
||||||
|
template <>
|
||||||
|
script_value call(const std::string& name, const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T call(const std::string& name, const std::vector<script_value>& arguments)
|
||||||
|
{
|
||||||
|
return call<script_value>(name, arguments).as<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
|
||||||
|
script_value call_script_function(const entity& entity, const std::string& filename,
|
||||||
|
const std::string& function, const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
|
void clear_entity_fields(const entity& entity);
|
||||||
|
void clear_custom_fields();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
7483
src/game/scripting/function_tables.cpp
Normal file
7483
src/game/scripting/function_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
93
src/game/scripting/functions.cpp
Normal file
93
src/game/scripting/functions.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "functions.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, unsigned> lowercase_map(
|
||||||
|
const std::unordered_map<std::string, unsigned>& old_map)
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, unsigned> new_map{};
|
||||||
|
for (auto& entry : old_map)
|
||||||
|
{
|
||||||
|
new_map[utils::string::to_lower(entry.first)] = entry.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, unsigned>& get_methods()
|
||||||
|
{
|
||||||
|
static auto methods = lowercase_map(method_map);
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, unsigned>& get_functions()
|
||||||
|
{
|
||||||
|
static auto function = lowercase_map(function_map);
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_function_index(const std::string& name, const bool prefer_global)
|
||||||
|
{
|
||||||
|
const auto target = utils::string::to_lower(name);
|
||||||
|
|
||||||
|
const auto& primary_map = prefer_global
|
||||||
|
? get_functions()
|
||||||
|
: get_methods();
|
||||||
|
const auto& secondary_map = !prefer_global
|
||||||
|
? get_functions()
|
||||||
|
: get_methods();
|
||||||
|
|
||||||
|
auto function_entry = primary_map.find(target);
|
||||||
|
if (function_entry != primary_map.end())
|
||||||
|
{
|
||||||
|
return function_entry->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
function_entry = secondary_map.find(target);
|
||||||
|
if (function_entry != secondary_map.end())
|
||||||
|
{
|
||||||
|
return function_entry->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_function get_function_by_index(const unsigned index)
|
||||||
|
{
|
||||||
|
static const auto function_table = 0x1D6EB34;
|
||||||
|
static const auto method_table = 0x1D4F258;
|
||||||
|
|
||||||
|
if (index < 0x1C7)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<script_function*>(function_table)[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<script_function*>(method_table)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_token_id(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto result = token_map.find(name);
|
||||||
|
|
||||||
|
if (result != token_map.end())
|
||||||
|
{
|
||||||
|
return result->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_function find_function(const std::string& name, const bool prefer_global)
|
||||||
|
{
|
||||||
|
const auto index = find_function_index(name, prefer_global);
|
||||||
|
if (index < 0) return nullptr;
|
||||||
|
|
||||||
|
return get_function_by_index(index);
|
||||||
|
}
|
||||||
|
}
|
15
src/game/scripting/functions.hpp
Normal file
15
src/game/scripting/functions.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
extern std::unordered_map<std::string, unsigned> method_map;
|
||||||
|
extern std::unordered_map<std::string, unsigned> function_map;
|
||||||
|
extern std::unordered_map<std::string, unsigned> token_map;
|
||||||
|
extern std::unordered_map<unsigned, std::string> file_list;
|
||||||
|
|
||||||
|
using script_function = void(*)(game::scr_entref_t);
|
||||||
|
|
||||||
|
script_function find_function(const std::string& name, const bool prefer_global);
|
||||||
|
int find_token_id(const std::string& name);
|
||||||
|
}
|
74
src/game/scripting/safe_execution.cpp
Normal file
74
src/game/scripting/safe_execution.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "safe_execution.hpp"
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4611)
|
||||||
|
|
||||||
|
namespace scripting::safe_execution
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
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]))
|
||||||
|
{
|
||||||
|
*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]))
|
||||||
|
{
|
||||||
|
*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]))
|
||||||
|
{
|
||||||
|
value->type = game::SCRIPT_NONE;
|
||||||
|
value->u.intValue = 0;
|
||||||
|
*game::g_script_error_level -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _value = game::GetEntityFieldValue(entref.classnum, entref.entnum, offset);
|
||||||
|
value->type = _value.type;
|
||||||
|
value->u = _value.u;
|
||||||
|
|
||||||
|
*game::g_script_error_level -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
10
src/game/scripting/safe_execution.hpp
Normal file
10
src/game/scripting/safe_execution.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "functions.hpp"
|
||||||
|
|
||||||
|
namespace scripting::safe_execution
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
287
src/game/scripting/script_value.cpp
Normal file
287
src/game/scripting/script_value.cpp
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "script_value.hpp"
|
||||||
|
#include "entity.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);
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([&variable]()
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(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 vector& value)
|
||||||
|
{
|
||||||
|
game::VariableValue variable{};
|
||||||
|
variable.type = game::SCRIPT_VECTOR;
|
||||||
|
variable.u.vectorValue = game::Scr_AllocVector(value);
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([&variable]()
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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<std::vector<script_value>>() 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->objectVariableValue[id].w.type;
|
||||||
|
|
||||||
|
return type == game::SCRIPT_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Struct
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<std::map<std::string, script_value>>() 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->objectVariableValue[id].w.type;
|
||||||
|
|
||||||
|
return type == game::SCRIPT_STRUCT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Function
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<std::function<void()>>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().type == game::SCRIPT_FUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
65
src/game/scripting/script_value.hpp
Normal file
65
src/game/scripting/script_value.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "variable_value.hpp"
|
||||||
|
#include "vector.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class entity;
|
||||||
|
|
||||||
|
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 vector& value);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool is() const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as() const
|
||||||
|
{
|
||||||
|
if (!this->is<T>())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* as_ptr()
|
||||||
|
{
|
||||||
|
if (!this->is<int>())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<T*>(this->get<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
const game::VariableValue& get_raw() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
T get() const;
|
||||||
|
|
||||||
|
variable_value value_{};
|
||||||
|
};
|
||||||
|
}
|
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::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_;
|
||||||
|
};
|
||||||
|
}
|
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(this->value_.type, this->value_.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void variable_value::release()
|
||||||
|
{
|
||||||
|
if (this->value_.type != game::SCRIPT_NONE)
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(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 };
|
||||||
|
};
|
||||||
|
}
|
301
src/game/structs.hpp
Normal file
301
src/game/structs.hpp
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
typedef float vec_t;
|
||||||
|
typedef vec_t vec2_t[2];
|
||||||
|
typedef vec_t vec3_t[3];
|
||||||
|
typedef vec_t vec4_t[4];
|
||||||
|
|
||||||
|
struct cmd_function_t
|
||||||
|
{
|
||||||
|
cmd_function_t* next;
|
||||||
|
const char* name;
|
||||||
|
const char* autoCompleteDir;
|
||||||
|
const char* autoCompleteExt;
|
||||||
|
void(__cdecl* function)();
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct msg_t
|
||||||
|
{
|
||||||
|
int overflowed;
|
||||||
|
int readOnly;
|
||||||
|
char* data;
|
||||||
|
char* splitData;
|
||||||
|
int maxsize;
|
||||||
|
int cursize;
|
||||||
|
int splitSize;
|
||||||
|
int readcount;
|
||||||
|
int bit;
|
||||||
|
int lastEntityRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XZoneInfo
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int allocFlags;
|
||||||
|
int freeFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scr_entref_t
|
||||||
|
{
|
||||||
|
unsigned __int16 entnum;
|
||||||
|
unsigned __int16 classnum;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void(__cdecl* scr_call_t)(int entref);
|
||||||
|
|
||||||
|
enum MeansOfDeath
|
||||||
|
{
|
||||||
|
MOD_UNKNOWN = 0,
|
||||||
|
MOD_PISTOL_BULLET = 1,
|
||||||
|
MOD_RIFLE_BULLET = 2,
|
||||||
|
MOD_EXPLOSIVE_BULLET = 3,
|
||||||
|
MOD_GRENADE = 4,
|
||||||
|
MOD_GRENADE_SPLASH = 5,
|
||||||
|
MOD_PROJECTILE = 6,
|
||||||
|
MOD_PROJECTILE_SPLASH = 7,
|
||||||
|
MOD_MELEE = 8,
|
||||||
|
MOD_HEAD_SHOT = 9,
|
||||||
|
MOD_CRUSH = 10,
|
||||||
|
MOD_FALLING = 11,
|
||||||
|
MOD_SUICIDE = 12,
|
||||||
|
MOD_TRIGGER_HURT = 13,
|
||||||
|
MOD_EXPLOSIVE = 14,
|
||||||
|
MOD_IMPACT = 15,
|
||||||
|
MOD_NUM = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
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_END = 8,
|
||||||
|
SCRIPT_FUNCTION = 9,
|
||||||
|
SCRIPT_STRUCT = 19,
|
||||||
|
SCRIPT_ARRAY = 22,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VariableStackBuffer
|
||||||
|
{
|
||||||
|
const char* pos;
|
||||||
|
unsigned __int16 size;
|
||||||
|
unsigned __int16 bufLen;
|
||||||
|
unsigned __int16 localId;
|
||||||
|
char time;
|
||||||
|
char buf[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
union VariableUnion
|
||||||
|
{
|
||||||
|
int intValue;
|
||||||
|
float floatValue;
|
||||||
|
unsigned int stringValue;
|
||||||
|
const float* vectorValue;
|
||||||
|
const char* codePosValue;
|
||||||
|
unsigned int pointerValue;
|
||||||
|
VariableStackBuffer* stackValue;
|
||||||
|
unsigned int entityId;
|
||||||
|
unsigned int uintValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VariableValue
|
||||||
|
{
|
||||||
|
VariableUnion u;
|
||||||
|
scriptType_e type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct function_stack_t
|
||||||
|
{
|
||||||
|
const char* pos;
|
||||||
|
unsigned int localId;
|
||||||
|
unsigned int localVarCount;
|
||||||
|
VariableValue* top;
|
||||||
|
VariableValue* startTop;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct function_frame_t
|
||||||
|
{
|
||||||
|
function_stack_t fs;
|
||||||
|
int topType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scrVmPub_t
|
||||||
|
{
|
||||||
|
unsigned int* localVars;
|
||||||
|
VariableValue* maxstack;
|
||||||
|
int function_count;
|
||||||
|
function_frame_t* function_frame;
|
||||||
|
VariableValue* top;
|
||||||
|
/*bool debugCode;
|
||||||
|
bool abort_on_error;
|
||||||
|
bool terminal_error;
|
||||||
|
bool block_execution;*/
|
||||||
|
unsigned int inparamcount;
|
||||||
|
unsigned int outparamcount;
|
||||||
|
unsigned int breakpointOutparamcount;
|
||||||
|
bool showError;
|
||||||
|
function_frame_t function_frame_start[32];
|
||||||
|
VariableValue stack[2048];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scr_classStruct_t
|
||||||
|
{
|
||||||
|
unsigned __int16 id;
|
||||||
|
unsigned __int16 entArrayId;
|
||||||
|
char charId;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectVariableChildren
|
||||||
|
{
|
||||||
|
unsigned __int16 firstChild;
|
||||||
|
unsigned __int16 lastChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectVariableValue_u_f
|
||||||
|
{
|
||||||
|
unsigned __int16 prev;
|
||||||
|
unsigned __int16 next;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ObjectVariableValue_u_o_u
|
||||||
|
{
|
||||||
|
unsigned __int16 size;
|
||||||
|
unsigned __int16 entnum;
|
||||||
|
unsigned __int16 nextEntId;
|
||||||
|
unsigned __int16 self;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectVariableValue_u_o
|
||||||
|
{
|
||||||
|
unsigned __int16 refCount;
|
||||||
|
ObjectVariableValue_u_o_u u;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ObjectVariableValue_w
|
||||||
|
{
|
||||||
|
unsigned int type;
|
||||||
|
unsigned int classnum;
|
||||||
|
unsigned int notifyName;
|
||||||
|
unsigned int waitTime;
|
||||||
|
unsigned int parentLocalId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChildVariableValue_u_f
|
||||||
|
{
|
||||||
|
unsigned __int16 prev;
|
||||||
|
unsigned __int16 next;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ChildVariableValue_u
|
||||||
|
{
|
||||||
|
ChildVariableValue_u_f f;
|
||||||
|
VariableUnion u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChildBucketMatchKeys_keys
|
||||||
|
{
|
||||||
|
unsigned __int16 name_hi;
|
||||||
|
unsigned __int16 parentId;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ChildBucketMatchKeys
|
||||||
|
{
|
||||||
|
ChildBucketMatchKeys_keys keys;
|
||||||
|
unsigned int match;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChildVariableValue
|
||||||
|
{
|
||||||
|
ChildVariableValue_u u;
|
||||||
|
unsigned __int16 next;
|
||||||
|
char type;
|
||||||
|
char name_lo;
|
||||||
|
ChildBucketMatchKeys k;
|
||||||
|
unsigned __int16 nextSibling;
|
||||||
|
unsigned __int16 prevSibling;
|
||||||
|
};
|
||||||
|
|
||||||
|
union ObjectVariableValue_u
|
||||||
|
{
|
||||||
|
ObjectVariableValue_u_f f;
|
||||||
|
ObjectVariableValue_u_o o;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectVariableValue
|
||||||
|
{
|
||||||
|
ObjectVariableValue_u u;
|
||||||
|
ObjectVariableValue_w w;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scrVarGlob_t
|
||||||
|
{
|
||||||
|
ObjectVariableValue objectVariableValue[36864];
|
||||||
|
ObjectVariableChildren objectVariableChildren[36864];
|
||||||
|
unsigned __int16 childVariableBucket[65536];
|
||||||
|
ChildVariableValue childVariableValue[102400];
|
||||||
|
};
|
||||||
|
|
||||||
|
union DvarValue
|
||||||
|
{
|
||||||
|
bool enabled;
|
||||||
|
int integer;
|
||||||
|
unsigned int unsignedInt;
|
||||||
|
float value;
|
||||||
|
float vector[4];
|
||||||
|
const char* string;
|
||||||
|
char color[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct enum_limit
|
||||||
|
{
|
||||||
|
int stringCount;
|
||||||
|
const char** strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct int_limit
|
||||||
|
{
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct float_limit
|
||||||
|
{
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
};
|
||||||
|
|
||||||
|
union DvarLimits
|
||||||
|
{
|
||||||
|
enum_limit enumeration;
|
||||||
|
int_limit integer;
|
||||||
|
float_limit value;
|
||||||
|
float_limit vector;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dvar_t
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
unsigned int flags;
|
||||||
|
char type;
|
||||||
|
bool modified;
|
||||||
|
DvarValue current;
|
||||||
|
DvarValue latched;
|
||||||
|
DvarValue reset;
|
||||||
|
DvarLimits domain;
|
||||||
|
bool(__cdecl* domainFunc)(dvar_t*, DvarValue);
|
||||||
|
dvar_t* hashNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gentity_s
|
||||||
|
{
|
||||||
|
int entnum;
|
||||||
|
};
|
||||||
|
}
|
69
src/game/symbols.hpp
Normal file
69
src/game/symbols.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WEAK __declspec(selectany)
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x5656E0};
|
||||||
|
WEAK symbol<void(unsigned int id)> AddReftoObject{0x5655F0};
|
||||||
|
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x565580};
|
||||||
|
WEAK symbol<unsigned int()> AllocObject{0x565530};
|
||||||
|
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x565730};
|
||||||
|
|
||||||
|
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x42F760};
|
||||||
|
|
||||||
|
WEAK symbol<const char*(int index)> ConcatArgs{0x502150};
|
||||||
|
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x545680};
|
||||||
|
WEAK symbol<void(const char* cmdName, void(), cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{0x0};
|
||||||
|
WEAK symbol<const char*(int index)> Cmd_Argv{0x0};
|
||||||
|
|
||||||
|
WEAK symbol<const dvar_t*(const char*)> Dvar_FindVar{0x5BDCC0};
|
||||||
|
|
||||||
|
WEAK symbol<char*(const char*)> I_CleanStr{0x0};
|
||||||
|
|
||||||
|
WEAK symbol<VariableValue(unsigned int classnum, int entnum, int offset)> GetEntityFieldValue{0x56AF20};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x5651F0};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindObject{0x565BD0};
|
||||||
|
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> GetVariable{0x5663E0};
|
||||||
|
|
||||||
|
WEAK symbol<const float* (const float* v)> Scr_AllocVector{0x565680};
|
||||||
|
WEAK symbol<void()> Scr_ClearOutParams{0x569010};
|
||||||
|
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x565F60};
|
||||||
|
WEAK symbol<void(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x52BCC0};
|
||||||
|
WEAK symbol<void(int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x56B5E0};
|
||||||
|
WEAK symbol<int(const char* filename, unsigned int str)> Scr_GetFunctionHandle{0x5618A0};
|
||||||
|
WEAK symbol<unsigned int(int handle, unsigned int objId, unsigned int paramcount)> Scr_ExecThreadInternal{0x56E1C0};
|
||||||
|
|
||||||
|
WEAK symbol<unsigned int(const char* str, unsigned int user)> SL_GetString{0x5649E0};
|
||||||
|
WEAK symbol<const char*(unsigned int stringValue)> SL_ConvertToString{0x564270};
|
||||||
|
|
||||||
|
WEAK symbol<void(int clientNum, int type, const char* command)> SV_GameSendServerCommand{0x573220};
|
||||||
|
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x5459F0};
|
||||||
|
|
||||||
|
WEAK symbol<void(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top)> VM_Notify{0x569720};
|
||||||
|
WEAK symbol<unsigned int(unsigned int localId, const char* pos, unsigned int paramcount)> VM_Execute{0x56DFE0};
|
||||||
|
|
||||||
|
WEAK symbol<void* (jmp_buf* Buf, int Value)> longjmp{0x7363BC};
|
||||||
|
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x734CF8};
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
WEAK symbol<int> g_script_error_level{0x20B21FC};
|
||||||
|
WEAK symbol<jmp_buf> g_script_error{0x20B4218};
|
||||||
|
|
||||||
|
WEAK symbol<scrVmPub_t> scr_VmPub{0x20B4A80};
|
||||||
|
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x1E72180};
|
||||||
|
|
||||||
|
WEAK symbol<scr_classStruct_t*> g_classMap{0x8B4300};
|
||||||
|
|
||||||
|
WEAK symbol<gentity_s> g_entities{0x0};
|
||||||
|
WEAK symbol<unsigned int> levelEntityId{0x208E1A4};
|
||||||
|
|
||||||
|
namespace plutonium
|
||||||
|
{
|
||||||
|
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> function_map_rev{0x205862C0};
|
||||||
|
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> method_map_rev{0x205862E0};
|
||||||
|
}
|
||||||
|
}
|
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>
|
41
src/stdinc.hpp
Normal file
41
src/stdinc.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(disable: 4018)
|
||||||
|
#pragma warning(disable: 4146)
|
||||||
|
#pragma warning(disable: 4244)
|
||||||
|
#pragma warning(disable: 4267)
|
||||||
|
#pragma warning(disable: 26812)
|
||||||
|
|
||||||
|
#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>
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
#include <MinHook.h>
|
||||||
|
|
||||||
|
#include "utils/memory.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "utils/hook.hpp"
|
||||||
|
#include "utils/concurrent_list.hpp"
|
||||||
|
#include "utils/io.hpp"
|
||||||
|
|
||||||
|
#include "game/structs.hpp"
|
||||||
|
#include "game/game.hpp"
|
144
src/utils/concurrent_list.hpp
Normal file
144
src/utils/concurrent_list.hpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// This class is trash. Need to get rid of it.
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class concurrent_list final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class element final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit element(std::recursive_mutex* mutex, std::shared_ptr<T> entry = {},
|
||||||
|
std::shared_ptr<element> next = {}) :
|
||||||
|
mutex_(mutex),
|
||||||
|
entry_(std::move(entry)),
|
||||||
|
next_(std::move(next))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const std::shared_ptr<T>& element)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
if (!this->next_) return;
|
||||||
|
|
||||||
|
if (this->next_->entry_.get() == element.get())
|
||||||
|
{
|
||||||
|
this->next_ = this->next_->next_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->next_->remove(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<element> get_next() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
return this->next_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> operator*() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
return this->entry_;
|
||||||
|
}
|
||||||
|
|
||||||
|
element& operator++()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
*this = this->next_ ? *this->next_ : element(this->mutex_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
element operator++(int)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
auto result = *this;
|
||||||
|
this->operator++();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const element& other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
return this->entry_.get() == other.entry_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const element& other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(*this->mutex_);
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::recursive_mutex* mutex_;
|
||||||
|
std::shared_ptr<T> entry_;
|
||||||
|
std::shared_ptr<element> next_;
|
||||||
|
};
|
||||||
|
|
||||||
|
element begin()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
return this->entry_ ? *this->entry_ : this->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
element end()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
return element(&this->mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const element& entry)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
this->remove(*entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(const std::shared_ptr<T>& element)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
if (!this->entry_) return;
|
||||||
|
|
||||||
|
if ((**this->entry_).get() == element.get())
|
||||||
|
{
|
||||||
|
this->entry_ = this->entry_->get_next();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->entry_->remove(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const T& object)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
|
||||||
|
const auto object_ptr = std::make_shared<T>(object);
|
||||||
|
this->entry_ = std::make_shared<element>(&this->mutex_, object_ptr, this->entry_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(T&& object)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
|
||||||
|
const auto object_ptr = std::make_shared<T>(std::move(object));
|
||||||
|
this->entry_ = std::make_shared<element>(&this->mutex_, object_ptr, this->entry_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(this->mutex_);
|
||||||
|
this->entry_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::recursive_mutex mutex_;
|
||||||
|
std::shared_ptr<element> entry_;
|
||||||
|
};
|
||||||
|
}
|
180
src/utils/hook.cpp
Normal file
180
src/utils/hook.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include <stdinc.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();
|
||||||
|
}
|
||||||
|
} __;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
116
src/utils/hook.hpp
Normal file
116
src/utils/hook.hpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CalculateRelativeJMPAddress(X, Y) (((std::uintptr_t)Y - (std::uintptr_t)X) - 5)
|
||||||
|
|
||||||
|
namespace utils::hook
|
||||||
|
{
|
||||||
|
class detour
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
detour() = default;
|
||||||
|
detour(void* place, void* target);
|
||||||
|
detour(size_t place, void* target);
|
||||||
|
~detour();
|
||||||
|
|
||||||
|
detour(detour&& other) noexcept
|
||||||
|
{
|
||||||
|
this->operator=(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
detour& operator= (detour&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
this->~detour();
|
||||||
|
|
||||||
|
this->place_ = other.place_;
|
||||||
|
this->original_ = other.original_;
|
||||||
|
|
||||||
|
other.place_ = nullptr;
|
||||||
|
other.original_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
detour(const detour&) = delete;
|
||||||
|
detour& operator= (const detour&) = delete;
|
||||||
|
|
||||||
|
void enable() const;
|
||||||
|
void disable() const;
|
||||||
|
|
||||||
|
void create(void* place, void* target);
|
||||||
|
void create(size_t place, void* target);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->get_original());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke(Args... args)
|
||||||
|
{
|
||||||
|
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] void* get_original() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* place_{};
|
||||||
|
void* original_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
void nop(void* place, size_t length);
|
||||||
|
void nop(size_t place, size_t length);
|
||||||
|
|
||||||
|
void copy(void* place, const void* data, size_t length);
|
||||||
|
void copy(size_t place, const void* data, size_t length);
|
||||||
|
|
||||||
|
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||||
|
|
||||||
|
void call(void* pointer, void* data);
|
||||||
|
void call(size_t pointer, void* data);
|
||||||
|
void call(size_t pointer, size_t data);
|
||||||
|
|
||||||
|
void jump(std::uintptr_t address, void* destination);
|
||||||
|
|
||||||
|
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...);
|
||||||
|
}
|
||||||
|
}
|
112
src/utils/io.cpp
Normal file
112
src/utils/io.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace utils::io
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
19
src/utils/io.hpp
Normal file
19
src/utils/io.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace utils::io
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
112
src/utils/memory.cpp
Normal file
112
src/utils/memory.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// https://github.com/momo5502/open-iw5
|
||||||
|
|
||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
memory::allocator memory::mem_allocator_;
|
||||||
|
|
||||||
|
memory::allocator::~allocator()
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::clear()
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
for (auto& data : this->pool_)
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pool_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(void* data)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||||
|
if (j != this->pool_.end())
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
this->pool_.erase(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(const void* data)
|
||||||
|
{
|
||||||
|
this->free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memory::allocator::allocate(const size_t length)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::allocate(length);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::allocator::empty() const
|
||||||
|
{
|
||||||
|
return this->pool_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*char* memory::allocator::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::duplicate_string(string);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
void* memory::allocate(const size_t length)
|
||||||
|
{
|
||||||
|
const auto data = calloc(length, 1);
|
||||||
|
assert(data != nullptr);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*char* memory::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||||
|
std::memcpy(new_string, string.data(), string.size());
|
||||||
|
return new_string;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
void memory::free(void* data)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
::free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::free(const void* data)
|
||||||
|
{
|
||||||
|
free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_set(const void* mem, const char chr, const size_t length)
|
||||||
|
{
|
||||||
|
const auto mem_arr = static_cast<const char*>(mem);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (mem_arr[i] != chr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory::allocator* memory::get_allocator()
|
||||||
|
{
|
||||||
|
return &memory::mem_allocator_;
|
||||||
|
}
|
||||||
|
}
|
70
src/utils/memory.hpp
Normal file
70
src/utils/memory.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// https://github.com/momo5502/open-iw5
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
class memory final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class allocator final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~allocator();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void free(void* data);
|
||||||
|
|
||||||
|
void free(const void* data);
|
||||||
|
|
||||||
|
void* allocate(size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocate()
|
||||||
|
{
|
||||||
|
return this->allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
//char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::vector<void*> pool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* allocate(size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate()
|
||||||
|
{
|
||||||
|
return allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//static char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
static void free(void* data);
|
||||||
|
static void free(const void* data);
|
||||||
|
|
||||||
|
static bool is_set(const void* mem, char chr, size_t length);
|
||||||
|
|
||||||
|
static allocator* get_allocator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static allocator mem_allocator_;
|
||||||
|
};
|
||||||
|
}
|
151
src/utils/string.cpp
Normal file
151
src/utils/string.cpp
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
|
||||||
|
namespace utils::string
|
||||||
|
{
|
||||||
|
const char* va(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static thread_local va_provider<8, 256> provider;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
const char* result = provider.get(fmt, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string& s, const char delim)
|
||||||
|
{
|
||||||
|
std::stringstream ss(s);
|
||||||
|
std::string item;
|
||||||
|
std::vector<std::string> elems;
|
||||||
|
|
||||||
|
while (std::getline(ss, item, delim))
|
||||||
|
{
|
||||||
|
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
|
||||||
|
}
|
||||||
|
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_lower(std::string text)
|
||||||
|
{
|
||||||
|
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
|
||||||
|
{
|
||||||
|
return 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
98
src/utils/string.hpp
Normal file
98
src/utils/string.hpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
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