From 9512add5ad2051c96fbe4422d783f39fdd510788 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Fri, 10 Jun 2022 02:05:28 +0200 Subject: [PATCH] Some changes + support STL containers as argument types --- src/component/gsc.cpp | 100 ++++++++++++++++++---------- src/component/gsc.hpp | 28 +++++--- src/game/scripting/array.cpp | 4 +- src/game/scripting/array.hpp | 2 +- src/game/scripting/script_value.cpp | 20 +++++- src/game/scripting/script_value.hpp | 78 +++++++++++++++++++--- 6 files changed, 168 insertions(+), 64 deletions(-) diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index cc43af5..77c1e2a 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -13,22 +13,22 @@ #include #include -namespace scripting +namespace gsc { namespace { utils::hook::detour get_function_hook; utils::hook::detour get_method_hook; - std::unordered_map> functions; - std::unordered_map> methods; + std::unordered_map> functions; + std::unordered_map> methods; std::unordered_map function_wraps; std::unordered_map method_wraps; - std::vector get_arguments() + std::vector get_arguments() { - std::vector args; + std::vector args; for (auto i = 0; static_cast(i) < game::scr_VmPub->outparamcount; i++) { @@ -46,7 +46,7 @@ namespace scripting game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER); } - push_value(value); + scripting::push_value(value); } void call_function(const char* name) @@ -82,10 +82,10 @@ namespace scripting try { - const entity entity = game::Scr_GetEntityId( + const scripting::entity entity = game::Scr_GetEntityId( game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0); - std::vector args_{}; + std::vector args_{}; args_.push_back(entity); for (const auto& arg : args) { @@ -134,6 +134,11 @@ namespace scripting script_function get_function_stub(const char** name, int* type) { + if (*name == "print"s) + { + MessageBoxA(nullptr, "", "", 0); + } + if (function_wraps.find(*name) != function_wraps.end()) { return reinterpret_cast(function_wraps[*name]); @@ -169,26 +174,32 @@ namespace scripting utils::hook::detour scr_settings_hook; void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst) { - scr_settings_hook.invoke(0x55D010, developer_script, developer_script, 0, inst); + scr_settings_hook.invoke(SELECT_VALUE(0x0, 0x55D010), developer_script, developer_script, 0, inst); } } - template - void add_function(const std::string& name, F f) + namespace function { - const auto wrap = wrap_function(f); - functions[name] = wrap; - const auto call_wrap = wrap_function_call(name); - function_wraps[name] = call_wrap; + template + void add(const std::string& name, F f) + { + const auto wrap = wrap_function(f); + functions[name] = wrap; + const auto call_wrap = wrap_function_call(name); + function_wraps[name] = call_wrap; + } } - template - void add_method(const std::string& name, F f) + namespace method { - const auto wrap = wrap_function(f); - methods[name] = wrap; - const auto call_wrap = wrap_method_call(name); - method_wraps[name] = call_wrap; + template + void add(const std::string& name, F f) + { + const auto wrap = wrap_function(f); + methods[name] = wrap; + const auto call_wrap = wrap_method_call(name); + method_wraps[name] = call_wrap; + } } class component final : public component_interface @@ -197,34 +208,51 @@ namespace scripting void post_unpack() override { // Don't com_error on gsc errors - utils::hook::nop(SELECT_VALUE(0, 0x4D9BB1), 5); - utils::hook::jump(0x568B90, print); + utils::hook::nop(SELECT_VALUE(0x0, 0x4D9BB1), 5); + utils::hook::jump(SELECT_VALUE(0x0, 0x568B90), print); scr_settings_hook.create(SELECT_VALUE(0x0, 0x55D010), scr_settings_stub); get_function_hook.create(SELECT_VALUE(0x0, 0x465E20), get_function_stub); get_method_hook.create(SELECT_VALUE(0x0, 0x555580), get_method_stub); - add_function("print_", [](const variadic_args& args) + function::add("print_", [](const scripting::variadic_args& va) { - for (const auto& arg : args) + for (const auto& arg : va) { printf("%s\t", arg.to_string().data()); } printf("\n"); }); - add_function("fileexists", utils::io::file_exists); - add_function("writefile", utils::io::write_file); - add_function("movefile", utils::io::move_file); - add_function("filesize", utils::io::file_size); - add_function("createdirectory", utils::io::create_directory); - add_function("directoryexists", utils::io::directory_exists); - add_function("directoryisempty", utils::io::directory_is_empty); - add_function("listfiles", utils::io::list_files); - add_function("removefile", utils::io::remove_file); - add_function("readfile", static_cast(utils::io::read_file)); + function::add("writefile", [](const std::string& file, const std::string& data, + const scripting::variadic_args& va) + { + auto append = false; + + if (va.size() > 0) + { + append = va[0]; + } + + return utils::io::write_file(file, data, append); + }); + + function::add("appendfile", [](const std::string& file, const std::string& data) + { + return utils::io::write_file(file, data, true); + }); + + function::add("fileexists", utils::io::file_exists); + function::add("movefile", utils::io::move_file); + function::add("filesize", utils::io::file_size); + function::add("createdirectory", utils::io::create_directory); + function::add("directoryexists", utils::io::directory_exists); + function::add("directoryisempty", utils::io::directory_is_empty); + function::add("listfiles", utils::io::list_files); + function::add("removefile", utils::io::remove_file); + function::add("readfile", static_cast(utils::io::read_file)); } }; } -REGISTER_COMPONENT(scripting::component) \ No newline at end of file +REGISTER_COMPONENT(gsc::component) diff --git a/src/component/gsc.hpp b/src/component/gsc.hpp index 49421e3..551413f 100644 --- a/src/component/gsc.hpp +++ b/src/component/gsc.hpp @@ -2,24 +2,24 @@ #include "game/scripting/array.hpp" #include "game/scripting/execution.hpp" -namespace scripting +namespace gsc { using script_function = void(*)(game::scr_entref_t); template auto wrap_function(const std::function& f, std::index_sequence) { - return [f]([[maybe_unused]] const function_arguments& args) + return [f]([[maybe_unused]] const scripting::function_arguments& args) { f(args[I]...); - return script_value{}; + return scripting::script_value{}; }; } template - auto wrap_function(const std::function& f, std::index_sequence) + auto wrap_function(const std::function& f, std::index_sequence) { - return [f]([[maybe_unused]] const function_arguments& args) + return [f]([[maybe_unused]] const scripting::function_arguments& args) { return f(args[I]...); }; @@ -28,9 +28,9 @@ namespace scripting template auto wrap_function(const std::function& f, std::index_sequence) { - return [f]([[maybe_unused]] const function_arguments& args) + return [f]([[maybe_unused]] const scripting::function_arguments& args) { - return script_value{f(args[I]...)}; + return scripting::script_value{f(args[I]...)}; }; } @@ -46,9 +46,15 @@ namespace scripting return wrap_function(std::function(f)); } - template - void add_function(const std::string& name, F f); + namespace function + { + template + void add(const std::string& name, F f); + } - template - void add_method(const std::string& name, F f); + namespace method + { + template + void add(const std::string& name, F f); + } } diff --git a/src/game/scripting/array.cpp b/src/game/scripting/array.cpp index fb0f430..335ab4b 100644 --- a/src/game/scripting/array.cpp +++ b/src/game/scripting/array.cpp @@ -143,9 +143,9 @@ namespace scripting return result; } - unsigned int array::size() const + int array::size() const { - return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_); + return static_cast(game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_)); } unsigned int array::push(const script_value& value) const diff --git a/src/game/scripting/array.hpp b/src/game/scripting/array.hpp index 9edc697..34b3e19 100644 --- a/src/game/scripting/array.hpp +++ b/src/game/scripting/array.hpp @@ -28,7 +28,7 @@ namespace scripting array& operator=(array&& other) noexcept; std::vector get_keys() const; - unsigned int size() const; + int size() const; unsigned int push(const script_value&) const; void erase(const unsigned int) const; diff --git a/src/game/scripting/script_value.cpp b/src/game/scripting/script_value.cpp index bd4ff5c..08b739e 100644 --- a/src/game/scripting/script_value.cpp +++ b/src/game/scripting/script_value.cpp @@ -330,6 +330,18 @@ namespace scripting * **********************************************/ + template <> + bool script_value::is() const + { + return true; + } + + template <> + script_value script_value::get() const + { + return this; + } + const game::VariableValue& script_value::get_raw() const { return this->value_.get(); @@ -372,11 +384,13 @@ namespace scripting return this->type_name(); } - function_argument::function_argument(const arguments& args, const script_value& value, const int index) + function_argument::function_argument(const arguments& args, const script_value& value, const int index, const bool exists) : values_(args) - , value_(value) - , index_(index) + , value_(value) + , index_(index) + , exists_(exists) { + } function_arguments::function_arguments(const arguments& values) diff --git a/src/game/scripting/script_value.hpp b/src/game/scripting/script_value.hpp index d58c7c1..5c2e8cd 100644 --- a/src/game/scripting/script_value.hpp +++ b/src/game/scripting/script_value.hpp @@ -144,7 +144,7 @@ namespace scripting { const auto type = get_typename(this->get_raw()); const auto c_type = get_c_typename(); - throw std::runtime_error(std::string("has type '" + type + "' but should be '" + c_type + "'")); + throw std::runtime_error(utils::string::va("has type '%s' but should be '%s'", type.data(), c_type.data())); } return get(); @@ -190,18 +190,22 @@ namespace scripting }; - class variadic_args : public arguments - { - }; + class function_argument; + using variadic_args = std::vector; class function_argument { public: - function_argument(const arguments& args, const script_value& value, const int index); + function_argument(const arguments& args, const script_value& value, const int index, const bool exists); template T as() const { + if (!this->exists_) + { + throw std::runtime_error(utils::string::va("parameter %d does not exist", this->index_)); + } + try { return this->value_.as(); @@ -209,7 +213,7 @@ namespace scripting catch (const std::exception& e) { throw std::runtime_error(utils::string::va("parameter %d %s", - this->index_ + 1, e.what())); + this->index_, e.what())); } } @@ -219,17 +223,68 @@ namespace scripting variadic_args args{}; for (auto i = this->index_; i < static_cast(this->values_.size()); i++) { - args.push_back(this->values_[i]); + args.push_back({this->values_, this->values_[i], i, true}); } return args; } - template <> - script_value as() const + std::string to_string() const + { + return this->value_.to_string(); + } + + std::string type_name() const + { + return this->value_.type_name(); + } + + script_value get_raw() const { return this->value_; } + operator variadic_args() const + { + variadic_args args{}; + for (auto i = this->index_; i < static_cast(this->values_.size()); i++) + { + args.push_back({this->values_, this->values_[i], i, true}); + } + return args; + } + + template class C, class T, class ArrayType = array> + operator C>() const + { + const auto container_type = get_c_typename>>(); + if (!this->value_.is()) + { + const auto type = get_typename(this->value_.get_raw()); + + throw std::runtime_error(utils::string::va("has type '%s' but should be '%s'", + type.data(), + container_type.data() + )); + } + + C> container{}; + const auto array = this->value_.as(); + for (auto i = 0; i < array.size(); i++) + { + try + { + container.push_back(array.get(i).as()); + } + catch (const std::exception& e) + { + throw std::runtime_error(utils::string::va("element %d of parameter %d of type '%s' %s", + i, this->index_, container_type.data(), e.what())); + } + } + + return container; + } + template operator T() const { @@ -240,6 +295,7 @@ namespace scripting arguments values_{}; script_value value_{}; int index_{}; + bool exists_{}; }; class function_arguments @@ -251,10 +307,10 @@ namespace scripting { if (index >= static_cast(values_.size())) { - throw std::runtime_error(utils::string::va("parameter %d does not exist", index)); + return {values_, {}, index, false}; } - return {values_, values_[index], index}; + return {values_, values_[index], index, true}; } private: arguments values_{};