This commit is contained in:
2023-12-08 17:16:22 +01:00
commit ce6b6f4112
169 changed files with 22772 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include "extension.hpp"
namespace gsc {
#define GSC_DEBUG_FUNCTIONS
namespace {
struct script_function_def {
game::BuiltinFunction actionFunc;
bool type;
};
struct script_method_def {
game::BuiltinMethod actionFunc;
bool type;
};
std::unordered_map<std::string, script_function_def> custom_scr_funcs;
std::unordered_map<std::string, script_method_def> custom_scr_meths;
game::BuiltinFunction built_in_get_function_stub(const char** p_name,
int* type) {
if (p_name) {
const auto itr = custom_scr_funcs.find(utils::string::to_lower(*p_name));
if (itr != custom_scr_funcs.end()) {
*type = itr->second.type;
return itr->second.actionFunc;
}
} else {
for (const auto& [name, func] : custom_scr_funcs) {
game::Scr_RegisterFunction(reinterpret_cast<int>(func.actionFunc),
name.data());
}
}
// If no function was found let's call BuiltIn_GetFunction
return utils::hook::invoke<game::BuiltinFunction>(0x4DD160, p_name, type);
}
game::BuiltinMethod built_in_get_method_stub(const char** p_name, int* type) {
if (p_name) {
const auto itr = custom_scr_meths.find(utils::string::to_lower(*p_name));
if (itr != custom_scr_meths.end()) {
*type = itr->second.type;
return itr->second.actionFunc;
}
} else {
for (const auto& [name, meth] : custom_scr_meths) {
game::Scr_RegisterFunction(reinterpret_cast<int>(meth.actionFunc),
name.data());
}
}
// If no method was found let's call BuiltIn_GetMethod
return utils::hook::invoke<game::BuiltinMethod>(0x5DB850, p_name, type);
}
} // namespace
void add_function(const char* name, game::BuiltinFunction func, bool type) {
script_function_def def;
def.actionFunc = func;
def.type = type;
custom_scr_funcs.emplace(utils::string::to_lower(name), def);
}
void add_method(const char* name, game::BuiltinMethod func, bool type) {
script_method_def def;
def.actionFunc = func;
def.type = type;
custom_scr_meths.emplace(utils::string::to_lower(name), def);
}
class extension final : public component_interface {
public:
void post_load() override {
// Fetch custom functions
utils::hook(0x4ADF9C, built_in_get_function_stub, HOOK_CALL)
.install()
->quick(); // Scr_GetFunction
utils::hook(0x444827, built_in_get_method_stub, HOOK_CALL)
.install()
->quick(); // Scr_GetMethod
add_functions();
#ifdef GSC_DEBUG_FUNCTIONS
add_debug_functions();
#endif
}
static void add_functions() {
add_function("Float", [] {
switch (game::Scr_GetType(0)) {
case game::VAR_STRING:
game::Scr_AddFloat(
static_cast<float>(std::atof(game::Scr_GetString(0))));
break;
case game::VAR_FLOAT:
game::Scr_AddFloat(game::Scr_GetFloat(0));
break;
case game::VAR_INTEGER:
game::Scr_AddFloat(static_cast<float>(game::Scr_GetInt(0)));
break;
default:
game::Scr_ParamError(0, utils::string::va("cannot cast {0} to float",
game::Scr_GetTypeName(0)));
break;
}
});
}
static void add_debug_functions() {
add_function("AddDebugCommand",
[] { game::Cbuf_AddText(0, game::Scr_GetString(0)); });
}
};
} // namespace gsc
REGISTER_COMPONENT(gsc::extension)

View File

@ -0,0 +1,7 @@
#pragma once
namespace gsc {
void add_function(const char* name, game::BuiltinFunction func,
bool type = false);
void add_method(const char* name, game::BuiltinMethod func, bool type = false);
} // namespace gsc

View File

@ -0,0 +1,88 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace gsc {
namespace {
std::vector<int> main_handles;
std::vector<int> init_handles;
// Do not use C++ objects because Scr_LoadScript may longjmp
void g_scr_load_scripts_stub() {
// Clear handles (from previous GSC loading session)
main_handles.clear();
init_handles.clear();
char path[MAX_PATH]{};
auto num_files = 0;
const auto** files =
game::FS_ListFiles("scripts/", "gsc", game::FS_LIST_ALL, &num_files, 10);
for (auto i = 0; i < num_files; ++i) {
const auto* script_file = files[i];
game::Com_Printf(game::CON_CHANNEL_SERVER, "Loading script %s...\n",
script_file);
const auto len = sprintf_s(path, "%s/%s", "scripts", script_file);
if (len == -1) {
continue;
}
// Scr_LoadScriptInternal will add the '.gsc' suffix so we remove it
path[len - 4] = '\0';
if (!game::Scr_LoadScript(path)) {
game::Com_Printf(game::CON_CHANNEL_SERVER,
"Script %s encountered an error while loading\n", path);
continue;
}
game::Com_Printf(game::CON_CHANNEL_SERVER,
"Script %s.gsc loaded successfully\n", path);
const auto main_handle = game::Scr_GetFunctionHandle(path, "main");
if (main_handle) {
main_handles.push_back(main_handle);
}
const auto init_handle = game::Scr_GetFunctionHandle(path, "init");
if (init_handle) {
init_handles.push_back(init_handle);
}
// Allow scripts with no handles
}
game::FS_FreeFileList(files, 10);
}
void scr_load_level_stub() {
for (const auto& handle : main_handles) {
const auto id = game::Scr_ExecThread(handle, 0);
game::Scr_FreeThread(static_cast<std::uint16_t>(id));
}
utils::hook::invoke<void>(0x470860); // Scr_LoadLevel
for (const auto& handle : init_handles) {
const auto id = game::Scr_ExecThread(handle, 0);
game::Scr_FreeThread(static_cast<std::uint16_t>(id));
}
}
} // namespace
class loading final : public component_interface {
public:
void post_load() override {
utils::hook(0x4054C2, g_scr_load_scripts_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x4C1E34, scr_load_level_stub, HOOK_CALL).install()->quick();
}
};
} // namespace gsc
REGISTER_COMPONENT(gsc::loading)

View File

@ -0,0 +1,83 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "extension.hpp"
namespace gsc {
namespace {
void scr_str_i_cmp() {
const auto* string1 = game::SL_ConvertToString(game::Scr_GetConstString(0));
const auto* string2 = game::SL_ConvertToString(game::Scr_GetConstString(1));
game::Scr_AddInt(game::I_stricmp(string1, string2));
}
void scr_is_end_str() {
const auto* str = game::Scr_GetString(0);
const auto* suffix = game::Scr_GetString(1);
const auto str_len = std::strlen(str);
const auto suffix_len = std::strlen(suffix);
if (suffix_len > str_len) {
game::Scr_AddInt(0);
return;
}
game::Scr_AddInt(
std::memcmp(str + str_len - suffix_len, suffix, suffix_len) == 0);
}
void scr_to_upper() {
const auto script_value = game::Scr_GetConstString(0);
const auto* string = game::SL_ConvertToString(script_value);
char out[1024]{};
bool changed = false;
std::size_t i = 0;
while (i < sizeof(out)) {
const auto value = *string;
const auto result =
static_cast<char>(std::toupper(static_cast<unsigned char>(value)));
out[i] = result;
if (value != result) {
changed = true;
}
if (result == '\0') {
break;
}
++string;
++i;
}
if (i >= sizeof(out)) {
game::Scr_Error("string too long");
return;
}
if (changed) {
game::Scr_AddString(out);
} else {
game::SL_AddRefToString(script_value);
game::Scr_AddConstString(script_value);
game::SL_RemoveRefToString(script_value);
}
}
} // namespace
class string final : public component_interface {
public:
void post_load() override {
add_function("StrICmp", scr_str_i_cmp);
add_function("IsEndStr", scr_is_end_str);
add_function("ToUpper", scr_to_upper);
}
};
} // namespace gsc
REGISTER_COMPONENT(gsc::string)