init
This commit is contained in:
1206
src/client/component/gsc/error.cpp
Normal file
1206
src/client/component/gsc/error.cpp
Normal file
File diff suppressed because it is too large
Load Diff
126
src/client/component/gsc/extension.cpp
Normal file
126
src/client/component/gsc/extension.cpp
Normal 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)
|
7
src/client/component/gsc/extension.hpp
Normal file
7
src/client/component/gsc/extension.hpp
Normal 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
|
88
src/client/component/gsc/loading.cpp
Normal file
88
src/client/component/gsc/loading.cpp
Normal 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)
|
83
src/client/component/gsc/string.cpp
Normal file
83
src/client/component/gsc/string.cpp
Normal 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)
|
Reference in New Issue
Block a user