mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-04-20 21:45:43 +00:00
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bf1632f4ec | ||
|
4bd1ff9a66 | ||
|
9b8680b7bd | ||
|
646b5a3264 | ||
|
3e9fcc5fda | ||
|
83ffa1c324 | ||
|
fd80b4c55a | ||
|
348bf99bd6 | ||
|
0c128ca259 | ||
|
232ce3bcaa | ||
|
483c7126c8 | ||
|
0cd113b33f | ||
|
4c77045add | ||
|
b0ccd678d3 | ||
|
f4ac726a1e | ||
|
3a4595a641 | ||
|
a3a7b8847c | ||
|
7fccea636f | ||
|
38a860e4ce | ||
|
3995bed200 | ||
|
3502a70933 | ||
|
1a33de617d | ||
|
59e57d05f7 | ||
|
23f5c28c29 | ||
|
0e29d35407 | ||
|
a756ab6cc0 | ||
|
c15f3c21d5 | ||
|
5a944f8711 | ||
|
e0728eb375 | ||
|
bafc637be8 | ||
|
7da012fdf3 | ||
|
d9c3b7cab5 | ||
|
8a36e02fdd | ||
|
944bab5e5c | ||
|
8ca8002067 | ||
|
afc01a05e3 | ||
|
8cb282da97 | ||
|
0327434187 | ||
|
b3b693a56e | ||
|
347033f424 | ||
|
4c78d6fe41 | ||
|
33b6006a34 | ||
|
98b2c3f4d6 | ||
|
beb90edc3d | ||
|
df9ef00a64 | ||
|
1f0717edef | ||
|
7a82be782d | ||
|
f0c95340bf | ||
|
4843c61e2e | ||
|
b79b776f63 | ||
|
d1a0277861 | ||
|
4963a9180a | ||
|
56e8c485af | ||
|
9648a38fbe | ||
|
3fdcf3261d | ||
|
deed3b66f5 | ||
|
fce9fcee51 | ||
|
cfd4640035 | ||
|
827d7c955e | ||
|
c5eaf6f961 | ||
|
eec84e91ff | ||
|
3f406c26e7 | ||
|
18659448d7 | ||
|
3bff917ce3 | ||
|
d6a6a096fd | ||
|
c147f3fbe2 | ||
|
8ea93169da | ||
|
7da463ccd3 | ||
|
7503e8988b |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -29,3 +29,6 @@
|
|||||||
[submodule "deps/SQLiteCpp"]
|
[submodule "deps/SQLiteCpp"]
|
||||||
path = deps/SQLiteCpp
|
path = deps/SQLiteCpp
|
||||||
url = https://github.com/SRombauts/SQLiteCpp
|
url = https://github.com/SRombauts/SQLiteCpp
|
||||||
|
[submodule "deps/plutonium-sdk"]
|
||||||
|
path = deps/plutonium-sdk
|
||||||
|
url = https://github.com/plutoniummod/plutonium-sdk.git
|
||||||
|
109
README.md
109
README.md
@ -3,11 +3,112 @@ A plugin that has code that hopefully compiles and the game will load it to do t
|
|||||||
|
|
||||||
Requires Git (https://git-scm.com/), Premake5 (https://premake.github.io/), and MSVC 2022 (https://visualstudio.microsoft.com/vs/features/cplusplus/) to build.
|
Requires Git (https://git-scm.com/), Premake5 (https://premake.github.io/), and MSVC 2022 (https://visualstudio.microsoft.com/vs/features/cplusplus/) to build.
|
||||||
|
|
||||||
# What does it do?
|
|
||||||
Nothing really right now, just detours and reimplements the entire GSC VM, for research purposes.
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
Move the `t4sp-server-plugin.dll` to `%LOCALAPPDATA%\Plutonium\storage\t4\plugins\`, the plugin will be loaded when you start up a dedicated server for Plutonium T4SP.
|
Move the `t4sp-server-plugin.dll` to `%LOCALAPPDATA%\Plutonium\plugins\`, the plugin will be loaded when you start up a dedicated server for Plutonium T4SP.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
Detours and reimplements the entire GSC VM + compiler.
|
||||||
|
|
||||||
|
Adds custom GSC functions.
|
||||||
|
|
||||||
|
## FileIO
|
||||||
|
This plugin provides FileIO interface to GSC for reading and writing files, this is exact to [CoD4x's](https://github.com/callofduty4x/CoD4x_Server/blob/master/scriptdocumentation/script_functions_reference.md#file-operations) interface.
|
||||||
|
|
||||||
|
However, all reads and writes will take place strictly and only in the `scriptdata` folder, no up directory traversal allowed.
|
||||||
|
|
||||||
|
All files will be closed upon GSC restart (map_restart or fast_restart or missionfailed, etc), only a maximum of 10 files may be opened at once.
|
||||||
|
|
||||||
|
* `<bool> FS_TestFile(<filename string>)` Returns `true` if the file exists, `false` otherwise.
|
||||||
|
* `<bool> FS_Remove(<filename string>, <(optional) use_global bool>)` Deletes the file, return `true` if successful, `false` otherwise. `use_global` will use non mod specific folder.
|
||||||
|
```gsc
|
||||||
|
// test to see if "scriptdata/test.txt" file exists
|
||||||
|
if (FS_TestFile("test.txt")) // not a typo, all file io will take place inside the "scriptdata" folder
|
||||||
|
{
|
||||||
|
PrintConsole("Found test.txt!");
|
||||||
|
|
||||||
|
// delete it!
|
||||||
|
if (FS_Remove("test.txt"))
|
||||||
|
{
|
||||||
|
PrintConsole("test.txt was deleted!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `FS_FCloseAll()` Closes every full file.
|
||||||
|
```gsc
|
||||||
|
// open some files
|
||||||
|
|
||||||
|
FS_FCloseAll(); // close them all
|
||||||
|
```
|
||||||
|
|
||||||
|
* `<int> FS_FOpen(<filename string>, <mode string>, <(optional) use_global bool>)` Tries to open the file, mode must be one of `read`, `write` (clears the file), `append` (appends to the file), returns the filehandle. Will return `0` if failed to open. `use_global` will use non mod specific folder (only applies to `write` mode).
|
||||||
|
* `FS_FClose(<filehandle int>)` Closes the file pointed by the filehandle given, which was returned from `FS_FOpen`.
|
||||||
|
```gsc
|
||||||
|
// opens "scriptdata/test.txt", all io will take place inside the "scriptdata" folder
|
||||||
|
f = FS_FOpen("test.txt", "read"); // can be "read" "write", or "append"
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
PrintConsole("test.txt failed to be opened for reading!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do stuff with the file
|
||||||
|
|
||||||
|
FS_FClose(f); // make sure to close it
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
* `<string> FS_ReadLine(<filehandle int>)` Reads a line from the file pointed by the filehandle, removes the newline char. Returns `undefined` when nothing is left to read. Will not read more than 65536 characters at once. Filehandle must be opened for reading.
|
||||||
|
* `<string> FS_Read(<filehandle int>, <bytes int>(optional))` Reads number of bytes from the file. If bytes is `undefined`, reads the entire file. No more than 65536 characters will be read at once. Returns `undefined` if there are nothing left to read.
|
||||||
|
```gsc
|
||||||
|
// open the file for reading
|
||||||
|
|
||||||
|
line = FS_ReadLine(f);
|
||||||
|
while (isDefined(line))
|
||||||
|
{
|
||||||
|
// do something with line
|
||||||
|
|
||||||
|
line = FS_ReadLine(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// entire file is read
|
||||||
|
|
||||||
|
// close the file
|
||||||
|
```
|
||||||
|
|
||||||
|
* `<bool> FS_WriteLine(<filehandle int>, <contents string>)` Writes to the file pointed by the filehandle. Appends a newline character. Returns `true` if successful, `false` otherwise. Filehandle must be opened for writing.
|
||||||
|
* `<bool> FS_Write(<filehandle int>, <contents string>)` Same as above, does not add a newline character.
|
||||||
|
```gsc
|
||||||
|
// open the file for writing
|
||||||
|
|
||||||
|
FS_WriteLine(f, "writing some text with newline added");
|
||||||
|
|
||||||
|
FS_Write(f, "no newline here");
|
||||||
|
FS_Write(f, "i manually add a newline\n");
|
||||||
|
|
||||||
|
// close the file
|
||||||
|
```
|
||||||
|
|
||||||
|
* `<array of strings> FS_ListFiles(<folder string>)` Returns a list of files inside of the folder given.
|
||||||
|
```gsc
|
||||||
|
folder = "testfolder/";
|
||||||
|
files = FS_ListFiles(folder);
|
||||||
|
|
||||||
|
for (i = 0; i < files.size; i++)
|
||||||
|
{
|
||||||
|
filename = files[i];
|
||||||
|
|
||||||
|
// do something with the filename
|
||||||
|
filepath = folder + filename;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `<int> FS_Length(<filehandle int>)` Returns the length in bytes of the open'd file.
|
||||||
|
* `<int> FS_GetSeek(<filehandle int>)` Returns the seek of the open'd file (only for reading).
|
||||||
|
* `<int> FS_Seek(<filehandle int>, <seek int>)` Sets the seek of the open'd file (only for reading).
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
- momo5502 (https://github.com/momo5502)
|
- momo5502 (https://github.com/momo5502)
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 43d60c5e3891dab6491a76d0bac554a4a89d57f6
|
Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d
|
2
deps/asmjit
vendored
2
deps/asmjit
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f1ea8a46c3f2bce5c51f290050c9d194668206bf
|
Subproject commit 416f7356967c1f66784dc1580fe157f9406d8bff
|
2
deps/curl
vendored
2
deps/curl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 4528690cd51e5445df74aef8f83470a602683797
|
Subproject commit 78a1814b331c3535a785aa1c501203ad5a947838
|
2
deps/json
vendored
2
deps/json
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 546370c9e778d99e7176641123e5cc1d0b62acab
|
Subproject commit a259ecc51e1951e12f757ce17db958e9881e9c6c
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit b96e96cf8b22a931e8e91098ac37bc72f9e2f033
|
Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 7f96509df1a6b44867bbda56bbf2cb92524be8ef
|
Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091
|
2
deps/minhook
vendored
2
deps/minhook
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 49d03ad118cf7f6768c79a8f187e14b8f2a07f94
|
Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636
|
1
deps/plutonium-sdk
vendored
Submodule
1
deps/plutonium-sdk
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 17e9a0a4d5e1133b50f879e3db07e97d1bf92e10
|
18
deps/premake/plutonium-sdk.lua
vendored
Normal file
18
deps/premake/plutonium-sdk.lua
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
plutonium_sdk = {
|
||||||
|
source = path.join(dependencies.basePath, "plutonium-sdk"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function plutonium_sdk.import()
|
||||||
|
plutonium_sdk.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function plutonium_sdk.includes()
|
||||||
|
includedirs {
|
||||||
|
plutonium_sdk.source,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function plutonium_sdk.project()
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, plutonium_sdk)
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc
|
Subproject commit 643e17b7498d12ab8d15565662880579692f769d
|
@ -108,13 +108,13 @@ workspace "t4sp-server-plugin"
|
|||||||
else
|
else
|
||||||
filter "configurations:Release"
|
filter "configurations:Release"
|
||||||
postbuildcommands {
|
postbuildcommands {
|
||||||
"if \"%COMPUTERNAME%\" == \"NEW-BUILT\" ( copy /y \"$(TargetPath)\" \"$(CODWAW_PATH)\\t4\\plugins\\\" )"
|
"if \"%COMPUTERNAME%\" == \"\" ( copy /y \"$(TargetPath)\" \"$(LOCALAPPDATA)\\Plutonium\\plugins\\\" )"
|
||||||
}
|
}
|
||||||
filter {}
|
filter {}
|
||||||
|
|
||||||
filter "configurations:Debug"
|
filter "configurations:Debug"
|
||||||
postbuildcommands {
|
postbuildcommands {
|
||||||
"if \"%COMPUTERNAME%\" == \"NEW-BUILT\" ( copy /y \"$(TargetPath)\" \"$(CODWAW_PATH)\\t4staging\\plugins\\\" )"
|
"if \"%COMPUTERNAME%\" == \"\" ( copy /y \"$(TargetPath)\" \"$(LOCALAPPDATA)\\Plutonium-staging\\plugins\\\" )"
|
||||||
}
|
}
|
||||||
filter {}
|
filter {}
|
||||||
end
|
end
|
||||||
|
@ -2011,14 +2011,6 @@ LABEL_17:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// our addition
|
|
||||||
auto f = gsc::function::get(pName, type);
|
|
||||||
if (f != nullptr)
|
|
||||||
{
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
|
|
||||||
// pluto
|
// pluto
|
||||||
if (game::plutonium::scr_get_function_hook != nullptr)
|
if (game::plutonium::scr_get_function_hook != nullptr)
|
||||||
{
|
{
|
||||||
@ -2154,14 +2146,6 @@ LABEL_17:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// our addition
|
|
||||||
auto f = gsc::method::get(pName, type);
|
|
||||||
if (f != nullptr)
|
|
||||||
{
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
|
|
||||||
// pluto
|
// pluto
|
||||||
if (game::plutonium::scr_get_method_hook != nullptr)
|
if (game::plutonium::scr_get_method_hook != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -304,7 +304,6 @@ namespace codsrc
|
|||||||
{
|
{
|
||||||
game::plutonium::script_preprocess(sourceBuffer, inst, &parseData); // the pluto hook will call ScriptParse, so we dont have to
|
game::plutonium::script_preprocess(sourceBuffer, inst, &parseData); // the pluto hook will call ScriptParse, so we dont have to
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -723,19 +723,20 @@ namespace codsrc
|
|||||||
{
|
{
|
||||||
if (game::Scr_IsInOpcodeMemory(scriptInstance, codepos - 1))
|
if (game::Scr_IsInOpcodeMemory(scriptInstance, codepos - 1))
|
||||||
{
|
{
|
||||||
// pluto
|
|
||||||
const char* s;
|
const char* s;
|
||||||
|
|
||||||
|
// pluto
|
||||||
if (game::plutonium::at_codepose_va != nullptr)
|
if (game::plutonium::at_codepose_va != nullptr)
|
||||||
{
|
{
|
||||||
s = game::plutonium::at_codepose_va(scriptInstance, codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
s = game::plutonium::at_codepose_va(scriptInstance, codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
||||||
}
|
}
|
||||||
|
//
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s = game::va("@ %d\n", codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
s = game::va("@ %d\n", codepos - game::gScrVarPub[scriptInstance].programBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
game::Com_PrintMessage(channel, s, 0);
|
game::Com_PrintMessage(channel, s, 0);
|
||||||
//
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,11 +67,18 @@ namespace codsrc
|
|||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
for ( len = refString->u.s.byteLen - 1;
|
if (!refString->u.s.byteLen)
|
||||||
refString->str[len];
|
|
||||||
len += 256 )
|
|
||||||
{
|
{
|
||||||
;
|
len = 256 - 1; //Bugfix for 256 % 256 = 0 or 512 % 256 = 0 or... Just promote it to 256
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
len = refString->u.s.byteLen - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (refString->str[len])
|
||||||
|
{
|
||||||
|
len += 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
@ -4702,10 +4702,6 @@ namespace codsrc
|
|||||||
// Decomp Status: Tested, Completed
|
// Decomp Status: Tested, Completed
|
||||||
void Scr_InitSystem(game::scriptInstance_t inst)
|
void Scr_InitSystem(game::scriptInstance_t inst)
|
||||||
{
|
{
|
||||||
// our additions
|
|
||||||
scheduler::exec_pre_scr_init_funcs(inst);
|
|
||||||
//
|
|
||||||
|
|
||||||
assert(!game::gScrVarPub[inst].timeArrayId);
|
assert(!game::gScrVarPub[inst].timeArrayId);
|
||||||
|
|
||||||
//assert(!game::gScrVarPub[inst].ext_threadcount);
|
//assert(!game::gScrVarPub[inst].ext_threadcount);
|
||||||
@ -4731,10 +4727,6 @@ namespace codsrc
|
|||||||
assert(!game::gScrVarPub[inst].freeEntList);
|
assert(!game::gScrVarPub[inst].freeEntList);
|
||||||
|
|
||||||
game::g_script_error_level[inst] = -1;
|
game::g_script_error_level[inst] = -1;
|
||||||
|
|
||||||
// our additions
|
|
||||||
scheduler::exec_post_scr_init_funcs(inst);
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Restored function
|
//Restored function
|
||||||
@ -4890,20 +4882,24 @@ namespace codsrc
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.u.linkPointer = (const char*)value->u.intValue;
|
result = value->u.anim;
|
||||||
if ( !animTreeInputForValidation )
|
if ( !animTreeInputForValidation )
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
animTreeAnimPtr = animTreeInputForValidation->anims;
|
assert(animTreeInputForValidation);
|
||||||
animPtr = game::gScrAnimPub[inst].xanim_lookup[user][(unsigned int)result.u.linkPointer >> 16].anims;
|
animTreeAnimPtr = animTreeInputForValidation->anims; // XAnimGetAnims(inst, animTreeInputForValidation);
|
||||||
|
|
||||||
|
assert(result.u.s.tree > 0 && result.u.s.tree < game::gScrAnimPub[inst].xanim_num[user]);
|
||||||
|
animPtr = game::gScrAnimPub[inst].xanim_lookup[user][result.u.s.tree].anims; // Scr_GetAnims(inst, user, result.u.s.tree);
|
||||||
|
|
||||||
if ( animPtr == animTreeAnimPtr )
|
if ( animPtr == animTreeAnimPtr )
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugMsg = game::XAnimGetAnimDebugName(value->u.intValue, animPtr);
|
debugMsg = game::XAnimGetAnimDebugName(result.u.s.index, animPtr);
|
||||||
game::gScrVarPub[inst].error_message = (char*)game::va("anim '%s' in animtree '%s' does not belong to the entity's animtree '%s'", debugMsg, animTreeAnimPtr->debugName, animTreeAnimPtr->debugName);
|
game::gScrVarPub[inst].error_message = (char*)game::va("anim '%s' in animtree '%s' does not belong to the entity's animtree '%s'", debugMsg, animTreeAnimPtr->debugName, animTreeAnimPtr->debugName);
|
||||||
game::RemoveRefToValueInternal(game::SCRIPTINSTANCE_SERVER, value->type, value->u);
|
game::RemoveRefToValueInternal(game::SCRIPTINSTANCE_SERVER, value->type, value->u);
|
||||||
value->type = game::VAR_UNDEFINED;
|
value->type = game::VAR_UNDEFINED;
|
||||||
|
467
src/component/fileio.cpp
Normal file
467
src/component/fileio.cpp
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "gsc.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include <utils/memory.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace fileio
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static constexpr size_t max_fhs = 10;
|
||||||
|
static constexpr size_t max_gsc_string = 0x10000 - 1;
|
||||||
|
|
||||||
|
enum class scr_fh_type_e
|
||||||
|
{
|
||||||
|
UNUSED,
|
||||||
|
READ,
|
||||||
|
WRITE,
|
||||||
|
APPEND
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scr_fh_t
|
||||||
|
{
|
||||||
|
scr_fh_type_e type;
|
||||||
|
std::unique_ptr<char[]> file_buff;
|
||||||
|
int file_length;
|
||||||
|
int seek;
|
||||||
|
std::filesystem::path full_path;
|
||||||
|
std::string base_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<scr_fh_t, max_fhs> scr_fhs = {};
|
||||||
|
|
||||||
|
bool validate_scr_path(const std::string& fpath)
|
||||||
|
{
|
||||||
|
auto toks = utils::string::split(fpath, '/');
|
||||||
|
|
||||||
|
for (const auto& tok : toks)
|
||||||
|
{
|
||||||
|
if (tok == "." || tok == "..")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.find(":") != std::string::npos)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_base_path(const std::string& path_)
|
||||||
|
{
|
||||||
|
auto path = path_;
|
||||||
|
std::replace(path.begin(), path.end(), '\\', '/');
|
||||||
|
|
||||||
|
if (!validate_scr_path(path))
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, utils::string::va("Invalid path: %s", path_.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// its sandboxed, but what about symlinks?
|
||||||
|
return path.starts_with("scriptdata/") ? path : "scriptdata/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path build_full_path(const std::string& path, bool use_global)
|
||||||
|
{
|
||||||
|
static game::dvar_s* fs_localAppData = nullptr;
|
||||||
|
static game::dvar_s* fs_gamedir = nullptr;
|
||||||
|
|
||||||
|
if (!fs_localAppData)
|
||||||
|
{
|
||||||
|
fs_localAppData = game::Dvar_FindVar("fs_localAppData");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs_gamedir)
|
||||||
|
{
|
||||||
|
fs_gamedir = game::Dvar_FindVar("fs_game");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::filesystem::path(fs_localAppData->current.string) / (!use_global && *fs_gamedir->current.string ? fs_gamedir->current.string : "raw") / path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_scr_fh(scr_fh_t& scr_fh)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
plugin::get()->get_interface()->logging()->info(utils::string::va("free_scr_fh: closing %s\n", scr_fh.base_path.c_str()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scr_fh = {};
|
||||||
|
scr_fh.type = scr_fh_type_e::UNUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_all_scr_fh()
|
||||||
|
{
|
||||||
|
for (auto& fh : scr_fhs)
|
||||||
|
{
|
||||||
|
if (fh.type == scr_fh_type_e::UNUSED)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_scr_fh(fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int scr_get_fh()
|
||||||
|
{
|
||||||
|
auto fh = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0) - 1;
|
||||||
|
|
||||||
|
if (fh < 0 || fh >= max_fhs)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "fs_fwrite: invalid filehandle");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fwrite_to_file(bool append_newline)
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type != scr_fh_type_e::WRITE && scr_fhs[fh].type != scr_fh_type_e::APPEND)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened for writing");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_write = game::Scr_GetString(1, game::SCRIPTINSTANCE_SERVER);
|
||||||
|
if (append_newline)
|
||||||
|
{
|
||||||
|
to_write += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!utils::io::write_file(scr_fhs[fh].full_path.string(), to_write, true))
|
||||||
|
{
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Failed to write file: %s\n", scr_fhs[fh].base_path.c_str());
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_file_io()
|
||||||
|
{
|
||||||
|
scheduler::on_scr_execute([]()
|
||||||
|
{
|
||||||
|
close_all_scr_fh();
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_testfile", []()
|
||||||
|
{
|
||||||
|
auto fpath = build_base_path(game::Scr_GetString(0, game::SCRIPTINSTANCE_SERVER));
|
||||||
|
|
||||||
|
auto fd = 0;
|
||||||
|
auto file_length = game::FS_FOpenFileRead(fpath.c_str(), &fd);
|
||||||
|
|
||||||
|
if (!fd || file_length < 0)
|
||||||
|
{
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::FS_FCloseFile(fd);
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_fopen", []()
|
||||||
|
{
|
||||||
|
auto fpath = build_base_path(game::Scr_GetString(0, game::SCRIPTINSTANCE_SERVER));
|
||||||
|
|
||||||
|
// check for dupes
|
||||||
|
for (const auto& scr_fd : scr_fhs)
|
||||||
|
{
|
||||||
|
if (scr_fd.type != scr_fh_type_e::UNUSED && scr_fd.base_path == fpath)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File already opened");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for avail slot
|
||||||
|
auto i = 0;
|
||||||
|
for (; i < max_fhs; i++)
|
||||||
|
{
|
||||||
|
if (scr_fhs[i].type == scr_fh_type_e::UNUSED)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= max_fhs)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "Too many files opened");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check mode
|
||||||
|
auto mode = game::Scr_GetString(1, game::SCRIPTINSTANCE_SERVER);
|
||||||
|
if (mode == "read"s)
|
||||||
|
{
|
||||||
|
auto fd = 0;
|
||||||
|
auto file_length = game::FS_FOpenFileRead(fpath.c_str(), &fd);
|
||||||
|
|
||||||
|
if (!fd || file_length < 0)
|
||||||
|
{
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Failed to open file for reading: %s\n", fpath.c_str());
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_fhs[i].file_buff = std::make_unique<char[]>(file_length + 1);
|
||||||
|
auto bytes_read = game::FS_Read(scr_fhs[i].file_buff.get(), file_length, fd);
|
||||||
|
scr_fhs[i].file_buff[file_length] = '\0';
|
||||||
|
game::FS_FCloseFile(fd);
|
||||||
|
|
||||||
|
assert(bytes_read == file_length);
|
||||||
|
|
||||||
|
if (bytes_read < 0)
|
||||||
|
{
|
||||||
|
scr_fhs[i].file_buff = {};
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Failed to read file: %s\n", fpath.c_str());
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_fhs[i].type = scr_fh_type_e::READ;
|
||||||
|
scr_fhs[i].file_length = bytes_read;
|
||||||
|
scr_fhs[i].seek = 0;
|
||||||
|
scr_fhs[i].base_path = fpath;
|
||||||
|
}
|
||||||
|
else if (mode == "write"s || mode == "append"s)
|
||||||
|
{
|
||||||
|
auto full_path = build_full_path(fpath, game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 3 && game::Scr_GetType(game::SCRIPTINSTANCE_SERVER, 2) == game::VAR_INTEGER && game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 2));
|
||||||
|
|
||||||
|
if (!utils::io::write_file(full_path.string(), "", (mode == "append"s)))
|
||||||
|
{
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Failed to open file for writing: %s\n", fpath.c_str());
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_fhs[i].type = scr_fh_type_e::WRITE;
|
||||||
|
scr_fhs[i].base_path = fpath;
|
||||||
|
scr_fhs[i].full_path = full_path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(1, game::SCRIPTINSTANCE_SERVER, utils::string::va("Invalid mode: %s", mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
plugin::get()->get_interface()->logging()->info(utils::string::va("gscr_fs_fopen: opening %s, mode %s\n", fpath.c_str(), mode));
|
||||||
|
#endif
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, i + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_write", []()
|
||||||
|
{
|
||||||
|
fwrite_to_file(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_writeline", []()
|
||||||
|
{
|
||||||
|
fwrite_to_file(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_readline", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened for reading");
|
||||||
|
}
|
||||||
|
|
||||||
|
// file is completed being read
|
||||||
|
if (scr_fhs[fh].seek >= scr_fhs[fh].file_length)
|
||||||
|
{
|
||||||
|
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// count how many bytes until the newline
|
||||||
|
auto bytes_to_read = 0;
|
||||||
|
auto found_nl = false;
|
||||||
|
|
||||||
|
for (auto i = scr_fhs[fh].seek; i < scr_fhs[fh].file_length; bytes_to_read++, i++)
|
||||||
|
{
|
||||||
|
if (scr_fhs[fh].file_buff[i] == '\n')
|
||||||
|
{
|
||||||
|
bytes_to_read++;
|
||||||
|
found_nl = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_to_read > max_gsc_string)
|
||||||
|
{
|
||||||
|
found_nl = false;
|
||||||
|
bytes_to_read = max_gsc_string;
|
||||||
|
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Line was too long in file %s, truncating\n", scr_fhs[fh].base_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scr_str = std::string(&scr_fhs[fh].file_buff[scr_fhs[fh].seek], bytes_to_read);
|
||||||
|
scr_fhs[fh].seek += bytes_to_read;
|
||||||
|
|
||||||
|
// remove all '\r'
|
||||||
|
scr_str.erase(std::remove(scr_str.begin(), scr_str.end(), '\r'), scr_str.end());
|
||||||
|
|
||||||
|
// chop the newline char off
|
||||||
|
if (found_nl)
|
||||||
|
{
|
||||||
|
scr_str.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, scr_str.c_str());
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_read", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened for reading");
|
||||||
|
}
|
||||||
|
|
||||||
|
// file is completed being read
|
||||||
|
if (scr_fhs[fh].seek >= scr_fhs[fh].file_length)
|
||||||
|
{
|
||||||
|
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bytes_to_read = scr_fhs[fh].file_length - scr_fhs[fh].seek;
|
||||||
|
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 2)
|
||||||
|
{
|
||||||
|
bytes_to_read = std::clamp(game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 1), 0, bytes_to_read);
|
||||||
|
|
||||||
|
if (bytes_to_read <= 0)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(1, game::SCRIPTINSTANCE_SERVER, "Trying to read <1 bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_to_read > max_gsc_string)
|
||||||
|
{
|
||||||
|
bytes_to_read = max_gsc_string;
|
||||||
|
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Line was too long in file %s, truncating\n", scr_fhs[fh].base_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scr_str = std::string(&scr_fhs[fh].file_buff[scr_fhs[fh].seek], bytes_to_read);
|
||||||
|
scr_fhs[fh].seek += bytes_to_read;
|
||||||
|
|
||||||
|
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, scr_str.c_str());
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_fcloseall", []()
|
||||||
|
{
|
||||||
|
close_all_scr_fh();
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_fclose", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type == scr_fh_type_e::UNUSED)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened");
|
||||||
|
}
|
||||||
|
|
||||||
|
free_scr_fh(scr_fhs[fh]);
|
||||||
|
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_length", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type == scr_fh_type_e::UNUSED)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened");
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, scr_fhs[fh].file_length);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_getseek", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
// write seek would require completely redoing how we write files...
|
||||||
|
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened for reading");
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, scr_fhs[fh].seek);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_seek", []()
|
||||||
|
{
|
||||||
|
auto fh = scr_get_fh();
|
||||||
|
|
||||||
|
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||||
|
{
|
||||||
|
game::Scr_ParamError(0, game::SCRIPTINSTANCE_SERVER, "File not opened for reading");
|
||||||
|
}
|
||||||
|
|
||||||
|
scr_fhs[fh].seek = std::clamp(game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 1), 0, scr_fhs[fh].file_length);
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_remove", []()
|
||||||
|
{
|
||||||
|
auto fpath = build_base_path(game::Scr_GetString(0, game::SCRIPTINSTANCE_SERVER));
|
||||||
|
auto full_path = build_full_path(fpath, game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 2 && game::Scr_GetType(game::SCRIPTINSTANCE_SERVER, 1) == game::VAR_INTEGER && game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 1));
|
||||||
|
|
||||||
|
if (!utils::io::remove_file(full_path.string()))
|
||||||
|
{
|
||||||
|
game::Com_PrintWarning(game::CON_CHANNEL_SCRIPT, "Failed to delete file: %s\n", fpath.c_str());
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("fs_listfiles", []()
|
||||||
|
{
|
||||||
|
auto fpath = build_base_path(game::Scr_GetString(0, game::SCRIPTINSTANCE_SERVER));
|
||||||
|
|
||||||
|
int numfiles;
|
||||||
|
auto* files = game::FS_ListFiles(fpath.c_str(), "", game::FS_LIST_ALL, &numfiles);
|
||||||
|
|
||||||
|
game::Scr_MakeArray(game::SCRIPTINSTANCE_SERVER);
|
||||||
|
for (int i = 0; i < numfiles; i++)
|
||||||
|
{
|
||||||
|
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, files[i]);
|
||||||
|
game::Scr_AddArray(game::SCRIPTINSTANCE_SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::FS_FreeFileList(files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
add_file_io();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(fileio::component)
|
||||||
|
|
@ -1,125 +1,14 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
#include "gsc.hpp"
|
#include "gsc.hpp"
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
|
|
||||||
#include <json.hpp>
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, game::BuiltinFunction> functions;
|
|
||||||
std::unordered_map<std::string, game::BuiltinMethod> methods;
|
|
||||||
|
|
||||||
utils::hook::detour scr_getmethod_hook;
|
|
||||||
void* scr_getfunction_stub_ret_loc;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
game::BuiltinFunction scr_getfunction_call(const char** pName, int* pType)
|
|
||||||
{
|
|
||||||
auto itr = functions.find(*pName);
|
|
||||||
|
|
||||||
if (itr == functions.end())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pType = 0;
|
|
||||||
return itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::BuiltinFunction NAKED scr_getfunction_stub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push eax;
|
|
||||||
pushad;
|
|
||||||
|
|
||||||
lea eax, [esp + 0x24 + 0x2C - 0x1C];
|
|
||||||
push eax;
|
|
||||||
push edx;
|
|
||||||
call scr_getfunction_call;
|
|
||||||
add esp, 8;
|
|
||||||
mov [esp + 0x20], eax;
|
|
||||||
|
|
||||||
popad;
|
|
||||||
pop eax;
|
|
||||||
|
|
||||||
test eax, eax;
|
|
||||||
jnz just_ret;
|
|
||||||
|
|
||||||
// go do original code
|
|
||||||
push scr_getfunction_stub_ret_loc;
|
|
||||||
ret;
|
|
||||||
|
|
||||||
just_ret:
|
|
||||||
add esp, 4;
|
|
||||||
push 0x682DC8;
|
|
||||||
ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
game::BuiltinMethod scr_getmethod_call(const char** pName, int* pType)
|
|
||||||
{
|
|
||||||
auto itr = methods.find(*pName);
|
|
||||||
|
|
||||||
if (itr == methods.end())
|
|
||||||
{
|
|
||||||
// call og
|
|
||||||
const auto og_addr = scr_getmethod_hook.get_original();
|
|
||||||
game::BuiltinMethod answer;
|
|
||||||
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov edi, pType;
|
|
||||||
mov esi, pName;
|
|
||||||
call og_addr;
|
|
||||||
mov answer, eax;
|
|
||||||
}
|
|
||||||
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pType = 0;
|
|
||||||
return itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::BuiltinMethod NAKED scr_getmethod_stub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push edi;
|
|
||||||
push esi;
|
|
||||||
call scr_getmethod_call;
|
|
||||||
add esp, 8;
|
|
||||||
|
|
||||||
ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace function
|
namespace function
|
||||||
{
|
{
|
||||||
void add(const std::string& name, const game::BuiltinFunction function)
|
void add(const std::string& name, const game::BuiltinFunction function)
|
||||||
{
|
{
|
||||||
functions.insert_or_assign(name, function);
|
plugin::get()->get_interface()->gsc()->register_function(name, function);
|
||||||
}
|
|
||||||
|
|
||||||
game::BuiltinFunction get(const char** name, int* type)
|
|
||||||
{
|
|
||||||
auto got = functions.find(*name);
|
|
||||||
|
|
||||||
if (got == functions.end())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
*type = 0;
|
|
||||||
return got->second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,46 +16,8 @@ namespace gsc
|
|||||||
{
|
{
|
||||||
void add(const std::string& name, const game::BuiltinMethod method)
|
void add(const std::string& name, const game::BuiltinMethod method)
|
||||||
{
|
{
|
||||||
methods.insert_or_assign(name, method);
|
plugin::get()->get_interface()->gsc()->register_method(name, (plutonium::sdk::v1::interfaces::gsc::method_callback)method);
|
||||||
}
|
|
||||||
|
|
||||||
game::BuiltinMethod get(const char** name, int* type)
|
|
||||||
{
|
|
||||||
auto got = methods.find(*name);
|
|
||||||
|
|
||||||
if (got == methods.end())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
*type = 0;
|
|
||||||
return got->second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
// for when we dont use the decomp
|
|
||||||
// custom gsc methods
|
|
||||||
if (game::plutonium::scr_get_method_stub != nullptr)
|
|
||||||
{
|
|
||||||
scr_getmethod_hook.create(game::plutonium::scr_get_method_stub.get(), scr_getmethod_stub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom gsc funcs
|
|
||||||
if (game::plutonium::scr_get_function_stub != nullptr)
|
|
||||||
{
|
|
||||||
scr_getfunction_stub_ret_loc = game::plutonium::scr_get_function_stub.get();
|
|
||||||
utils::hook::jump(SELECT(0x0, 0x682D99), scr_getfunction_stub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_COMPONENT(gsc::component)
|
|
||||||
|
|
||||||
|
@ -5,12 +5,10 @@ namespace gsc
|
|||||||
namespace function
|
namespace function
|
||||||
{
|
{
|
||||||
void add(const std::string& name, const game::BuiltinFunction function);
|
void add(const std::string& name, const game::BuiltinFunction function);
|
||||||
game::BuiltinFunction get(const char** name, int* type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace method
|
namespace method
|
||||||
{
|
{
|
||||||
void add(const std::string& name, const game::BuiltinMethod method);
|
void add(const std::string& name, const game::BuiltinMethod method);
|
||||||
game::BuiltinMethod get(const char** name, int* type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,266 +1,11 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
#include <utils/hook.hpp>
|
|
||||||
|
|
||||||
namespace scheduler
|
namespace scheduler
|
||||||
{
|
{
|
||||||
namespace
|
void on_scr_execute(void(*callback)())
|
||||||
{
|
{
|
||||||
struct task
|
plugin::get()->get_interface()->callbacks()->on_scripts_execute(callback);
|
||||||
{
|
|
||||||
std::function<bool()> handler{};
|
|
||||||
std::chrono::milliseconds interval{};
|
|
||||||
std::chrono::high_resolution_clock::time_point last_call{};
|
|
||||||
};
|
|
||||||
|
|
||||||
using task_list = std::vector<task>;
|
|
||||||
|
|
||||||
class task_pipeline
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void add(task&& task)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&task](task_list& tasks)
|
|
||||||
{
|
|
||||||
tasks.emplace_back(std::move(task));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
this->merge_callbacks();
|
|
||||||
|
|
||||||
for (auto i = tasks.begin(); i != tasks.end();)
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::high_resolution_clock::now();
|
|
||||||
const auto diff = now - i->last_call;
|
|
||||||
|
|
||||||
if (diff < i->interval)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i->last_call = now;
|
|
||||||
|
|
||||||
const auto res = i->handler();
|
|
||||||
if (res == cond_end)
|
|
||||||
{
|
|
||||||
i = tasks.erase(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
utils::concurrency::container<task_list> new_callbacks_;
|
|
||||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
|
||||||
|
|
||||||
void merge_callbacks()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
|
||||||
new_tasks = {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::thread thread;
|
|
||||||
task_pipeline pipelines[pipeline::count];
|
|
||||||
|
|
||||||
void execute(const pipeline type)
|
|
||||||
{
|
|
||||||
assert(type >= 0 && type < pipeline::count);
|
|
||||||
pipelines[type].execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute_server()
|
|
||||||
{
|
|
||||||
execute(pipeline::server);
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute_main()
|
|
||||||
{
|
|
||||||
execute(pipeline::main);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils::hook::detour com_init_hook;
|
|
||||||
utils::hook::detour gscr_postloadscripts_hook;
|
|
||||||
|
|
||||||
std::vector<std::function<void()>> post_init_funcs;
|
|
||||||
bool com_inited = false;
|
|
||||||
|
|
||||||
void on_post_init_hook()
|
|
||||||
{
|
|
||||||
if (com_inited)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
com_inited = true;
|
|
||||||
for (const auto& func : post_init_funcs)
|
|
||||||
{
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
|
|
||||||
post_init_funcs.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void com_init_stub()
|
|
||||||
{
|
|
||||||
com_init_hook.invoke<void>();
|
|
||||||
on_post_init_hook();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::function<void(game::scriptInstance_t)>> pre_scr_init_funcs;
|
|
||||||
std::vector<std::function<void(game::scriptInstance_t)>> post_scr_init_funcs;
|
|
||||||
|
|
||||||
utils::hook::detour pre_scr_init_system_hook;
|
|
||||||
utils::hook::detour post_scr_init_system_hook;
|
|
||||||
|
|
||||||
void* pre_scr_init_system_original;
|
|
||||||
void* post_scr_init_system_original;
|
|
||||||
|
|
||||||
NAKED void pre_scr_init_system_stub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pushad;
|
|
||||||
push eax;
|
|
||||||
call exec_pre_scr_init_funcs;
|
|
||||||
add esp, 4;
|
|
||||||
popad;
|
|
||||||
|
|
||||||
push pre_scr_init_system_original;
|
|
||||||
ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NAKED void post_scr_init_system_stub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pushad;
|
|
||||||
push eax;
|
|
||||||
call exec_post_scr_init_funcs;
|
|
||||||
add esp, 4;
|
|
||||||
popad;
|
|
||||||
|
|
||||||
push post_scr_init_system_original;
|
|
||||||
ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
|
||||||
const std::chrono::milliseconds delay)
|
|
||||||
{
|
|
||||||
assert(type >= 0 && type < pipeline::count);
|
|
||||||
|
|
||||||
task task;
|
|
||||||
task.handler = callback;
|
|
||||||
task.interval = delay;
|
|
||||||
task.last_call = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
pipelines[type].add(std::move(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop(const std::function<void()>& callback, const pipeline type,
|
|
||||||
const std::chrono::milliseconds delay)
|
|
||||||
{
|
|
||||||
schedule([callback]()
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
return cond_continue;
|
|
||||||
}, type, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
void once(const std::function<void()>& callback, const pipeline type,
|
|
||||||
const std::chrono::milliseconds delay)
|
|
||||||
{
|
|
||||||
schedule([callback]()
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
return cond_end;
|
|
||||||
}, type, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_init(const std::function<void()>& callback)
|
|
||||||
{
|
|
||||||
if (com_inited)
|
|
||||||
{
|
|
||||||
once(callback, pipeline::main);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
post_init_funcs.push_back(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_pre_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback)
|
|
||||||
{
|
|
||||||
pre_scr_init_funcs.push_back(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_post_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback)
|
|
||||||
{
|
|
||||||
post_scr_init_funcs.push_back(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec_pre_scr_init_funcs(game::scriptInstance_t inst)
|
|
||||||
{
|
|
||||||
for (const auto& func : pre_scr_init_funcs)
|
|
||||||
{
|
|
||||||
func(inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec_post_scr_init_funcs(game::scriptInstance_t inst)
|
|
||||||
{
|
|
||||||
for (const auto& func : post_scr_init_funcs)
|
|
||||||
{
|
|
||||||
func(inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class component final : public component_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void post_unpack() override
|
|
||||||
{
|
|
||||||
thread = std::thread([]()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
execute(pipeline::async);
|
|
||||||
std::this_thread::sleep_for(10ms);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
com_init_hook.create(SELECT(0x0, 0x59D710), com_init_stub);
|
|
||||||
utils::hook::call(SELECT(0x0, 0x503B5D), execute_server);
|
|
||||||
utils::hook::call(SELECT(0x0, 0x59DCFD), execute_main);
|
|
||||||
|
|
||||||
// for when we dont use decomp
|
|
||||||
pre_scr_init_system_hook.create(0x699865, pre_scr_init_system_stub);
|
|
||||||
pre_scr_init_system_original = pre_scr_init_system_hook.get_original();
|
|
||||||
post_scr_init_system_hook.create(0x699924, post_scr_init_system_stub);
|
|
||||||
post_scr_init_system_original = post_scr_init_system_hook.get_original();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_COMPONENT(scheduler::component)
|
|
||||||
|
@ -2,28 +2,5 @@
|
|||||||
|
|
||||||
namespace scheduler
|
namespace scheduler
|
||||||
{
|
{
|
||||||
enum pipeline
|
void on_scr_execute(void(*callback)());
|
||||||
{
|
|
||||||
server,
|
|
||||||
async,
|
|
||||||
main,
|
|
||||||
count,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const bool cond_continue = false;
|
|
||||||
static const bool cond_end = true;
|
|
||||||
|
|
||||||
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::main,
|
|
||||||
std::chrono::milliseconds delay = 0ms);
|
|
||||||
void loop(const std::function<void()>& callback, pipeline type = pipeline::main,
|
|
||||||
std::chrono::milliseconds delay = 0ms);
|
|
||||||
void once(const std::function<void()>& callback, pipeline type = pipeline::main,
|
|
||||||
std::chrono::milliseconds delay = 0ms);
|
|
||||||
|
|
||||||
void on_init(const std::function<void()>& callback);
|
|
||||||
|
|
||||||
void on_pre_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback);
|
|
||||||
void on_post_scr_init_system(const std::function<void(game::scriptInstance_t)>& callback);
|
|
||||||
void exec_pre_scr_init_funcs(game::scriptInstance_t inst);
|
|
||||||
void exec_post_scr_init_funcs(game::scriptInstance_t inst);
|
|
||||||
}
|
}
|
||||||
|
@ -9,174 +9,32 @@
|
|||||||
|
|
||||||
namespace signatures
|
namespace signatures
|
||||||
{
|
{
|
||||||
std::string read_sigs_file()
|
bool addr_is_in_image_space_of_pluto(size_t wheree)
|
||||||
{
|
|
||||||
return utils::compression::zlib::decompress(utils::cryptography::des::decrypt(utils::io::read_file("t4sp-server-plugin/sigs")));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_sigs_file(const std::string& f)
|
|
||||||
{
|
|
||||||
return utils::io::write_file("t4sp-server-plugin/sigs", utils::cryptography::des::encrypt(utils::compression::zlib::compress(f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_current_version()
|
|
||||||
{
|
|
||||||
return *reinterpret_cast<const char**>(0x4FF72D + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::string> get_cache_info_for_our_version()
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, std::string> answer;
|
|
||||||
|
|
||||||
auto* version = get_current_version();
|
|
||||||
|
|
||||||
nlohmann::json cache_json = nlohmann::json::parse(read_sigs_file(), nullptr, false, true);
|
|
||||||
if (!cache_json.is_discarded() && cache_json.is_object())
|
|
||||||
{
|
|
||||||
for (const auto& [key, value] : cache_json.items())
|
|
||||||
{
|
|
||||||
if (key != version)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value.is_object())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
answer = value.get<std::unordered_map<std::string, std::string>>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool save_cache_info_for_our_version(const std::unordered_map<std::string, std::string>& cache_info)
|
|
||||||
{
|
|
||||||
auto* version = get_current_version();
|
|
||||||
|
|
||||||
nlohmann::json cache_json = nlohmann::json::parse(read_sigs_file(), nullptr, false, true);
|
|
||||||
if (cache_json.is_discarded() || !cache_json.is_object())
|
|
||||||
{
|
|
||||||
cache_json = nlohmann::json::parse("{}", nullptr, false, true);
|
|
||||||
|
|
||||||
if (cache_json.is_discarded() || !cache_json.is_object())
|
|
||||||
{
|
|
||||||
return false; // can't happen?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_json[version] = cache_info;
|
|
||||||
return write_sigs_file(cache_json.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t load_image_size()
|
|
||||||
{
|
{
|
||||||
MODULEINFO info{};
|
MODULEINFO info{};
|
||||||
GetModuleInformation(GetCurrentProcess(),
|
GetModuleInformation(GetCurrentProcess(),
|
||||||
GetModuleHandle("plutonium-bootstrapper-win32.exe"), &info, sizeof(MODULEINFO));
|
GetModuleHandle("plutonium-bootstrapper-win32.exe"), &info, sizeof(MODULEINFO));
|
||||||
return info.SizeOfImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t get_image_size()
|
static const auto image_base = reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
||||||
{
|
|
||||||
static const auto image_size = load_image_size();
|
|
||||||
return image_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t load_iamge_base()
|
return wheree >= image_base && wheree < image_base + info.SizeOfImage;
|
||||||
{
|
|
||||||
return reinterpret_cast<size_t>(GetModuleHandle("plutonium-bootstrapper-win32.exe"));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t get_image_base()
|
|
||||||
{
|
|
||||||
static const auto image_base = load_iamge_base();
|
|
||||||
return image_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool addr_is_in_image_space(size_t wheree)
|
|
||||||
{
|
|
||||||
static const auto image_base = load_iamge_base();
|
|
||||||
|
|
||||||
return wheree >= image_base && wheree < image_base + get_image_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t find_string_ptr(const std::string& string)
|
|
||||||
{
|
|
||||||
const char* string_ptr = nullptr;
|
|
||||||
std::string mask(string.size(), 'x');
|
|
||||||
const auto base = get_image_base();
|
|
||||||
utils::hook::signature signature(base, get_image_size() - base);
|
|
||||||
|
|
||||||
signature.add({
|
|
||||||
string,
|
|
||||||
mask,
|
|
||||||
[&](char* address)
|
|
||||||
{
|
|
||||||
string_ptr = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
signature.process();
|
|
||||||
return reinterpret_cast<size_t>(string_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t find_string_ref(const std::string& string)
|
|
||||||
{
|
|
||||||
char bytes[4] = {0};
|
|
||||||
const auto string_ptr = find_string_ptr(string);
|
|
||||||
if (!string_ptr)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(bytes, &string_ptr, sizeof(bytes));
|
|
||||||
return find_string_ptr({bytes, 4});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string err_reason;
|
std::string err_reason;
|
||||||
|
|
||||||
const std::string& get_err_reason()
|
const std::string& get_err_reason()
|
||||||
{
|
{
|
||||||
return err_reason;
|
return err_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process_printf(std::unordered_map<std::string, std::string> &cache_info)
|
|
||||||
{
|
|
||||||
if (cache_info.contains("printf"))
|
|
||||||
{
|
|
||||||
game::plutonium::printf.set(std::atoi(cache_info.at("printf").c_str()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto string_ref = find_string_ref("A critical exception occured!\n");
|
|
||||||
if (!string_ref)
|
|
||||||
{
|
|
||||||
err_reason = "printf";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto offset = *reinterpret_cast<size_t*>(string_ref + 5);
|
|
||||||
game::plutonium::printf.set(string_ref + 4 + 5 + offset);
|
|
||||||
|
|
||||||
cache_info.insert_or_assign("printf", std::to_string(string_ref + 4 + 5 + offset));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define SAFE_SET_PLUTO_SYMBOL_DOUBLE(name, addr, off) \
|
#define SAFE_SET_PLUTO_SYMBOL_DOUBLE(name, addr, off) \
|
||||||
addr2 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
addr2 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
||||||
if (!addr_is_in_image_space(addr2)) \
|
if (!addr_is_in_image_space_of_pluto(addr2)) \
|
||||||
{ \
|
{ \
|
||||||
err_reason = #name " 1"; \
|
err_reason = #name " 1"; \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr2 + off)); \
|
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr2 + off)); \
|
||||||
if (!addr_is_in_image_space(addr1)) \
|
if (!addr_is_in_image_space_of_pluto(addr1)) \
|
||||||
{ \
|
{ \
|
||||||
err_reason = #name " 2"; \
|
err_reason = #name " 2"; \
|
||||||
return false; \
|
return false; \
|
||||||
@ -186,7 +44,7 @@ namespace signatures
|
|||||||
|
|
||||||
#define SAFE_SET_PLUTO_SYMBOL(name, addr) \
|
#define SAFE_SET_PLUTO_SYMBOL(name, addr) \
|
||||||
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
addr1 = reinterpret_cast<size_t>(utils::hook::get_displacement_addr(addr)); \
|
||||||
if (!addr_is_in_image_space(addr1)) \
|
if (!addr_is_in_image_space_of_pluto(addr1)) \
|
||||||
{ \
|
{ \
|
||||||
err_reason = #name; \
|
err_reason = #name; \
|
||||||
return false; \
|
return false; \
|
||||||
@ -198,12 +56,6 @@ namespace signatures
|
|||||||
{
|
{
|
||||||
size_t addr1;
|
size_t addr1;
|
||||||
size_t addr2;
|
size_t addr2;
|
||||||
auto cache_info = get_cache_info_for_our_version();
|
|
||||||
|
|
||||||
if (!process_printf(cache_info))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(load_custom_script_func, 0x689C80, 0x6);
|
SAFE_SET_PLUTO_SYMBOL_DOUBLE(load_custom_script_func, 0x689C80, 0x6);
|
||||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(script_preprocess, 0x689BCF, 0x2);
|
SAFE_SET_PLUTO_SYMBOL_DOUBLE(script_preprocess, 0x689BCF, 0x2);
|
||||||
@ -216,21 +68,15 @@ namespace signatures
|
|||||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(store_func_codepos, 0x688909, 0x3);
|
SAFE_SET_PLUTO_SYMBOL_DOUBLE(store_func_codepos, 0x688909, 0x3);
|
||||||
|
|
||||||
SAFE_SET_PLUTO_SYMBOL(cscr_get_function_hook, 0x682DC0);
|
SAFE_SET_PLUTO_SYMBOL(cscr_get_function_hook, 0x682DC0);
|
||||||
SAFE_SET_PLUTO_SYMBOL(scr_get_function_stub, 0x682D99);
|
|
||||||
SAFE_SET_PLUTO_SYMBOL(scr_get_method_stub, 0x683043);
|
|
||||||
SAFE_SET_PLUTO_SYMBOL(cscr_get_method_hook, 0x68305C);
|
SAFE_SET_PLUTO_SYMBOL(cscr_get_method_hook, 0x68305C);
|
||||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_method_hook, 0x683043, 0x4);
|
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_method_hook, 0x683043, 0x4);
|
||||||
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_function_hook, 0x682D99, 0x8);
|
SAFE_SET_PLUTO_SYMBOL_DOUBLE(scr_get_function_hook, 0x682D99, 0x8);
|
||||||
|
|
||||||
save_cache_info_for_our_version(cache_info);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process()
|
bool process()
|
||||||
{
|
{
|
||||||
utils::cryptography::des::set_key("694201337");
|
|
||||||
|
|
||||||
return handle_funcs();
|
return handle_funcs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,11 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
|
||||||
#include "component/signatures.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize()
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD ul_reason_for_call, LPVOID /*reserved_*/)
|
|
||||||
{
|
{
|
||||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
return plugin::get();
|
||||||
{
|
}
|
||||||
if (game::environment::t4sp())
|
|
||||||
{
|
|
||||||
if (!signatures::process())
|
|
||||||
{
|
|
||||||
MessageBoxA(NULL,
|
|
||||||
std::format("This version of t4sp-server-plugin is outdated.\n" \
|
|
||||||
"Download the latest dll from here: https://github.com/JezuzLizard/T4SP-Server-Plugin/releases\n" \
|
|
||||||
"'{}' failed", signatures::get_err_reason()).c_str(),
|
|
||||||
"ERROR", MB_ICONERROR);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game::plutonium::printf.get() != nullptr)
|
|
||||||
{
|
|
||||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), game::plutonium::printf);
|
|
||||||
}
|
|
||||||
|
|
||||||
component_loader::post_unpack();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageBoxA(nullptr, "Unsupported game executable. (t4sp is only supported)", "ERROR, BRO!", 0);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain(HMODULE /*module_*/, DWORD /*ul_reason_for_call*/, LPVOID /*reserved_*/)
|
||||||
|
{
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
@ -231,6 +231,21 @@ namespace game
|
|||||||
*cmd_functions = newCmd;
|
*cmd_functions = newCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restored
|
||||||
|
const char** FS_ListFiles(const char* path, const char* extension, FsListBehavior_e behavior, int* numfiles)
|
||||||
|
{
|
||||||
|
return FS_ListFilteredFiles(*fs_searchpaths, path, extension, nullptr, behavior, numfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restored
|
||||||
|
void FS_FreeFileList(const char** list)
|
||||||
|
{
|
||||||
|
if ( list )
|
||||||
|
{
|
||||||
|
Hunk_UserDestroy((HunkUser*)*(list - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// restored
|
// restored
|
||||||
void Sys_EnterCriticalSection(CriticalSection critSect)
|
void Sys_EnterCriticalSection(CriticalSection critSect)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +78,9 @@ namespace game
|
|||||||
void Sys_EnterCriticalSection(CriticalSection critSect);
|
void Sys_EnterCriticalSection(CriticalSection critSect);
|
||||||
void Sys_LeaveCriticalSection(CriticalSection critSect);
|
void Sys_LeaveCriticalSection(CriticalSection critSect);
|
||||||
|
|
||||||
|
const char** FS_ListFiles(const char* path, const char* extension, FsListBehavior_e behavior, int* numfiles);
|
||||||
|
void FS_FreeFileList(const char** list);
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
WEAK symbol<CRITICAL_SECTION> s_criticalSection{ 0x0, 0x2298D08 };
|
WEAK symbol<CRITICAL_SECTION> s_criticalSection{ 0x0, 0x2298D08 };
|
||||||
WEAK symbol<HunkUser*> g_DebugHunkUser{ 0x0, 0x212B2EC };
|
WEAK symbol<HunkUser*> g_DebugHunkUser{ 0x0, 0x212B2EC };
|
||||||
@ -93,8 +96,6 @@ namespace game
|
|||||||
|
|
||||||
namespace plutonium
|
namespace plutonium
|
||||||
{
|
{
|
||||||
WEAK symbol<int(const char* fmt, ...)> printf{0x0, 0x0};
|
|
||||||
|
|
||||||
WEAK symbol<void(scriptInstance_t)> load_custom_script_func{0x0, 0x0};
|
WEAK symbol<void(scriptInstance_t)> load_custom_script_func{0x0, 0x0};
|
||||||
WEAK symbol<void(char*, game::scriptInstance_t, sval_u*)> script_preprocess{0x0, 0x0};
|
WEAK symbol<void(char*, game::scriptInstance_t, sval_u*)> script_preprocess{0x0, 0x0};
|
||||||
WEAK symbol<void(game::scriptInstance_t)> vm_execute_update_codepos{0x0, 0x0};
|
WEAK symbol<void(game::scriptInstance_t)> vm_execute_update_codepos{0x0, 0x0};
|
||||||
@ -104,9 +105,6 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<const char*(game::scriptInstance_t, unsigned int)> at_codepose_va{ 0x0, 0x0 };
|
WEAK symbol<const char*(game::scriptInstance_t, unsigned int)> at_codepose_va{ 0x0, 0x0 };
|
||||||
|
|
||||||
WEAK symbol<void()> scr_get_method_stub{ 0x0, 0x0 };
|
|
||||||
WEAK symbol<void()> scr_get_function_stub{ 0x0, 0x0 };
|
|
||||||
|
|
||||||
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> scr_get_method_hook{ 0x0, 0x0 };
|
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> scr_get_method_hook{ 0x0, 0x0 };
|
||||||
WEAK symbol<game::BuiltinFunction(const char** name, int* type)> scr_get_function_hook{ 0x0, 0x0 };
|
WEAK symbol<game::BuiltinFunction(const char** name, int* type)> scr_get_function_hook{ 0x0, 0x0 };
|
||||||
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> cscr_get_method_hook{ 0x0, 0x0 };
|
WEAK symbol<game::BuiltinMethod(const char** name, int* type)> cscr_get_method_hook{ 0x0, 0x0 };
|
||||||
|
68
src/plugin.cpp
Normal file
68
src/plugin.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "component/signatures.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
namespace plugin
|
||||||
|
{
|
||||||
|
std::uint32_t plugin::plugin_version()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* plugin::plugin_name()
|
||||||
|
{
|
||||||
|
return "t4sp-server-plugin";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plugin::is_game_supported(plutonium::sdk::game game)
|
||||||
|
{
|
||||||
|
return game == plutonium::sdk::game::t4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin::on_startup(plutonium::sdk::iinterface* interface_ptr, plutonium::sdk::game game)
|
||||||
|
{
|
||||||
|
this->interface_ = interface_ptr;
|
||||||
|
this->game_ = game;
|
||||||
|
|
||||||
|
if (!game::environment::t4sp())
|
||||||
|
{
|
||||||
|
MessageBoxA(nullptr, "Unsupported game executable. (t4sp is only supported)", "ERROR, BRO!", 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signatures::process())
|
||||||
|
{
|
||||||
|
MessageBoxA(NULL,
|
||||||
|
std::format("This version of t4sp-server-plugin is outdated.\n" \
|
||||||
|
"Download the latest dll from here: https://github.com/JezuzLizard/T4SP-Server-Plugin/releases\n" \
|
||||||
|
"'{}' failed", signatures::get_err_reason()).c_str(),
|
||||||
|
"ERROR", MB_ICONERROR);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
component_loader::post_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void plugin::on_shutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
plutonium::sdk::iinterface* plugin::get_interface()
|
||||||
|
{
|
||||||
|
return this->interface_;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutonium::sdk::game plugin::get_game()
|
||||||
|
{
|
||||||
|
return this->game_;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin* get()
|
||||||
|
{
|
||||||
|
static plugin instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
}
|
30
src/plugin.hpp
Normal file
30
src/plugin.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <plutonium_sdk.hpp>
|
||||||
|
|
||||||
|
namespace plugin
|
||||||
|
{
|
||||||
|
class plugin : public plutonium::sdk::plugin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~plugin() = default;
|
||||||
|
|
||||||
|
std::uint32_t plugin_version() override;
|
||||||
|
const char* plugin_name() override;
|
||||||
|
|
||||||
|
bool is_game_supported(plutonium::sdk::game game) override;
|
||||||
|
|
||||||
|
void on_startup(plutonium::sdk::iinterface* interface_ptr, plutonium::sdk::game game) override;
|
||||||
|
void on_shutdown() override;
|
||||||
|
|
||||||
|
plutonium::sdk::iinterface* get_interface();
|
||||||
|
plutonium::sdk::game get_game();
|
||||||
|
|
||||||
|
private:
|
||||||
|
plutonium::sdk::iinterface* interface_{};
|
||||||
|
plutonium::sdk::game game_{};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
plugin* get();
|
||||||
|
}
|
@ -71,6 +71,8 @@
|
|||||||
#include "game/structs.hpp"
|
#include "game/structs.hpp"
|
||||||
#include "game/symbols.hpp"
|
#include "game/symbols.hpp"
|
||||||
|
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
std::string build_gsc_dump(game::scriptInstance_t inst);
|
std::string build_gsc_dump(game::scriptInstance_t inst);
|
||||||
void push_opcode_history(game::scriptInstance_t inst, game::OpcodeVM op);
|
void push_opcode_history(game::scriptInstance_t inst, game::OpcodeVM op);
|
||||||
void push_builtin_history(game::scriptInstance_t inst, int idx);
|
void push_builtin_history(game::scriptInstance_t inst, int idx);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user