5 Commits

6 changed files with 223 additions and 62 deletions

View File

@@ -2,21 +2,22 @@
## Project Overview ## Project Overview
This project is a hook API for Star Wars: Empire at War: Forces of Corruption to expose custom Lua functions available for scripting. This project is a DLL hook for Star Wars: Empire at War: Forces of Corruption to add custom Lua functions available for scripting.
## Client and Debug Kits ## Client and Debug Kits
- StarWarsG.exe -- the binary shipped with the game for all clients. PDB unavailable. - StarWarsG.exe -- the binary shipped with the game for all clients. PDB unavailable.
- StarWarsI.exe -- the debug kit binary which comes with debug functionality but is too slow to run normally. PDB available. - StarWarsI.exe -- the debug kit binary which comes with debug functionality but is too slow to run normally. PDB available.
- Both binares are 64-bit Windows portable executables.
## DLL Hook ## DLL Hook
- Uses MinHook to create the DLL hook into the game via powerprof.dll - Uses MinHook to create the DLL hook into the game via powerprof.dll
- Using IDA Free to find addresses of function calls. - Using Ghidra to find addresses of function calls from StarWarsI and map them using Version Control to addresses in StarWarsG.
## Empire at War Lua Scripting Engine ## Empire at War Lua Scripting Engine
The source code for the Lua scripting engine in Empire at War was leaked at: https://github.com/PetroglyphGames/GlyphX-Reference. Use this as a guide for what Lua functions and variables are already available, and how the engine hooks Lua scripting into the rest of the game. No other engine source code is available. The source code for parts of the Lua scripting engine in Empire at War was leaked at and can be accessed [here](../GlyphX-Reference-master/Project1/Code/). Use this as a guide for what Lua functions and variables are already available, and how the engine hooks Lua scripting into the rest of the game.
## Adding a New Lua Function ## Adding a New Lua Function

View File

@@ -4,9 +4,21 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
// Offset of SmartPtr<GameObjectTypeClass> within GameObjectTypeWrapper. // SmartPtr offset is per-executable (0x70 StarWarsI, 0x28 StarWarsG).
// SmartPtr stores the raw pointer as its first member. // Read from g_rvas->got_object_offset at runtime.
static constexpr int OFFSET_OBJECT = 0x70;
// MSVC std::string layout (x64, MSVC 14.0+).
// Used to read the std::string at a known field offset within GameObjectTypeClass.
struct MSVCString {
union {
char buf[16]; // SSO buffer (up to 15 chars + null)
char* ptr; // heap pointer when capacity > 15
};
size_t size;
size_t capacity; // <= 15 means SSO, > 15 means heap
const char* c_str() const { return (capacity <= 15) ? buf : ptr; }
};
// MSVC std::wstring layout (x64, MSVC 14.0+). // MSVC std::wstring layout (x64, MSVC 14.0+).
// Used to read the wstring* returned by GameTextClass::Get. // Used to read the wstring* returned by GameTextClass::Get.
@@ -26,24 +38,21 @@ struct MSVCWString {
// On x64 this is the standard calling convention: this_wrapper in RCX, script in RDX, params in R8. // On x64 this is the standard calling convention: this_wrapper in RCX, script in RDX, params in R8.
static void* Lua_Get_Display_Name(void* this_wrapper, void* /*script*/, void* /*params*/) static void* Lua_Get_Display_Name(void* this_wrapper, void* /*script*/, void* /*params*/)
{ {
// 1. Get GameObjectTypeClass* from SmartPtr at offset 0x70 // 1. Get GameObjectTypeClass* from SmartPtr at offset
void* got_class = *(void**)((char*)this_wrapper + OFFSET_OBJECT); void* got_class = *(void**)((char*)this_wrapper + g_rvas->got_object_offset);
if (!got_class) if (!got_class)
return nullptr; return nullptr;
// 2. Get the text ID string (e.g. "TEXT_UNIT_X_WING") via Get_Text_Name_ID. // 2. Read the TextNameID string from the GameObjectTypeClass field
// The game's function constructs a std::string into our buffer via copy-ctor. MSVCString* text_name = (MSVCString*)((char*)got_class + g_text_name_id_offset);
// Assumes shared CRT (both DLL and game link to vcruntime140.dll dynamically). std::string text_id(text_name->c_str(), text_name->size);
std::string text_id;
pfn_get_text_name_id(got_class, &text_id);
// 3. Resolve the localized display name via TheGameText.Get(text_id, false). // 3. Resolve the localized display name via TheGameText.Get(text_id, false)
// Returns a pointer to an internal wstring (the translated text).
MSVCWString* display_wstr = (MSVCWString*)pfn_game_text_get(g_the_game_text, &text_id, false); MSVCWString* display_wstr = (MSVCWString*)pfn_game_text_get(g_the_game_text, &text_id, false);
if (!display_wstr) if (!display_wstr)
return nullptr; return nullptr;
// 4. Convert the wide display name to a narrow (UTF-8) string for Lua. // 4. Convert the wide display name to a narrow (UTF-8) string for Lua
const wchar_t* wdata = display_wstr->c_str(); const wchar_t* wdata = display_wstr->c_str();
int wlen = (int)display_wstr->size; int wlen = (int)display_wstr->size;
@@ -54,38 +63,46 @@ static void* Lua_Get_Display_Name(void* this_wrapper, void* /*script*/, void* /*
std::string narrow(narrow_len, '\0'); std::string narrow(narrow_len, '\0');
WideCharToMultiByte(CP_UTF8, 0, wdata, wlen, &narrow[0], narrow_len, nullptr, nullptr); WideCharToMultiByte(CP_UTF8, 0, wdata, wlen, &narrow[0], narrow_len, nullptr, nullptr);
// 5. Create a LuaString (LuaValue<string>, 0x78 bytes) and return it. // 5. Create a LuaString and return it
void* lua_value = ::operator new(0x78); void* lua_value = CreateLuaValueString(narrow);
if (!lua_value) if (!lua_value)
return nullptr; return nullptr;
memset(lua_value, 0, 0x78);
pfn_lua_value_ctor(lua_value, &narrow);
return pfn_return_variable(this_wrapper, lua_value); return pfn_return_variable(this_wrapper, lua_value);
} }
void Register_GOTMembers(void* wrapper) void Register_GOTMembers(void* wrapper)
{ {
if (!pfn_lmfw_ctor || !pfn_register_member) if (!pfn_lua_uservar_ctor || !pfn_register_member)
return; return;
if (!pfn_get_text_name_id || !pfn_game_text_get || !g_the_game_text) if (!g_text_name_id_offset || !pfn_game_text_get || !g_the_game_text)
return; return;
if (!pfn_lua_value_ctor || !pfn_return_variable) if (!pfn_return_variable || !g_rvas || !g_rvas->lmfw_vftable)
return; return;
// Allocate a LuaMemberFunctionWrapper<GameObjectTypeWrapper> (0x88 bytes). const LuaRVAs* rvas = g_rvas;
void* lmfw = ::operator new(0x88);
// Allocate a LuaMemberFunctionWrapper<GameObjectTypeWrapper>.
void* lmfw = ::operator new(rvas->lmfw_alloc_size);
if (!lmfw) return; if (!lmfw) return;
memset(lmfw, 0, 0x88); memset(lmfw, 0, rvas->lmfw_alloc_size);
// Build the 16-byte pointer-to-member-function structure. // Initialize the LuaUserVar base class.
pfn_lua_uservar_ctor(lmfw, 0, false);
// Set the LuaMemberFunctionWrapper<> vftable.
*(uintptr_t*)lmfw = g_base + rvas->lmfw_vftable;
// Write the 16-byte pointer-to-member-function (PMF) into the MemberFunction field.
// MSVC x64 multiple-inheritance PMF: [8-byte func addr][8-byte this-adjustment]. // MSVC x64 multiple-inheritance PMF: [8-byte func addr][8-byte this-adjustment].
// this-adjustment is 0 (our function expects the full GameObjectTypeWrapper*). // this-adjustment is 0 (our function expects the full GameObjectTypeWrapper*).
alignas(8) uint8_t pmf[16] = {}; alignas(8) uint8_t pmf[16] = {};
*(uintptr_t*)pmf = (uintptr_t)&Lua_Get_Display_Name; *(uintptr_t*)pmf = (uintptr_t)&Lua_Get_Display_Name;
memcpy((char*)lmfw + rvas->lmfw_pmf_offset, pmf, 16);
// Construct the LuaMemberFunctionWrapper via the game's ctor. // Set the Object (wrapper pointer) and UseMaps flag.
pfn_lmfw_ctor(lmfw, wrapper, pmf, false); *(void**)((char*)lmfw + rvas->lmfw_object_offset) = wrapper;
*(bool*)((char*)lmfw + rvas->lmfw_usemaps_offset) = false;
// Register as a member callable via wrapper.Get_Display_Name() // Register as a member callable via wrapper.Get_Display_Name()
pfn_register_member(wrapper, "Get_Display_Name", lmfw); pfn_register_member(wrapper, "Get_Display_Name", lmfw);

View File

@@ -9,8 +9,16 @@
// before its own null check, so we always pass a valid buffer. // before its own null check, so we always pass a valid buffer.
static int L_Add_Tutorial_Text(lua_State* L) static int L_Add_Tutorial_Text(lua_State* L)
{ {
if (pfn_debug_print) pfn_debug_print("[FoCAPI] Add_Tutorial_Text: entered\n");
const char* key = pfn_tostring(L, 1); const char* key = pfn_tostring(L, 1);
const char* text = pfn_tostring(L, 2); const char* text = pfn_tostring(L, 2);
if (pfn_debug_print) {
char msg[512];
wsprintfA(msg, "[FoCAPI] Add_Tutorial_Text: key = \"%s\", text = \"%s\"\n",
key ? key : "(null)", text ? text : "(null)");
pfn_debug_print(msg);
}
if (!key || !text) if (!key || !text)
return 0; return 0;
@@ -35,12 +43,29 @@ static int L_Add_Tutorial_Text(lua_State* L)
} }
} }
if (pfn_debug_print) {
char msg[128];
wsprintfA(msg, "[FoCAPI] Add_Tutorial_Text: duration = %d, color = [%d,%d,%d,%d]\n",
(int)(duration * 1000), (int)(color[0] * 255), (int)(color[1] * 255),
(int)(color[2] * 255), (int)(color[3] * 255));
pfn_debug_print(msg);
}
int wlen = MultiByteToWideChar(CP_ACP, 0, text, -1, nullptr, 0); int wlen = MultiByteToWideChar(CP_ACP, 0, text, -1, nullptr, 0);
std::wstring wtext(wlen - 1, L'\0'); std::wstring wtext(wlen - 1, L'\0');
MultiByteToWideChar(CP_ACP, 0, text, -1, &wtext[0], wlen); MultiByteToWideChar(CP_ACP, 0, text, -1, &wtext[0], wlen);
if (pfn_debug_print) {
char msg[128];
wsprintfA(msg, "[FoCAPI] Add_Tutorial_Text: wlen = %d, calling pfn_add_tutorial_text(%p, ...)\n",
wlen, g_command_bar);
pfn_debug_print(msg);
}
pfn_add_tutorial_text(g_command_bar, &wtext, (char*)key, duration, 0, false, color); pfn_add_tutorial_text(g_command_bar, &wtext, (char*)key, duration, 0, false, color);
if (pfn_debug_print) pfn_debug_print("[FoCAPI] Add_Tutorial_Text: returned OK\n");
return 0; return 0;
} }

View File

@@ -2,6 +2,32 @@
#include "functions/functions.h" #include "functions/functions.h"
#include <MinHook.h> #include <MinHook.h>
#include <windows.h> #include <windows.h>
#include <cstring>
#include <cstdarg>
#include <cstdio>
#include <new>
void debug_log(const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
// Also route through in-game debug if available
if (pfn_debug_print)
pfn_debug_print(buf);
FILE* f = nullptr;
if (fopen_s(&f, "FoCAPI_debug.log", "a") == 0 && f) {
fputs(buf, f);
fclose(f);
}
}
// Cached during init — used by CreateLuaValueString and Register_GOTMembers
const LuaRVAs* g_rvas = nullptr;
uintptr_t g_base = 0;
// Resolved function pointers // Resolved function pointers
fn_lua_open real_lua_open = nullptr; fn_lua_open real_lua_open = nullptr;
@@ -22,11 +48,12 @@ fn_RemoveTutorialText pfn_remove_tutorial_text = nullptr;
void* g_command_bar = nullptr; void* g_command_bar = nullptr;
fn_DebugPrint pfn_debug_print = nullptr; fn_DebugPrint pfn_debug_print = nullptr;
fn_RegisterMember pfn_register_member = nullptr; fn_RegisterMember pfn_register_member = nullptr;
fn_LuaValueCtor pfn_lua_value_ctor = nullptr;
fn_ReturnVariable pfn_return_variable = nullptr; fn_ReturnVariable pfn_return_variable = nullptr;
fn_GotWrapperCtor real_got_wrapper_ctor = nullptr; fn_GotWrapperCtor real_got_wrapper_ctor = nullptr;
fn_LmfwCtor pfn_lmfw_ctor = nullptr; fn_LuaUserVarCtor pfn_lua_uservar_ctor = nullptr;
fn_GetTextNameId pfn_get_text_name_id = nullptr; size_t g_text_name_id_offset = 0;
fn_LuaValueStringCtor pfn_lua_value_string_ctor = nullptr;
fn_GameOperatorNew pfn_game_operator_new = nullptr;
fn_GameTextGet pfn_game_text_get = nullptr; fn_GameTextGet pfn_game_text_get = nullptr;
void* g_the_game_text = nullptr; void* g_the_game_text = nullptr;
@@ -36,6 +63,48 @@ void register_global(lua_State* L, const char* name, lua_CFunction fn) {
pfn_settable(L, LUA_GLOBALSINDEX); pfn_settable(L, LUA_GLOBALSINDEX);
} }
void* CreateLuaValueString(const std::string& str) {
if (!g_rvas || !g_rvas->lua_value_string_vftable)
return nullptr;
void* obj = ::operator new(g_rvas->lua_value_string_size);
if (!obj)
return nullptr;
memset(obj, 0, g_rvas->lua_value_string_size);
if (pfn_lua_value_string_ctor) {
// Use the game's own constructor (StarWarsI).
// Handles all base class init (RefCountClass, RTTI, PooledObjectClass).
pfn_lua_value_string_ctor(obj, (void*)&str);
} else {
// Manual construction for builds where the ctor is inlined (StarWarsG).
*(uintptr_t*)obj = g_base + g_rvas->lua_value_string_vftable;
char* s = (char*)obj + g_rvas->lua_value_string_offset;
size_t len = str.size();
if (len < 16) {
memcpy(s, str.c_str(), len + 1);
*(size_t*)(s + 16) = len;
*(size_t*)(s + 24) = 15;
} else {
size_t cap = len | 0xF;
// Use the game's operator new so the game's destructor can free
// the buffer with its own operator delete (same CRT heap).
char* buf = pfn_game_operator_new
? (char*)pfn_game_operator_new(cap + 1)
: (char*)::operator new(cap + 1);
memcpy(buf, str.c_str(), len);
buf[len] = '\0';
*(char**)(s) = buf;
*(size_t*)(s + 16) = len;
*(size_t*)(s + 24) = cap;
}
}
return obj;
}
// Fired every time the game constructs a GameObjectTypeWrapper // Fired every time the game constructs a GameObjectTypeWrapper
static void* Hook_GOTWrapperCtor(void* this_wrapper) { static void* Hook_GOTWrapperCtor(void* this_wrapper) {
void* result = real_got_wrapper_ctor(this_wrapper); void* result = real_got_wrapper_ctor(this_wrapper);
@@ -73,6 +142,8 @@ bool LuaHook_Init() {
return false; return false;
uintptr_t base = (uintptr_t)GetModuleHandleA(nullptr); uintptr_t base = (uintptr_t)GetModuleHandleA(nullptr);
g_rvas = rvas;
g_base = base;
pfn_pushstring = (fn_lua_pushstring)(base + rvas->lua_pushstring); pfn_pushstring = (fn_lua_pushstring)(base + rvas->lua_pushstring);
pfn_pushcclosure = (fn_lua_pushcclosure)(base + rvas->lua_pushcclosure); pfn_pushcclosure = (fn_lua_pushcclosure)(base + rvas->lua_pushcclosure);
@@ -101,19 +172,22 @@ bool LuaHook_Init() {
if (rvas->register_member) if (rvas->register_member)
pfn_register_member = (fn_RegisterMember)(base + rvas->register_member); pfn_register_member = (fn_RegisterMember)(base + rvas->register_member);
if (rvas->lua_value_ctor)
pfn_lua_value_ctor = (fn_LuaValueCtor)(base + rvas->lua_value_ctor);
if (rvas->return_variable) if (rvas->return_variable)
pfn_return_variable = (fn_ReturnVariable)(base + rvas->return_variable); pfn_return_variable = (fn_ReturnVariable)(base + rvas->return_variable);
if (rvas->lua_value_string_ctor)
pfn_lua_value_string_ctor = (fn_LuaValueStringCtor)(base + rvas->lua_value_string_ctor);
if (rvas->game_operator_new)
pfn_game_operator_new = (fn_GameOperatorNew)(base + rvas->game_operator_new);
if (rvas->the_game_text) if (rvas->the_game_text)
g_the_game_text = (void*)(base + rvas->the_game_text); g_the_game_text = (void*)(base + rvas->the_game_text);
if (rvas->get_string) if (rvas->get_string)
pfn_game_text_get = (fn_GameTextGet)(base + rvas->get_string); pfn_game_text_get = (fn_GameTextGet)(base + rvas->get_string);
if (rvas->got_get_text_name_id) if (rvas->got_text_name_id_offset)
pfn_get_text_name_id = (fn_GetTextNameId)(base + rvas->got_get_text_name_id); g_text_name_id_offset = rvas->got_text_name_id_offset;
if (rvas->got_lmfw_ctor) if (rvas->lua_uservar_ctor)
pfn_lmfw_ctor = (fn_LmfwCtor)(base + rvas->got_lmfw_ctor); pfn_lua_uservar_ctor = (fn_LuaUserVarCtor)(base + rvas->lua_uservar_ctor);
if (rvas->lua_newtable && rvas->lua_rawseti) { if (rvas->lua_newtable && rvas->lua_rawseti) {
pfn_newtable = (fn_lua_newtable)(base + rvas->lua_newtable); pfn_newtable = (fn_lua_newtable)(base + rvas->lua_newtable);
@@ -135,10 +209,10 @@ bool LuaHook_Init() {
} }
// Hook GameObjectTypeWrapper constructor to register custom member functions // Hook GameObjectTypeWrapper constructor to register custom member functions
if (rvas->got_wrapper_ctor && rvas->got_lmfw_ctor && if (rvas->got_wrapper_ctor && rvas->lmfw_vftable && pfn_lua_uservar_ctor &&
pfn_register_member && pfn_lmfw_ctor && pfn_register_member &&
pfn_get_text_name_id && pfn_game_text_get && g_the_game_text && g_text_name_id_offset && pfn_game_text_get && g_the_game_text &&
pfn_lua_value_ctor && pfn_return_variable) rvas->lua_value_string_vftable && pfn_return_variable)
{ {
void* got_ctor_target = (void*)(base + rvas->got_wrapper_ctor); void* got_ctor_target = (void*)(base + rvas->got_wrapper_ctor);
if (MH_CreateHook(got_ctor_target, &Hook_GOTWrapperCtor, (void**)&real_got_wrapper_ctor) == MH_OK) { if (MH_CreateHook(got_ctor_target, &Hook_GOTWrapperCtor, (void**)&real_got_wrapper_ctor) == MH_OK) {

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "rvas.h" #include "rvas.h"
#include <string>
struct lua_State; struct lua_State;
typedef int (*lua_CFunction)(lua_State*); typedef int (*lua_CFunction)(lua_State*);
@@ -22,12 +23,14 @@ typedef void (*fn_RemoveTutorialText)(void*, const char*);
typedef void (*fn_DebugPrint)(const char*); // Debug_Print typedef void (*fn_DebugPrint)(const char*); // Debug_Print
// Scripting internals (LuaUserVar) -- usable by any wrapper module // Scripting internals (LuaUserVar) -- usable by any wrapper module
typedef void (*fn_RegisterMember)(void* uservar, const char* name, void* fn_ptr); // LuaUserVar::Register_Member typedef void (*fn_RegisterMember)(void* uservar, const char* name, void* fn_ptr); // LuaUserVar::Register_Member
typedef void (*fn_LuaValueCtor)(void* lua_value, void* basic_string_ptr); // LuaValue<>::LuaValue<>(basic_string<>*)
typedef void* (*fn_ReturnVariable)(void* uservar, void* lua_var); // LuaUserVar::Return_Variable(LuaVar*) -> LuaTable* typedef void* (*fn_ReturnVariable)(void* uservar, void* lua_var); // LuaUserVar::Return_Variable(LuaVar*) -> LuaTable*
// Game Object Type Wrapper internals // Game Object Type Wrapper internals
typedef void* (*fn_GotWrapperCtor)(void* this_wrapper); // GameObjectTypeWrapper::GameObjectTypeWrapper() typedef void* (*fn_GotWrapperCtor)(void* this_wrapper); // GameObjectTypeWrapper::GameObjectTypeWrapper()
typedef void* (*fn_LmfwCtor)(void* lmfw, void* wrapper, void* pmf, bool use_maps); // LuaMemberFunctionWrapper<GameObjectTypeWrapper>::ctor typedef void* (*fn_LuaUserVarCtor)(void* this_ptr, int param, bool flag); // LuaUserVar::LuaUserVar(int, bool)
typedef void* (*fn_GetTextNameId)(void* got_class, void* out_string); // GameObjectTypeClass::Get_Text_Name_ID() -- returns via hidden out-param // LuaValue<basic_string<>> constructor -- available in StarWarsI, inlined in StarWarsG
typedef void* (*fn_LuaValueStringCtor)(void* this_ptr, void* str_ptr); // LuaValue<basic_string<>>::LuaValue(basic_string*)
typedef void* (*fn_GameOperatorNew)(size_t size); // operator new(size_t) — game's CRT allocator
// GameObjectTypeClass::TextNameID is accessed via field offset (always inlined by compiler)
typedef void* (*fn_GameTextGet)(void* game_text, void* text_id_str, bool insert); // GameTextClass::Get(basic_string*, bool) -- returns wstring* typedef void* (*fn_GameTextGet)(void* game_text, void* text_id_str, bool insert); // GameTextClass::Get(basic_string*, bool) -- returns wstring*
// Resolved function pointers (defined in lua_hook.cpp) // Resolved function pointers (defined in lua_hook.cpp)
@@ -49,18 +52,28 @@ extern fn_RemoveTutorialText pfn_remove_tutorial_text;
extern void* g_command_bar; extern void* g_command_bar;
extern fn_DebugPrint pfn_debug_print; extern fn_DebugPrint pfn_debug_print;
extern fn_RegisterMember pfn_register_member; extern fn_RegisterMember pfn_register_member;
extern fn_LuaValueCtor pfn_lua_value_ctor;
extern fn_ReturnVariable pfn_return_variable; extern fn_ReturnVariable pfn_return_variable;
extern fn_GotWrapperCtor real_got_wrapper_ctor; extern fn_GotWrapperCtor real_got_wrapper_ctor;
extern fn_LmfwCtor pfn_lmfw_ctor; extern fn_LuaUserVarCtor pfn_lua_uservar_ctor;
extern fn_GetTextNameId pfn_get_text_name_id; extern size_t g_text_name_id_offset;
extern fn_LuaValueStringCtor pfn_lua_value_string_ctor;
extern fn_GameOperatorNew pfn_game_operator_new;
extern fn_GameTextGet pfn_game_text_get; extern fn_GameTextGet pfn_game_text_get;
extern void* g_the_game_text; extern void* g_the_game_text;
extern const LuaRVAs* g_rvas;
extern uintptr_t g_base;
constexpr int LUA_GLOBALSINDEX = -10001; constexpr int LUA_GLOBALSINDEX = -10001;
// Equivalent of lua_setglobal — registers a C function in the Lua global table // Equivalent of lua_setglobal — registers a C function in the Lua global table
void register_global(lua_State* L, const char* name, lua_CFunction fn); void register_global(lua_State* L, const char* name, lua_CFunction fn);
// Creates a LuaValue<basic_string<>> from a std::string.
// Reproduces the inlined constructor pattern using per-executable layout constants.
void* CreateLuaValueString(const std::string& str);
// File-based debug logging (works on both executables; pfn_debug_print is unavailable on StarWarsG).
void debug_log(const char* fmt, ...);
bool LuaHook_Init(); bool LuaHook_Init();
void LuaHook_Shutdown(); void LuaHook_Shutdown();

59
rvas.h
View File

@@ -16,7 +16,11 @@ struct LuaRVAs {
uintptr_t get_script_from_state; // LuaScriptClass::Get_Script_From_State (static) uintptr_t get_script_from_state; // LuaScriptClass::Get_Script_From_State (static)
uintptr_t map_var_to_lua; // LuaScriptClass::Map_Var_To_Lua (member) uintptr_t map_var_to_lua; // LuaScriptClass::Map_Var_To_Lua (member)
uintptr_t register_member; // LuaUserVar::Register_Member(char*, LuaVar*) uintptr_t register_member; // LuaUserVar::Register_Member(char*, LuaVar*)
uintptr_t lua_value_ctor; // LuaValue<>::LuaValue<>(basic_string<>*) uintptr_t lua_value_string_vftable; // LuaValue<basic_string<>>::vftable
size_t lua_value_string_size; // allocation size of LuaValue<basic_string<>>
size_t lua_value_string_offset; // offset of basic_string member within LuaValue
uintptr_t lua_value_string_ctor; // LuaValue<basic_string<>>::LuaValue(basic_string*) — 0 if inlined
uintptr_t game_operator_new; // operator new(size_t) — game's CRT allocator, 0 if ctor available
uintptr_t return_variable; // LuaUserVar::Return_Variable(LuaVar*) uintptr_t return_variable; // LuaUserVar::Return_Variable(LuaVar*)
// --- TheGameText --- // --- TheGameText ---
@@ -35,8 +39,15 @@ struct LuaRVAs {
// --- Game Object Type Wrapper --- // --- Game Object Type Wrapper ---
uintptr_t got_wrapper_ctor; // GameObjectTypeWrapper::GameObjectTypeWrapper() uintptr_t got_wrapper_ctor; // GameObjectTypeWrapper::GameObjectTypeWrapper()
uintptr_t got_get_text_name_id; // GameObjectTypeClass::Get_Text_Name_ID() size_t got_object_offset; // offset of SmartPtr<GameObjectTypeClass> within GameObjectTypeWrapper
uintptr_t got_lmfw_ctor; // LuaMemberFunctionWrapper<GameObjectTypeWrapper>::LuaMemberFunctionWrapper<>() size_t got_text_name_id_offset; // offset of TextNameID (std::string) within GameObjectTypeClass
// LuaMemberFunctionWrapper<GameObjectTypeWrapper> layout (ctor always inlined)
uintptr_t lmfw_vftable; // LuaMemberFunctionWrapper<>::vftable
uintptr_t lua_uservar_ctor; // LuaUserVar::LuaUserVar(int, bool)
size_t lmfw_alloc_size; // allocation size of LuaMemberFunctionWrapper<>
size_t lmfw_pmf_offset; // offset of MemberFunction (16-byte PMF)
size_t lmfw_object_offset; // offset of Object (wrapper pointer)
size_t lmfw_usemaps_offset; // offset of UseMaps (bool)
// --- Debug --- // --- Debug ---
uintptr_t debug_print; // Debug_Print(char*) uintptr_t debug_print; // Debug_Print(char*)
@@ -58,7 +69,11 @@ constexpr LuaRVAs RVAs_StarWarsI = {
0x0537BB0, // LuaScriptClass::Get_Script_From_State 0x0537BB0, // LuaScriptClass::Get_Script_From_State
0x0536FE0, // LuaScriptClass::Map_Var_To_Lua 0x0536FE0, // LuaScriptClass::Map_Var_To_Lua
0x0046f51, // LuaUserVar::Register_Member 0x0046f51, // LuaUserVar::Register_Member
0x004df5e, // LuaValue<>::LuaValue<> 0x1453310, // LuaValue<basic_string<>>::vftable
0x78, // LuaValue<basic_string<>> allocation size
0x58, // basic_string member offset within LuaValue
0x0122920, // LuaValue<basic_string<>>::LuaValue(basic_string*)
0, // operator new(size_t) — not needed, ctor available
0x0058887, // LuaUserVar::Return_Variable 0x0058887, // LuaUserVar::Return_Variable
// --- TheGameText --- // --- TheGameText ---
@@ -77,8 +92,14 @@ constexpr LuaRVAs RVAs_StarWarsI = {
// --- Game Object Type Wrapper --- // --- Game Object Type Wrapper ---
0x004f6c9, // GameObjectTypeWrapper::GameObjectTypeWrapper() 0x004f6c9, // GameObjectTypeWrapper::GameObjectTypeWrapper()
0x007b747, // GameObjectTypeClass::Get_Text_Name_ID() 0x70, // SmartPtr<GameObjectTypeClass> offset within GameObjectTypeWrapper
0x0f08450, // LuaMemberFunctionWrapper<GameObjectTypeWrapper>::ctor 0x160, // GameObjectTypeClass::TextNameID field offset
0x158a680, // LuaMemberFunctionWrapper<>::vftable
0x0074f55, // LuaUserVar::LuaUserVar
0x88, // LuaMemberFunctionWrapper<> allocation size
0x68, // MemberFunction offset
0x78, // Object offset
0x80, // UseMaps offset
// --- Debug --- // --- Debug ---
0x0476AE0, // Debug_Print 0x0476AE0, // Debug_Print
@@ -99,13 +120,17 @@ constexpr LuaRVAs RVAs_StarWarsG = {
// --- Scripts --- // --- Scripts ---
0x0245790, // LuaScriptClass::Get_Script_From_State 0x0245790, // LuaScriptClass::Get_Script_From_State
0x0247700, // LuaScriptClass::Map_Var_To_Lua 0x0247700, // LuaScriptClass::Map_Var_To_Lua
0, // LuaUserVar::Register_Member -- not mapped 0x024be40, // LuaUserVar::Register_Member
0, // LuaString::LuaString -- not mapped 0x0855a98, // LuaValue<basic_string<>>::vftable
0, // LuaUserVar::Return_Variable -- not mapped 0x30, // LuaValue<basic_string<>> allocation size
0x10, // basic_string member offset within LuaValue
0, // LuaValue<basic_string<>>::LuaValue(basic_string*) — inlined in StarWarsG
0x0769c58, // operator new(size_t) — game's CRT allocator for manual string construction
0x0256d40, // LuaUserVar::Return_Variable
// --- TheGameText --- // --- TheGameText ---
0, // TheGameText global object 0x0a7bc58, // TheGameText global object
0, // GameTextClass::Get 0x01fa680, // GameTextClass::Get
// --- Players --- // --- Players ---
0x0294bc0, // PlayerListClass::Get_Player_By_Index 0x0294bc0, // PlayerListClass::Get_Player_By_Index
@@ -118,9 +143,15 @@ constexpr LuaRVAs RVAs_StarWarsG = {
0x02ff290, // CommandBarClass::Remove_Tutorial_Text 0x02ff290, // CommandBarClass::Remove_Tutorial_Text
// --- Game Object Type Wrapper --- // --- Game Object Type Wrapper ---
0, // GameObjectTypeWrapper::GameObjectTypeWrapper() -- not mapped 0x0604a10, // GameObjectTypeWrapper::GameObjectTypeWrapper()
0, // GameObjectTypeClass::Get_Text_Name_ID() -- not mapped 0x28, // SmartPtr<GameObjectTypeClass> offset within GameObjectTypeWrapper
0, // LuaMemberFunctionWrapper<GameObjectTypeWrapper>::ctor -- not mapped 0x120, // GameObjectTypeClass::TextNameID field offset
0x08ab770, // LuaMemberFunctionWrapper<>::vftable
0x0249c20, // LuaUserVar::LuaUserVar
0x48, // LuaMemberFunctionWrapper<> allocation size
0x28, // MemberFunction offset
0x38, // Object offset
0x40, // UseMaps offset
// --- Debug --- // --- Debug ---
0, // Debug_Print (not mapped) 0, // Debug_Print (not mapped)