Prepare scripting environment

This commit is contained in:
momo5502 2019-01-13 19:03:46 +01:00
parent e78dcdf21b
commit 508cc5d51e
9 changed files with 316 additions and 18 deletions

View File

@ -13,13 +13,61 @@ namespace game
MSG_ReadData_t MSG_ReadData;
RemoveRefToValue_t RemoveRefToValue;
Sys_ShowConsole_t Sys_ShowConsole;
VM_Notify_t VM_Notify;
int* cmd_args;
int* cmd_argc;
const char*** cmd_argv;
}
short* scrVarGlob;
char** scrMemTreePub;
void AddRefToValue(VariableValue* value)
{
if (value->type == SCRIPT_OBJECT)
{
++scrVarGlob[4 * value->u.entityId];
}
else if (value->type == SCRIPT_STRING)
{
static const auto size = is_sp() ? 16 : 12;
const auto ref_count = reinterpret_cast<unsigned volatile *>(*scrMemTreePub + size * value
->u.stringValue);
InterlockedIncrement(ref_count);
}
else if (value->type == SCRIPT_VECTOR)
{
if (!*PBYTE(value->u.vectorValue - 1))
{
++*PWORD(value->u.vectorValue - 4);
}
}
}
scr_entref_t Scr_GetEntityIdRef(const unsigned int id)
{
static auto class_array = reinterpret_cast<DWORD*>(SELECT_VALUE(0x19AFC84, 0x1E72184, 0x1D3C804));
static auto ent_array = reinterpret_cast<WORD*>(SELECT_VALUE(0x19AFC82, 0x1E72182, 0x1D3C802));
scr_entref_t result{};
result.raw.classnum = static_cast<unsigned short>(class_array[2 * id]) >> 8;
result.raw.entnum = ent_array[4 * id];
return result;
}
const char* SL_ConvertToString(unsigned int stringValue)
{
if (!stringValue) return nullptr;
static const auto size = is_sp() ? 16 : 12;
return *scrMemTreePub + size * stringValue + 4;
}
}
launcher::mode mode = launcher::mode::none;
@ -50,10 +98,17 @@ namespace game
native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0));
native::RemoveRefToValue = native::RemoveRefToValue_t(SELECT_VALUE(0x477EA0, 0x565730, 0x4E8A40));
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0));
native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450));
native::cmd_args = reinterpret_cast<int*>(SELECT_VALUE(0x1750750, 0x1C978D0, 0x1B455F8));
native::cmd_argc = reinterpret_cast<int*>(SELECT_VALUE(0x1750794, 0x1C97914, 0x1B4563C));
native::cmd_argv = reinterpret_cast<const char***>(SELECT_VALUE(0x17507B4, 0x1C97934, 0x1B4565C));
native::scrVarGlob = reinterpret_cast<short*>(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800));
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4));
}
}

View File

@ -18,15 +18,30 @@ namespace game
typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync);
extern DB_LoadXAssets_t DB_LoadXAssets;
typedef void(*MSG_ReadData_t)(msg_t *msg, void *data, int len);
typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len);
extern MSG_ReadData_t MSG_ReadData;
typedef void (*RemoveRefToValue_t)(scriptType_e type, VariableUnion u);
extern RemoveRefToValue_t RemoveRefToValue;
typedef void (*Sys_ShowConsole_t)();
extern Sys_ShowConsole_t Sys_ShowConsole;
typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top);
extern VM_Notify_t VM_Notify;
extern int* cmd_args;
extern int* cmd_argc;
extern const char*** cmd_argv;
extern short* scrVarGlob;
extern char** scrMemTreePub;
void AddRefToValue(VariableValue* value);
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
const char* SL_ConvertToString(unsigned int stringValue);
}
bool is_mp();

View File

@ -399,5 +399,56 @@ namespace game
int allocFlags;
int freeFlags;
};
struct scr_entref_raw
{
unsigned __int16 entnum;
unsigned __int16 classnum;
};
union scr_entref_t
{
unsigned int val;
scr_entref_raw raw;
};
enum scriptType_e
{
SCRIPT_NONE = 0,
SCRIPT_OBJECT = 1,
SCRIPT_STRING = 2,
SCRIPT_VECTOR = 4,
SCRIPT_FLOAT = 5,
SCRIPT_INTEGER = 6,
SCRIPT_END = 8, // Custom
};
struct VariableStackBuffer
{
const char* pos;
unsigned __int16 size;
unsigned __int16 bufLen;
unsigned __int16 localId;
char time;
char buf[1];
};
union VariableUnion
{
int intValue;
float floatValue;
unsigned int stringValue;
const float* vectorValue;
const char* codePosValue;
unsigned int pointerValue;
VariableStackBuffer* stackValue;
unsigned int entityId;
};
struct VariableValue
{
VariableUnion u;
scriptType_e type;
};
}
}

View File

@ -0,0 +1,37 @@
#include <std_include.hpp>
#include "loader/module_loader.hpp"
#include "notification.hpp"
#include "utils/hook.hpp"
void notification::post_load()
{
utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), vm_notify_stub, HOOK_CALL).install()->quick();
utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), vm_notify_stub, HOOK_CALL).install()->quick();
if (game::is_sp())
{
utils::hook(0x610970, vm_notify_stub, HOOK_JUMP).install()->quick();
}
}
void notification::vm_notify_stub(const unsigned int notify_id, const unsigned short type,
game::native::VariableValue* stack)
{
event e;
e.name = game::native::SL_ConvertToString(type);
e.entity = game::native::Scr_GetEntityIdRef(notify_id);
for (auto value = stack; value->type != game::native::SCRIPT_END; --value)
{
e.arguments.emplace_back(*value);
}
if(!e.arguments.empty())
{
printf("");
}
game::native::VM_Notify(notify_id, type, stack);
}
REGISTER_MODULE(notification)

View File

@ -0,0 +1,20 @@
#pragma once
#include "loader/module_loader.hpp"
#include "scripting.hpp"
class notification final : public module
{
public:
class event final
{
public:
std::string name;
game::native::scr_entref_t entity;
std::vector<scripting::variable> arguments;
};
void post_load() override;
private:
static void vm_notify_stub(unsigned int notify_id, unsigned short type, game::native::VariableValue* stack);
};

View File

@ -1,26 +1,101 @@
#include <std_include.hpp>
#include "loader/module_loader.hpp"
#include "game/game.hpp"
#include "scripting.hpp"
class scripting final : public module
utils::hook scripting::start_hook_;
utils::hook scripting::stop_hook_;
std::mutex scripting::mutex_;
std::vector<std::function<void()>> scripting::start_callbacks_;
std::vector<std::function<void()>> scripting::stop_callbacks_;
scripting::variable::variable(game::native::VariableValue value) : value_(value)
{
public:
void post_load() override
{
this->chai_.add(chaiscript::fun(&function), "function");
game::native::AddRefToValue(&value);
}
double d = this->chai_.eval<double>("function(3, 4.75);");
printf("Result: %f", d);
scripting::variable::~variable()
{
game::native::RemoveRefToValue(this->value_.type, this->value_.u);
}
scripting::variable::operator game::native::VariableValue() const
{
return this->value_;
}
void scripting::post_load()
{
start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), []()
{
start_execution();
static_cast<void(*)()>(start_hook_.get_original())();
}, HOOK_CALL)->install()->quick();
stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), []()
{
stop_execution();
static_cast<void(*)()>(stop_hook_.get_original())();
}, HOOK_CALL)->install()->quick();
on_start([this]()
{
this->chai_ = std::make_unique<chaiscript::ChaiScript>();
});
on_stop([this]()
{
this->chai_ = {};
});
}
void scripting::pre_destroy()
{
this->chai_ = {};
start_callbacks_.clear();
stop_callbacks_.clear();
}
void scripting::on_start(const std::function<void()>& callback)
{
std::lock_guard _(mutex_);
start_callbacks_.push_back(callback);
}
void scripting::on_stop(const std::function<void()>& callback)
{
std::lock_guard _(mutex_);
stop_callbacks_.push_back(callback);
}
void scripting::start_execution()
{
std::vector<std::function<void()>> copy;
{
std::lock_guard _(mutex_);
copy = start_callbacks_;
}
private:
chaiscript::ChaiScript chai_;
static double function(int i, double j)
for (const auto& callback : copy)
{
return i * j;
callback();
}
}
void scripting::stop_execution()
{
std::vector<std::function<void()>> copy;
{
std::lock_guard _(mutex_);
copy = stop_callbacks_;
}
};
for (const auto& callback : copy)
{
callback();
}
}
REGISTER_MODULE(scripting)

39
src/module/scripting.hpp Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "loader/module_loader.hpp"
#include "game/game.hpp"
#include "utils/hook.hpp"
class scripting final : public module
{
public:
class variable final
{
public:
variable(game::native::VariableValue value);
~variable();
operator game::native::VariableValue() const;
private:
game::native::VariableValue value_;
};
void post_load() override;
void pre_destroy() override;
static void on_start(const std::function<void()>& callback);
static void on_stop(const std::function<void()>& callback);
private:
std::unique_ptr<chaiscript::ChaiScript> chai_;
static utils::hook start_hook_;
static utils::hook stop_hook_;
static std::mutex mutex_;
static std::vector<std::function<void()>> start_callbacks_;
static std::vector<std::function<void()>> stop_callbacks_;
static void start_execution();
static void stop_execution();
};

View File

@ -156,6 +156,11 @@ namespace utils
return this->place_;
}
void* hook::get_original() const
{
return this->original_;
}
void hook::nop(void* place, const size_t length)
{
DWORD old_protect;

View File

@ -80,6 +80,7 @@ namespace utils
hook* uninstall(bool unprotect = true);
void* get_address() const;
void* get_original() const;
void quick();
static bool iat(nt::module module, const std::string& target_module, const std::string& process, void* stub);