From 7077f4b732b455c45a24892c47a979c195afd072 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Sat, 11 Jun 2022 20:29:52 +0200 Subject: [PATCH] Add some functions --- src/component/gsc.cpp | 25 +++- src/component/io.cpp | 9 -- src/component/string.cpp | 188 ++++++++++++++++++++++++++++ src/game/scripting/script_value.cpp | 18 ++- src/game/scripting/script_value.hpp | 30 +++-- src/utils/hook.hpp | 6 + 6 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 src/component/string.cpp diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index fe4a59b..a3cae8f 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -205,12 +205,33 @@ namespace gsc utils::hook::jump(SELECT_VALUE(0x5DFC40, 0x568B90), print); scr_settings_hook.create(SELECT_VALUE(0x4CEEA0, 0x55D010), scr_settings_stub); - get_function_hook.create(SELECT_VALUE(0x52BF80, 0x465E20), get_function_stub); - get_method_hook.create(SELECT_VALUE(0x68A640, 0x555580), get_method_stub); + + get_function_hook.create(utils::hook::extract(SELECT_VALUE(0x8A02FB, 0x8DE11B) + 1), get_function_stub); + get_method_hook.create(utils::hook::extract(SELECT_VALUE(0x8A052E, 0x8DE34E) + 1), get_method_stub); // \n******* script runtime error *******\n%s\n utils::hook::set(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n'); utils::hook::set(SELECT_VALUE(0x9FC5C0 + 41, 0xAABA68 + 41), '\0'); + + gsc::function::add("array", [](const scripting::variadic_args& va) + { + scripting::array array{}; + + for (const auto& arg : va) + { + array.push(arg); + } + + return array; + }); + + const auto typeof = [](const scripting::script_value& value) + { + return value.type_name(); + }; + + gsc::function::add("typeof", typeof); + gsc::function::add("type", typeof); } }; } diff --git a/src/component/io.cpp b/src/component/io.cpp index 2c12953..e5458ba 100644 --- a/src/component/io.cpp +++ b/src/component/io.cpp @@ -15,15 +15,6 @@ namespace io public: void post_unpack() override { - gsc::function::add("print_", [](const scripting::variadic_args& va) - { - for (const auto& arg : va) - { - printf("%s\t", arg.to_string().data()); - } - printf("\n"); - }); - gsc::function::add("writefile", [](const std::string& file, const std::string& data, const scripting::variadic_args& va) { diff --git a/src/component/string.cpp b/src/component/string.cpp new file mode 100644 index 0000000..9306533 --- /dev/null +++ b/src/component/string.cpp @@ -0,0 +1,188 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/structs.hpp" +#include "game/game.hpp" + +#include "gsc.hpp" + +#include +#include + +// lua/lstrlib.c +#define MAX_FORMAT 32 +#define L_FMTFLAGSF "-+#0 " +#define L_FMTFLAGSX "-#0" +#define L_FMTFLAGSI "-+0 " +#define L_FMTFLAGSU "-0" +#define L_FMTFLAGSC "-" + +namespace string +{ + namespace + { + // lua/lstrlib.c + const char* getformat(const char* strfrmt, char* form) + { + const auto len = strspn(strfrmt, L_FMTFLAGSF "123456789.") + 1; + if (len >= MAX_FORMAT - 10) + { + throw std::runtime_error("invalid format (too long)"); + } + + *(form++) = '%'; + std::memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; + } + + // lua/lstrlib.c + const char* get_2_digits(const char* s) + { + if (isdigit(static_cast(*s))) + { + s++; + if (isdigit(static_cast(*s))) + { + s++; + } + } + + return s; + } + + // lua/lstrlib.c + void check_format(const char* form, const char* flags, int precision) + { + const char* spec = form + 1; + spec += std::strspn(spec, flags); + if (*spec != '0') + { + spec = get_2_digits(spec); + if (*spec == '.' && precision) + { + spec++; + spec = get_2_digits(spec); + } + } + if (!std::isalpha(static_cast(*spec))) + { + throw std::runtime_error(utils::string::va("invalid conversion specification: '%s'", form)); + } + } + + // partially lua/lstrlib.c + std::string format_string(const std::string& fmt, const scripting::variadic_args& va) + { + std::string buffer{}; + size_t va_index{}; + const char* strfrmt = fmt.data(); + const char* strfrmt_end = strfrmt + fmt.size(); + + while (strfrmt < strfrmt_end) + { + if (*strfrmt != '%') + { + buffer.push_back(*strfrmt++); + } + else if (*++strfrmt == '%') + { + buffer.push_back(*strfrmt++); + } + else + { + char form[MAX_FORMAT]{}; + const char* flags = ""; + strfrmt = getformat(strfrmt, form); + + switch (*strfrmt++) + { + case 'd': + case 'i': + flags = L_FMTFLAGSI; + goto intcase; + case 'u': + case 'p': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': + case 'x': + case 'X': + flags = L_FMTFLAGSX; + intcase: + { + check_format(form, flags, 1); + const auto value = va[va_index].as(); + buffer.append(utils::string::va(form, value)); + va_index++; + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + { + check_format(form, L_FMTFLAGSF, 1); + const auto value = va[va_index].as(); + buffer.append(utils::string::va(form, value)); + va_index++; + break; + } + case 'c': + { + const auto value = va[va_index].as(); + check_format(form, L_FMTFLAGSC, 0); + buffer.append(utils::string::va(form, static_cast(value))); + va_index++; + break; + } + case 's': + { + const auto str = va[va_index].as(); + buffer.append(str); + va_index++; + break; + } + default: + { + throw std::runtime_error(utils::string::va("invalid conversion '%s' to 'format'", form)); + } + } + } + } + + return buffer; + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + gsc::function::add("va", format_string); + gsc::function::add("format", format_string); + gsc::function::add("formatstring", format_string); + gsc::function::add("formatstr", format_string); + gsc::function::add("sprintf", format_string); + + gsc::function::add("printf", [](const std::string& fmt, const scripting::variadic_args& va) + { + printf("%s", format_string(fmt, va).data()); + }); + + gsc::function::add("print", [](const scripting::variadic_args& va) + { + for (const auto& arg : va) + { + printf("%s\t", arg.to_string().data()); + } + printf("\n"); + }); + } + }; +} + +REGISTER_COMPONENT(string::component) diff --git a/src/game/scripting/script_value.cpp b/src/game/scripting/script_value.cpp index 39e7d70..a8b3099 100644 --- a/src/game/scripting/script_value.cpp +++ b/src/game/scripting/script_value.cpp @@ -384,7 +384,7 @@ namespace scripting return this->type_name(); } - function_argument::function_argument(const arguments& args, const script_value& value, const int index, const bool exists) + function_argument::function_argument(const arguments& args, const script_value& value, const size_t index, const bool exists) : script_value(value) , values_(args) , index_(index) @@ -397,4 +397,20 @@ namespace scripting : values_(values) { } + + variadic_args::variadic_args(const size_t begin) + : std::vector({}) + , begin_(begin) + { + } + + function_argument variadic_args::operator[](size_t index) const + { + if (index >= this->size()) + { + throw std::runtime_error(utils::string::va("parameter %d does not exist", this->begin_ + index)); + } + + return std::vector::operator[](index); + } } diff --git a/src/game/scripting/script_value.hpp b/src/game/scripting/script_value.hpp index b9bfc7b..68fd79e 100644 --- a/src/game/scripting/script_value.hpp +++ b/src/game/scripting/script_value.hpp @@ -230,12 +230,21 @@ public: \ }; class function_argument; - using variadic_args = std::vector; + + class variadic_args : public std::vector + { + public: + variadic_args(const size_t begin); + + function_argument operator[](size_t index) const; + private: + size_t begin_; + }; class function_argument : public script_value { public: - function_argument(const arguments& args, const script_value& value, const int index, const bool exists); + function_argument(const arguments& args, const script_value& value, const size_t index, const bool exists); template T as() const @@ -259,8 +268,8 @@ public: \ template <> variadic_args as() const { - variadic_args args{}; - for (auto i = this->index_; i < static_cast(this->values_.size()); i++) + variadic_args args{this->index_}; + for (auto i = this->index_; i < this->values_.size(); i++) { args.push_back({this->values_, this->values_[i], i, true}); } @@ -269,12 +278,7 @@ public: \ 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; + return this->as(); } template class C, class T, class ArrayType = array> @@ -317,7 +321,7 @@ public: \ private: arguments values_{}; - int index_{}; + size_t index_{}; bool exists_{}; }; @@ -326,9 +330,9 @@ public: \ public: function_arguments(const arguments& values); - function_argument operator[](const int index) const + function_argument operator[](const size_t index) const { - if (index >= static_cast(values_.size())) + if (index >= values_.size()) { return {values_, {}, index, false}; } diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp index 8986745..48db009 100644 --- a/src/utils/hook.hpp +++ b/src/utils/hook.hpp @@ -102,6 +102,12 @@ namespace utils::hook return reinterpret_cast(data + offset + 4); } + template + T extract(const size_t address) + { + return extract(reinterpret_cast(address)); + } + template static void set(void* place, T value) {