Some changes + support STL containers as argument types

This commit is contained in:
Federico Cecchetto 2022-06-10 02:05:28 +02:00
parent cdd2c51568
commit 9512add5ad
6 changed files with 168 additions and 64 deletions

View File

@ -13,22 +13,22 @@
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
namespace scripting namespace gsc
{ {
namespace namespace
{ {
utils::hook::detour get_function_hook; utils::hook::detour get_function_hook;
utils::hook::detour get_method_hook; utils::hook::detour get_method_hook;
std::unordered_map<std::string, std::function<script_value(const function_arguments& args)>> functions; std::unordered_map<std::string, std::function<scripting::script_value(const scripting::function_arguments& args)>> functions;
std::unordered_map<std::string, std::function<script_value(const function_arguments& args)>> methods; std::unordered_map<std::string, std::function<scripting::script_value(const scripting::function_arguments& args)>> methods;
std::unordered_map<std::string, void*> function_wraps; std::unordered_map<std::string, void*> function_wraps;
std::unordered_map<std::string, void*> method_wraps; std::unordered_map<std::string, void*> method_wraps;
std::vector<script_value> get_arguments() std::vector<scripting::script_value> get_arguments()
{ {
std::vector<script_value> args; std::vector<scripting::script_value> args;
for (auto i = 0; static_cast<unsigned int>(i) < game::scr_VmPub->outparamcount; i++) for (auto i = 0; static_cast<unsigned int>(i) < game::scr_VmPub->outparamcount; i++)
{ {
@ -46,7 +46,7 @@ namespace scripting
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER); game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
} }
push_value(value); scripting::push_value(value);
} }
void call_function(const char* name) void call_function(const char* name)
@ -82,10 +82,10 @@ namespace scripting
try try
{ {
const entity entity = game::Scr_GetEntityId( const scripting::entity entity = game::Scr_GetEntityId(
game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0); game::SCRIPTINSTANCE_SERVER, entref.entnum, entref.classnum, 0);
std::vector<script_value> args_{}; std::vector<scripting::script_value> args_{};
args_.push_back(entity); args_.push_back(entity);
for (const auto& arg : args) for (const auto& arg : args)
{ {
@ -134,6 +134,11 @@ namespace scripting
script_function get_function_stub(const char** name, int* type) 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()) if (function_wraps.find(*name) != function_wraps.end())
{ {
return reinterpret_cast<script_function>(function_wraps[*name]); return reinterpret_cast<script_function>(function_wraps[*name]);
@ -169,26 +174,32 @@ namespace scripting
utils::hook::detour scr_settings_hook; utils::hook::detour scr_settings_hook;
void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst) void scr_settings_stub(int /*developer*/, int developer_script, int /*abort_on_error*/, int inst)
{ {
scr_settings_hook.invoke<void>(0x55D010, developer_script, developer_script, 0, inst); scr_settings_hook.invoke<void>(SELECT_VALUE(0x0, 0x55D010), developer_script, developer_script, 0, inst);
} }
} }
template <typename F> namespace function
void add_function(const std::string& name, F f)
{ {
const auto wrap = wrap_function(f); template <typename F>
functions[name] = wrap; void add(const std::string& name, F f)
const auto call_wrap = wrap_function_call(name); {
function_wraps[name] = call_wrap; const auto wrap = wrap_function(f);
functions[name] = wrap;
const auto call_wrap = wrap_function_call(name);
function_wraps[name] = call_wrap;
}
} }
template <typename F> namespace method
void add_method(const std::string& name, F f)
{ {
const auto wrap = wrap_function(f); template <typename F>
methods[name] = wrap; void add(const std::string& name, F f)
const auto call_wrap = wrap_method_call(name); {
method_wraps[name] = call_wrap; 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 class component final : public component_interface
@ -197,34 +208,51 @@ namespace scripting
void post_unpack() override void post_unpack() override
{ {
// Don't com_error on gsc errors // Don't com_error on gsc errors
utils::hook::nop(SELECT_VALUE(0, 0x4D9BB1), 5); utils::hook::nop(SELECT_VALUE(0x0, 0x4D9BB1), 5);
utils::hook::jump(0x568B90, print); utils::hook::jump(SELECT_VALUE(0x0, 0x568B90), print);
scr_settings_hook.create(SELECT_VALUE(0x0, 0x55D010), scr_settings_stub); scr_settings_hook.create(SELECT_VALUE(0x0, 0x55D010), scr_settings_stub);
get_function_hook.create(SELECT_VALUE(0x0, 0x465E20), get_function_stub); get_function_hook.create(SELECT_VALUE(0x0, 0x465E20), get_function_stub);
get_method_hook.create(SELECT_VALUE(0x0, 0x555580), get_method_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("%s\t", arg.to_string().data());
} }
printf("\n"); printf("\n");
}); });
add_function("fileexists", utils::io::file_exists); function::add("writefile", [](const std::string& file, const std::string& data,
add_function("writefile", utils::io::write_file); const scripting::variadic_args& va)
add_function("movefile", utils::io::move_file); {
add_function("filesize", utils::io::file_size); auto append = false;
add_function("createdirectory", utils::io::create_directory);
add_function("directoryexists", utils::io::directory_exists); if (va.size() > 0)
add_function("directoryisempty", utils::io::directory_is_empty); {
add_function("listfiles", utils::io::list_files); append = va[0];
add_function("removefile", utils::io::remove_file); }
add_function("readfile", static_cast<std::string(*)(const std::string&)>(utils::io::read_file));
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<std::string(*)(const std::string&)>(utils::io::read_file));
} }
}; };
} }
REGISTER_COMPONENT(scripting::component) REGISTER_COMPONENT(gsc::component)

View File

@ -2,24 +2,24 @@
#include "game/scripting/array.hpp" #include "game/scripting/array.hpp"
#include "game/scripting/execution.hpp" #include "game/scripting/execution.hpp"
namespace scripting namespace gsc
{ {
using script_function = void(*)(game::scr_entref_t); using script_function = void(*)(game::scr_entref_t);
template <class... Args, std::size_t... I> template <class... Args, std::size_t... I>
auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>) auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>)
{ {
return [f]([[maybe_unused]] const function_arguments& args) return [f]([[maybe_unused]] const scripting::function_arguments& args)
{ {
f(args[I]...); f(args[I]...);
return script_value{}; return scripting::script_value{};
}; };
} }
template <class... Args, std::size_t... I> template <class... Args, std::size_t... I>
auto wrap_function(const std::function<script_value(Args...)>& f, std::index_sequence<I...>) auto wrap_function(const std::function<scripting::script_value(Args...)>& f, std::index_sequence<I...>)
{ {
return [f]([[maybe_unused]] const function_arguments& args) return [f]([[maybe_unused]] const scripting::function_arguments& args)
{ {
return f(args[I]...); return f(args[I]...);
}; };
@ -28,9 +28,9 @@ namespace scripting
template <typename R, class... Args, std::size_t... I> template <typename R, class... Args, std::size_t... I>
auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>) auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>)
{ {
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)); return wrap_function(std::function(f));
} }
template <typename F> namespace function
void add_function(const std::string& name, F f); {
template <typename F>
void add(const std::string& name, F f);
}
template <typename F> namespace method
void add_method(const std::string& name, F f); {
template <typename F>
void add(const std::string& name, F f);
}
} }

View File

@ -143,9 +143,9 @@ namespace scripting
return result; return result;
} }
unsigned int array::size() const int array::size() const
{ {
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_); return static_cast<int>(game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_));
} }
unsigned int array::push(const script_value& value) const unsigned int array::push(const script_value& value) const

View File

@ -28,7 +28,7 @@ namespace scripting
array& operator=(array&& other) noexcept; array& operator=(array&& other) noexcept;
std::vector<script_value> get_keys() const; std::vector<script_value> get_keys() const;
unsigned int size() const; int size() const;
unsigned int push(const script_value&) const; unsigned int push(const script_value&) const;
void erase(const unsigned int) const; void erase(const unsigned int) const;

View File

@ -330,6 +330,18 @@ namespace scripting
* *
**********************************************/ **********************************************/
template <>
bool script_value::is<script_value>() const
{
return true;
}
template <>
script_value script_value::get() const
{
return this;
}
const game::VariableValue& script_value::get_raw() const const game::VariableValue& script_value::get_raw() const
{ {
return this->value_.get(); return this->value_.get();
@ -372,11 +384,13 @@ namespace scripting
return this->type_name(); 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) : values_(args)
, value_(value) , value_(value)
, index_(index) , index_(index)
, exists_(exists)
{ {
} }
function_arguments::function_arguments(const arguments& values) function_arguments::function_arguments(const arguments& values)

View File

@ -144,7 +144,7 @@ namespace scripting
{ {
const auto type = get_typename(this->get_raw()); const auto type = get_typename(this->get_raw());
const auto c_type = get_c_typename<T>(); const auto c_type = get_c_typename<T>();
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<T>(); return get<T>();
@ -190,18 +190,22 @@ namespace scripting
}; };
class variadic_args : public arguments class function_argument;
{ using variadic_args = std::vector<function_argument>;
};
class function_argument class function_argument
{ {
public: 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 <typename T> template <typename T>
T as() const T as() const
{ {
if (!this->exists_)
{
throw std::runtime_error(utils::string::va("parameter %d does not exist", this->index_));
}
try try
{ {
return this->value_.as<T>(); return this->value_.as<T>();
@ -209,7 +213,7 @@ namespace scripting
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error(utils::string::va("parameter %d %s", 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{}; variadic_args args{};
for (auto i = this->index_; i < static_cast<int>(this->values_.size()); i++) for (auto i = this->index_; i < static_cast<int>(this->values_.size()); i++)
{ {
args.push_back(this->values_[i]); args.push_back({this->values_, this->values_[i], i, true});
} }
return args; return args;
} }
template <> std::string to_string() const
script_value as() const {
return this->value_.to_string();
}
std::string type_name() const
{
return this->value_.type_name();
}
script_value get_raw() const
{ {
return this->value_; return this->value_;
} }
operator variadic_args() const
{
variadic_args args{};
for (auto i = this->index_; i < static_cast<int>(this->values_.size()); i++)
{
args.push_back({this->values_, this->values_[i], i, true});
}
return args;
}
template <template<class, class> class C, class T, class ArrayType = array>
operator C<T, std::allocator<T>>() const
{
const auto container_type = get_c_typename<C<T, std::allocator<T>>>();
if (!this->value_.is<array>())
{
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<T, std::allocator<T>> container{};
const auto array = this->value_.as<ArrayType>();
for (auto i = 0; i < array.size(); i++)
{
try
{
container.push_back(array.get(i).as<T>());
}
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 <typename T> template <typename T>
operator T() const operator T() const
{ {
@ -240,6 +295,7 @@ namespace scripting
arguments values_{}; arguments values_{};
script_value value_{}; script_value value_{};
int index_{}; int index_{};
bool exists_{};
}; };
class function_arguments class function_arguments
@ -251,10 +307,10 @@ namespace scripting
{ {
if (index >= static_cast<int>(values_.size())) if (index >= static_cast<int>(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: private:
arguments values_{}; arguments values_{};