mirror of
https://github.com/JezuzLizard/T4SP-Server-Plugin.git
synced 2025-07-03 17:51:51 +00:00
Compare commits
51 Commits
v0.0.5
...
38a860e4ce
Author | SHA1 | Date | |
---|---|---|---|
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 |
101
README.md
101
README.md
@ -3,8 +3,105 @@ 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.
|
||||
|
||||
# What does it do?
|
||||
Nothing really right now, just detours and reimplements the entire GSC VM, for research purposes.
|
||||
# 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>)` Deletes the file, return `true` if successful, `false` otherwise.
|
||||
```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>)` 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.
|
||||
* `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;
|
||||
}
|
||||
```
|
||||
|
||||
# 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.
|
||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
Submodule deps/GSL updated: 43d60c5e38...e64c97fc2c
2
deps/asmjit
vendored
2
deps/asmjit
vendored
Submodule deps/asmjit updated: f1ea8a46c3...416f735696
2
deps/curl
vendored
2
deps/curl
vendored
Submodule deps/curl updated: 4528690cd5...78a1814b33
2
deps/json
vendored
2
deps/json
vendored
Submodule deps/json updated: 546370c9e7...a259ecc51e
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
Submodule deps/libtomcrypt updated: b96e96cf8b...7e863d2142
2
deps/libtommath
vendored
2
deps/libtommath
vendored
Submodule deps/libtommath updated: 7f96509df1...8314bde5e5
2
deps/minhook
vendored
2
deps/minhook
vendored
Submodule deps/minhook updated: 49d03ad118...f5485b8454
2
deps/zlib
vendored
2
deps/zlib
vendored
Submodule deps/zlib updated: 04f42ceca4...643e17b749
@ -67,11 +67,18 @@ namespace codsrc
|
||||
{
|
||||
int len;
|
||||
|
||||
for ( len = refString->u.s.byteLen - 1;
|
||||
refString->str[len];
|
||||
len += 256 )
|
||||
if (!refString->u.s.byteLen)
|
||||
{
|
||||
;
|
||||
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;
|
||||
|
@ -4890,20 +4890,24 @@ namespace codsrc
|
||||
return result;
|
||||
}
|
||||
|
||||
result.u.linkPointer = (const char*)value->u.intValue;
|
||||
result = value->u.anim;
|
||||
if ( !animTreeInputForValidation )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
animTreeAnimPtr = animTreeInputForValidation->anims;
|
||||
animPtr = game::gScrAnimPub[inst].xanim_lookup[user][(unsigned int)result.u.linkPointer >> 16].anims;
|
||||
assert(animTreeInputForValidation);
|
||||
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 )
|
||||
{
|
||||
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::RemoveRefToValueInternal(game::SCRIPTINSTANCE_SERVER, value->type, value->u);
|
||||
value->type = game::VAR_UNDEFINED;
|
||||
|
437
src/component/fileio.cpp
Normal file
437
src/component/fileio.cpp
Normal file
@ -0,0 +1,437 @@
|
||||
#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)
|
||||
{
|
||||
if (fpath.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr static std::array bad_strings { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < bad_strings.size(); i++)
|
||||
{
|
||||
if (fpath.find(bad_strings[i]) != std::string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string build_base_path(const std::string& path)
|
||||
{
|
||||
if (!validate_scr_path(path))
|
||||
{
|
||||
game::Scr_Error(utils::string::va("Invalid path: %s", path.c_str()), game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
return path.starts_with("scriptdata/") ? path : "scriptdata/" + path;
|
||||
}
|
||||
|
||||
std::filesystem::path build_full_path(const std::string& path)
|
||||
{
|
||||
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) / (*fs_gamedir->current.string ? fs_gamedir->current.string : "raw") / path;
|
||||
}
|
||||
|
||||
void free_scr_fh(scr_fh_t& scr_fh)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("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);
|
||||
}
|
||||
}
|
||||
|
||||
void fwrite_to_file(bool append_newline)
|
||||
{
|
||||
auto fh = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0) - 1;
|
||||
|
||||
if (fh < 0 || fh >= max_fhs)
|
||||
{
|
||||
game::Scr_Error("fs_fwrite: invalid filehandle", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
if (scr_fhs[fh].type != scr_fh_type_e::WRITE && scr_fhs[fh].type != scr_fh_type_e::APPEND)
|
||||
{
|
||||
game::Scr_Error("File not opened for writing", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
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_pre_scr_init_system([]([[maybe_unused]] game::scriptInstance_t inst)
|
||||
{
|
||||
if (inst != game::SCRIPTINSTANCE_SERVER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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_Error("File already opened", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 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_Error("Too many files opened", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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_Error(utils::string::va("Invalid mode: %s", mode), game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("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 = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0) - 1;
|
||||
|
||||
if (fh < 0 || fh >= max_fhs)
|
||||
{
|
||||
game::Scr_Error("Invalid filehandle", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||
{
|
||||
game::Scr_Error("File not opened for reading", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
// 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 = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0) - 1;
|
||||
|
||||
if (fh < 0 || fh >= max_fhs)
|
||||
{
|
||||
game::Scr_Error("Invalid filehandle", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
if (scr_fhs[fh].type != scr_fh_type_e::READ)
|
||||
{
|
||||
game::Scr_Error("File not opened for reading", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
// 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_Error("Trying to read <1 bytes", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
}
|
||||
|
||||
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 = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0) - 1;
|
||||
|
||||
if (fh < 0 || fh >= max_fhs)
|
||||
{
|
||||
game::Scr_Error("Invalid filehandle", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
if (scr_fhs[fh].type == scr_fh_type_e::UNUSED)
|
||||
{
|
||||
game::Scr_Error("File not opened", game::SCRIPTINSTANCE_SERVER, false);
|
||||
}
|
||||
|
||||
free_scr_fh(scr_fhs[fh]);
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -231,6 +231,21 @@ namespace game
|
||||
*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
|
||||
void Sys_EnterCriticalSection(CriticalSection critSect)
|
||||
{
|
||||
|
@ -78,6 +78,9 @@ namespace game
|
||||
void Sys_EnterCriticalSection(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
|
||||
WEAK symbol<CRITICAL_SECTION> s_criticalSection{ 0x0, 0x2298D08 };
|
||||
WEAK symbol<HunkUser*> g_DebugHunkUser{ 0x0, 0x212B2EC };
|
||||
|
Reference in New Issue
Block a user