diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 5b7c6d84..da7cc2ac 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -8,6 +8,7 @@ #include "command.hpp" #include "console.hpp" #include "game_console.hpp" +#include "gsc.hpp" #include "fastfiles.hpp" #include "filesystem.hpp" #include "scheduler.hpp" @@ -607,6 +608,12 @@ namespace command utils::hook::jump(SELECT_VALUE(0x3A7C80_b, 0x4E9F40_b), dvar_command_stub, true); add_commands_generic(); + + gsc::function::add("executecommand", []() + { + const auto cmd = gsc::get_argument(0).as(); + command::execute(cmd, true); + }); } private: diff --git a/src/client/component/gsc.cpp b/src/client/component/gsc.cpp index 4b53de1d..953e8ddd 100644 --- a/src/client/component/gsc.cpp +++ b/src/client/component/gsc.cpp @@ -3,12 +3,10 @@ #include "console.hpp" #include "fastfiles.hpp" -#include "gsc.hpp" #include "filesystem.hpp" -#include "logfile.hpp" +#include "gsc.hpp" #include "scripting.hpp" -#include "game/game.hpp" #include "game/dvars.hpp" #include "game/scripting/functions.hpp" @@ -452,16 +450,6 @@ namespace gsc } } - scripting::script_value get_argument(int index) - { - if (index >= static_cast(game::scr_VmPub->outparamcount)) - { - return {}; - } - - return game::scr_VmPub->top[-index]; - } - void execute_custom_function(builtin_function function) { auto error = false; @@ -551,18 +539,6 @@ namespace gsc current_filename = filename; scr_emit_function_hook.invoke(filename, thread_name, code_pos); } - - void replace(std::string& str, const std::string& from, const std::string& to) - { - const auto start_pos = str.find(from); - - if (start_pos == std::string::npos) - { - return; - } - - str.replace(start_pos, from.length(), to); - } } game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/) @@ -606,6 +582,16 @@ namespace gsc } } + scripting::script_value get_argument(int index) + { + if (index >= static_cast(game::scr_VmPub->outparamcount)) + { + return {}; + } + + return game::scr_VmPub->top[-index]; + } + namespace function { void add(const std::string& name, builtin_function function) @@ -715,94 +701,6 @@ namespace gsc utils::hook::nop(SELECT_VALUE(0x3CBA4E_b, 0x512AAE_b), 2); utils::hook::call(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), vm_call_builtin_method_stub); - function::add("print", []() - { - const auto num = game::Scr_GetNumParam(); - std::string buffer{}; - - for (auto i = 0; i < num; i++) - { - const auto str = game::Scr_GetString(i); - buffer.append(str); - buffer.append("\t"); - } - - console::info("[SCRIPT] %s\n", buffer.data()); - }); - - function::add("assert", []() - { - const auto expr = get_argument(0).as(); - if (!expr) - { - throw std::runtime_error("assert fail"); - } - }); - - function::add("assertex", []() - { - const auto expr = get_argument(0).as(); - if (!expr) - { - const auto error = get_argument(1).as(); - throw std::runtime_error(error); - } - }); - - function::add("replacefunc", []() - { - const auto what = get_argument(0).get_raw(); - const auto with = get_argument(1).get_raw(); - - if (what.type != game::SCRIPT_FUNCTION) - { - throw std::runtime_error("replaceFunc: parameter 1 must be a function"); - return; - } - - if (with.type != game::SCRIPT_FUNCTION) - { - throw std::runtime_error("replaceFunc: parameter 2 must be a function"); - return; - } - - logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); - }); - - function::add("toupper", []() - { - const auto string = get_argument(0).as(); - game::Scr_AddString(utils::string::to_upper(string).data()); - }); - - function::add("logprint", []() - { - std::string buffer{}; - - const auto params = game::Scr_GetNumParam(); - for (auto i = 0; i < params; i++) - { - const auto string = game::Scr_GetString(i); - buffer.append(string); - } - - game::G_LogPrintf("%s", buffer.data()); - }); - - function::add("va", []() - { - auto fmt = get_argument(0).as(); - - const auto params = game::Scr_GetNumParam(); - for (auto i = 1; i < params; i++) - { - const auto arg = get_argument(i).to_string(); - replace(fmt, "%s", arg); - } - - game::Scr_AddString(fmt.data()); - }); - scripting::on_shutdown([](int free_scripts) { if (free_scripts) diff --git a/src/client/component/gsc.hpp b/src/client/component/gsc.hpp index 727722fd..964e6a1f 100644 --- a/src/client/component/gsc.hpp +++ b/src/client/component/gsc.hpp @@ -1,6 +1,6 @@ #pragma once -#include "game/game.hpp" +#include "game/scripting/script_value.hpp" namespace gsc { @@ -15,6 +15,8 @@ namespace gsc void load_main_handles(); void load_init_handles(); + scripting::script_value get_argument(int index); + namespace function { void add(const std::string& name, builtin_function function); diff --git a/src/client/component/io.cpp b/src/client/component/io.cpp new file mode 100644 index 00000000..d663c750 --- /dev/null +++ b/src/client/component/io.cpp @@ -0,0 +1,220 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include "console.hpp" +#include "gsc.hpp" +#include "logfile.hpp" +#include "scheduler.hpp" + +#include "gsc.hpp" + +#include +#include +#include + +namespace io +{ + namespace + { + void replace(std::string& str, const std::string& from, const std::string& to) + { + const auto start_pos = str.find(from); + + if (start_pos == std::string::npos) + { + return; + } + + str.replace(start_pos, from.length(), to); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + gsc::function::add("print", []() + { + const auto num = game::Scr_GetNumParam(); + std::string buffer{}; + + for (auto i = 0; i < num; i++) + { + const auto str = game::Scr_GetString(i); + buffer.append(str); + buffer.append("\t"); + } + +#ifdef DEBUG + console::debug("[SCRIPT] %s\n", buffer.data()); +#else + console::info("[SCRIPT] %s\n", buffer.data()); +#endif + }); + + gsc::function::add("assert", []() + { + const auto expr = gsc::get_argument(0).as(); + if (!expr) + { + throw std::runtime_error("assert fail"); + } + }); + + gsc::function::add("assertex", []() + { + const auto expr = gsc::get_argument(0).as(); + if (!expr) + { + const auto error = gsc::get_argument(1).as(); + throw std::runtime_error(error); + } + }); + + gsc::function::add("replacefunc", []() + { + const auto what = gsc::get_argument(0).get_raw(); + const auto with = gsc::get_argument(1).get_raw(); + + if (what.type != game::SCRIPT_FUNCTION) + { + throw std::runtime_error("replaceFunc: parameter 1 must be a function"); + return; + } + + if (with.type != game::SCRIPT_FUNCTION) + { + throw std::runtime_error("replaceFunc: parameter 2 must be a function"); + return; + } + + logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); + }); + + gsc::function::add("toupper", []() + { + const auto string = gsc::get_argument(0).as(); + game::Scr_AddString(utils::string::to_upper(string).data()); + }); + + gsc::function::add("logprint", []() + { + std::string buffer{}; + + const auto params = game::Scr_GetNumParam(); + for (auto i = 0; i < params; i++) + { + const auto string = game::Scr_GetString(i); + buffer.append(string); + } + + game::G_LogPrintf("%s", buffer.data()); + }); + + gsc::function::add("va", []() + { + auto fmt = gsc::get_argument(0).as(); + + const auto params = game::Scr_GetNumParam(); + for (auto i = 1; i < params; i++) + { + const auto arg = gsc::get_argument(i).to_string(); + replace(fmt, "%s", arg); + } + + game::Scr_AddString(fmt.data()); + }); + + gsc::function::add("fileexists", []() + { + const auto path = gsc::get_argument(0).as(); + + game::Scr_AddBool(utils::io::file_exists(path)); + }); + + gsc::function::add("writefile", []() + { + const auto path = gsc::get_argument(0).as(); + const auto data = gsc::get_argument(1).as(); + + const auto params = game::Scr_GetNumParam(); + + auto append = false; + if (params > 2) + { + append = gsc::get_argument(2).as(); + } + + game::Scr_AddBool(utils::io::write_file(path, data, append)); + }); + + gsc::function::add("readfile", []() + { + const auto path = gsc::get_argument(0).as(); + game::Scr_AddString(utils::io::read_file(path).data()); + }); + + /* + gsc::function::add("filesize", []() + { + const auto path = gsc::get_argument(0).as(); + + // return type is size_t, so we probably need to find a way to do that :P maybe just make it a string? lmao + game::Scr_AddInt(utils::io::file_size(path)); + }); + */ + + gsc::function::add("createdirectory", []() + { + const auto path = gsc::get_argument(0).as(); + game::Scr_AddBool(utils::io::create_directory(path)); + }); + + gsc::function::add("directoryexists", []() + { + const auto path = gsc::get_argument(0).as(); + game::Scr_AddBool(utils::io::directory_exists(path)); + }); + + gsc::function::add("directoryisempty", []() + { + const auto path = gsc::get_argument(0).as(); + game::Scr_AddBool(utils::io::directory_is_empty(path)); + }); + + /* + gsc::function::add("listfiles", []() + { + const auto path = gsc::get_argument(0).as(); + const auto files = utils::io::list_files(path); + + scripting::array array{}; + for (const auto& file : files) + { + array.push(file); + } + + return array; + }); + */ + + gsc::function::add("copyfolder", []() + { + const auto source = gsc::get_argument(0).as(); + const auto target = gsc::get_argument(1).as(); + utils::io::copy_folder(source, target); + }); + + gsc::function::add("removefile", []() + { + const auto path = gsc::get_argument(0).as(); + game::Scr_AddBool(utils::io::remove_file(path)); + }); + } + }; +} + +REGISTER_COMPONENT(io::component) diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 0ed6957e..e55f206e 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -152,6 +152,8 @@ namespace game unsigned int paramcount)> VM_Execute{0x3C9E50, 0x510EB0}; WEAK symbol Scr_AddString{0x3C7B20, 0x50EC50}; + WEAK symbol Scr_AddInt{0x3C7A40, 0x50EB70}; + WEAK symbol Scr_AddBool{0x3C77C0, 0x50E8F0}; WEAK symbol Scr_NotifyId{0x3C92E0, 0x510340}; @@ -191,10 +193,10 @@ namespace game WEAK symbol DB_LoadXAssets{0x1F31E0, 0x397500}; WEAK symbol DB_IsLocalized{0x1F23C0, 0x396790}; - WEAK symbol LUI_OpenMenu{0x3F20A0, 0x1E1210}; - WEAK symbol LUI_LeaveMenuByName{0xF6D00, 0x26BE80}; + WEAK symbol LUI_OpenMenu{0x3F20A0, 0x1E1210}; + WEAK symbol LUI_LeaveMenuByName{0xF6D00, 0x26BE80}; WEAK symbol LUI_EnterCriticalSection{0xF19A0, 0x2669B0}; WEAK symbol LUI_LeaveCriticalSection{0xF6C40, 0x26BDC0};