9 Commits
v0.1.2 ... main

10 changed files with 382 additions and 20 deletions

View File

@@ -2,21 +2,22 @@
## 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
- 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.
- Both binares are 64-bit Windows portable executables.
## DLL Hook
- 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
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

View File

@@ -167,6 +167,7 @@
<ClCompile Include="functions\file_io.cpp" />
<ClCompile Include="functions\players.cpp" />
<ClCompile Include="functions\screen_text.cpp" />
<ClCompile Include="functions\game_object_type.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="powrprof.def" />

View File

@@ -59,6 +59,9 @@
<ClCompile Include="functions\functions.cpp">
<Filter>Source Files\Functions</Filter>
</ClCompile>
<ClCompile Include="functions\game_object_type.cpp">
<Filter>Source Files\Functions</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="powrprof.def">

View File

@@ -4,10 +4,11 @@ A powrprof.dll hook which adds custom Lua functions to Forces of Corruption.
## Usage
Drop powrprof.dll and MinHooks.x64.dll into the `/corruption/` folder of your Empire at War installation.
Currently only works with the debug kit `StarWarsI.exe`; `StarWarsG.exe` support is coming soon.
Drop powrprof.dll and MinHooks.x64.dll into the `/corruption` folder of your Empire at War installation.
Works with both the debug kit `StarWarsI.exe` and client version `StarWarsG.exe` of the Steam version.
Not supported on disc / GoG versions (sorry).
## Functions
## Global Functions
- Global `WriteToFile(file, text)` - Appends a line of text specified file in the game directory.
- Parameter `file` string - The file name to append.
@@ -23,3 +24,7 @@ Currently only works with the debug kit `StarWarsI.exe`; `StarWarsG.exe` support
- Parameter `a` number? - The alpha component. Default: 1
- Global `Remove_Tutorial_Text(key)` - Removes the line associated with the key.
- Parameter `key` string - The key used to identify the line when it was added.
## GameObjectTypeWrapper
- `Get_Display_Name()` - Returns the localized text string display name of the object type.

View File

@@ -11,3 +11,7 @@ void Register_All(lua_State* L);
void Register_FileIO(lua_State* L);
void Register_Players(lua_State* L);
void Register_ScreenText(lua_State* L);
// Called from the GameObjectTypeWrapper ctor hook (not from Register_All).
// Registers custom member functions on each wrapper instance.
void Register_GOTMembers(void* wrapper);

View File

@@ -0,0 +1,109 @@
#include "functions.h"
#include "../lua_hook.h"
#include <windows.h>
#include <string>
#include <cstring>
// SmartPtr offset is per-executable (0x70 StarWarsI, 0x28 StarWarsG).
// Read from g_rvas->got_object_offset at runtime.
// 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+).
// Used to read the wstring* returned by GameTextClass::Get.
struct MSVCWString {
union {
wchar_t buf[8]; // SSO buffer (up to 7 wchars + null)
wchar_t* ptr; // heap pointer when capacity > 7
};
size_t size;
size_t capacity; // <= 7 means SSO, > 7 means heap
const wchar_t* c_str() const { return (capacity <= 7) ? buf : ptr; }
};
// Custom member function registered on GameObjectTypeWrapper.
// Signature matches LuaTable* __thiscall (LuaScriptClass*, LuaTable*).
// 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*/)
{
// 1. Get GameObjectTypeClass* from SmartPtr at offset
void* got_class = *(void**)((char*)this_wrapper + g_rvas->got_object_offset);
if (!got_class)
return nullptr;
// 2. Read the TextNameID string from the GameObjectTypeClass field
MSVCString* text_name = (MSVCString*)((char*)got_class + g_text_name_id_offset);
std::string text_id(text_name->c_str(), text_name->size);
// 3. Resolve the localized display name via TheGameText.Get(text_id, false)
MSVCWString* display_wstr = (MSVCWString*)pfn_game_text_get(g_the_game_text, &text_id, false);
if (!display_wstr)
return nullptr;
// 4. Convert the wide display name to a narrow (UTF-8) string for Lua
const wchar_t* wdata = display_wstr->c_str();
int wlen = (int)display_wstr->size;
int narrow_len = WideCharToMultiByte(CP_UTF8, 0, wdata, wlen, nullptr, 0, nullptr, nullptr);
if (narrow_len <= 0)
return nullptr;
std::string narrow(narrow_len, '\0');
WideCharToMultiByte(CP_UTF8, 0, wdata, wlen, &narrow[0], narrow_len, nullptr, nullptr);
// 5. Create a LuaString and return it
void* lua_value = CreateLuaValueString(narrow);
if (!lua_value)
return nullptr;
return pfn_return_variable(this_wrapper, lua_value);
}
void Register_GOTMembers(void* wrapper)
{
if (!pfn_lua_uservar_ctor || !pfn_register_member)
return;
if (!g_text_name_id_offset || !pfn_game_text_get || !g_the_game_text)
return;
if (!pfn_return_variable || !g_rvas || !g_rvas->lmfw_vftable)
return;
const LuaRVAs* rvas = g_rvas;
// Allocate a LuaMemberFunctionWrapper<GameObjectTypeWrapper>.
void* lmfw = ::operator new(rvas->lmfw_alloc_size);
if (!lmfw) return;
memset(lmfw, 0, rvas->lmfw_alloc_size);
// 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].
// this-adjustment is 0 (our function expects the full GameObjectTypeWrapper*).
alignas(8) uint8_t pmf[16] = {};
*(uintptr_t*)pmf = (uintptr_t)&Lua_Get_Display_Name;
memcpy((char*)lmfw + rvas->lmfw_pmf_offset, pmf, 16);
// Set the Object (wrapper pointer) and UseMaps flag.
*(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()
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.
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* 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)
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);
std::wstring wtext(wlen - 1, L'\0');
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);
if (pfn_debug_print) pfn_debug_print("[FoCAPI] Add_Tutorial_Text: returned OK\n");
return 0;
}

View File

@@ -2,6 +2,32 @@
#include "functions/functions.h"
#include <MinHook.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
fn_lua_open real_lua_open = nullptr;
@@ -21,6 +47,15 @@ fn_AddTutorialText pfn_add_tutorial_text = nullptr;
fn_RemoveTutorialText pfn_remove_tutorial_text = nullptr;
void* g_command_bar = nullptr;
fn_DebugPrint pfn_debug_print = nullptr;
fn_RegisterMember pfn_register_member = nullptr;
fn_ReturnVariable pfn_return_variable = nullptr;
fn_GotWrapperCtor real_got_wrapper_ctor = nullptr;
fn_LuaUserVarCtor pfn_lua_uservar_ctor = 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;
void* g_the_game_text = nullptr;
void register_global(lua_State* L, const char* name, lua_CFunction fn) {
pfn_pushstring(L, name);
@@ -28,6 +63,55 @@ void register_global(lua_State* L, const char* name, lua_CFunction fn) {
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
static void* Hook_GOTWrapperCtor(void* this_wrapper) {
void* result = real_got_wrapper_ctor(this_wrapper);
Register_GOTMembers(this_wrapper);
return result;
}
// Fired every time the game creates a new Lua state
static lua_State* Hook_lua_open() {
lua_State* L = real_lua_open();
@@ -58,6 +142,8 @@ bool LuaHook_Init() {
return false;
uintptr_t base = (uintptr_t)GetModuleHandleA(nullptr);
g_rvas = rvas;
g_base = base;
pfn_pushstring = (fn_lua_pushstring)(base + rvas->lua_pushstring);
pfn_pushcclosure = (fn_lua_pushcclosure)(base + rvas->lua_pushcclosure);
@@ -84,6 +170,25 @@ bool LuaHook_Init() {
if (rvas->debug_print)
pfn_debug_print = (fn_DebugPrint)(base + rvas->debug_print);
if (rvas->register_member)
pfn_register_member = (fn_RegisterMember)(base + rvas->register_member);
if (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)
g_the_game_text = (void*)(base + rvas->the_game_text);
if (rvas->get_string)
pfn_game_text_get = (fn_GameTextGet)(base + rvas->get_string);
if (rvas->got_text_name_id_offset)
g_text_name_id_offset = rvas->got_text_name_id_offset;
if (rvas->lua_uservar_ctor)
pfn_lua_uservar_ctor = (fn_LuaUserVarCtor)(base + rvas->lua_uservar_ctor);
if (rvas->lua_newtable && rvas->lua_rawseti) {
pfn_newtable = (fn_lua_newtable)(base + rvas->lua_newtable);
pfn_rawseti = (fn_lua_rawseti)(base + rvas->lua_rawseti);
@@ -103,6 +208,18 @@ bool LuaHook_Init() {
return false;
}
// Hook GameObjectTypeWrapper constructor to register custom member functions
if (rvas->got_wrapper_ctor && rvas->lmfw_vftable && pfn_lua_uservar_ctor &&
pfn_register_member &&
g_text_name_id_offset && pfn_game_text_get && g_the_game_text &&
rvas->lua_value_string_vftable && pfn_return_variable)
{
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) {
MH_EnableHook(got_ctor_target);
}
}
return true;
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "rvas.h"
#include <string>
struct lua_State;
typedef int (*lua_CFunction)(lua_State*);
@@ -20,6 +21,17 @@ typedef void (*fn_MapVarToLua)(lua_State*, void*); // LuaScriptCl
typedef void (*fn_AddTutorialText)(void*, void*, const char*, float, unsigned int, bool, void*); // CommandBarClass::Add_Tutorial_Text (inner)
typedef void (*fn_RemoveTutorialText)(void*, const char*); // CommandBarClass::Remove_Tutorial_Text
typedef void (*fn_DebugPrint)(const char*); // Debug_Print
// 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_ReturnVariable)(void* uservar, void* lua_var); // LuaUserVar::Return_Variable(LuaVar*) -> LuaTable*
// Game Object Type Wrapper internals
typedef void* (*fn_GotWrapperCtor)(void* this_wrapper); // GameObjectTypeWrapper::GameObjectTypeWrapper()
typedef void* (*fn_LuaUserVarCtor)(void* this_ptr, int param, bool flag); // LuaUserVar::LuaUserVar(int, bool)
// 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*
// Resolved function pointers (defined in lua_hook.cpp)
extern fn_lua_open real_lua_open;
@@ -39,11 +51,29 @@ extern fn_AddTutorialText pfn_add_tutorial_text;
extern fn_RemoveTutorialText pfn_remove_tutorial_text;
extern void* g_command_bar;
extern fn_DebugPrint pfn_debug_print;
extern fn_RegisterMember pfn_register_member;
extern fn_ReturnVariable pfn_return_variable;
extern fn_GotWrapperCtor real_got_wrapper_ctor;
extern fn_LuaUserVarCtor pfn_lua_uservar_ctor;
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 void* g_the_game_text;
extern const LuaRVAs* g_rvas;
extern uintptr_t g_base;
constexpr int LUA_GLOBALSINDEX = -10001;
// 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);
// 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();
void LuaHook_Shutdown();

93
rvas.h
View File

@@ -12,20 +12,43 @@ struct LuaRVAs {
uintptr_t lua_newtable;
uintptr_t lua_rawseti;
// --- Scripts ---
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 register_member; // LuaUserVar::Register_Member(char*, LuaVar*)
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*)
// --- TheGameText ---
uintptr_t the_game_text; // TheGameText global object (data, not a function)
uintptr_t get_string; // GameTextClass::Get(char*, bool)
// --- Players ---
uintptr_t get_player_by_index; // PlayerListClass::Get_Player_By_Index
uintptr_t player_list; // PlayerList global object (data, not a function)
uintptr_t player_wrapper_create; // PlayerWrapper::Create (static)
// --- Scripts ---
uintptr_t get_script_from_state; // LuaScriptClass::Get_Script_From_State (static)
uintptr_t map_var_to_lua; // LuaScriptClass::Map_Var_To_Lua (member)
// --- Screen Text ---
uintptr_t command_bar; // TheCommandBar global object (data, not a function)
uintptr_t add_tutorial_text; // CommandBarClass::Add_Tutorial_Text (inner overload: wstring*, char*, float, uint, bool, RGBAClass*)
uintptr_t remove_tutorial_text; // CommandBarClass::Remove_Tutorial_Text(char*)
// --- Game Object Type Wrapper ---
uintptr_t got_wrapper_ctor; // GameObjectTypeWrapper::GameObjectTypeWrapper()
size_t got_object_offset; // offset of SmartPtr<GameObjectTypeClass> within GameObjectTypeWrapper
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 ---
uintptr_t debug_print; // Debug_Print(char*)
};
@@ -42,20 +65,42 @@ constexpr LuaRVAs RVAs_StarWarsI = {
0x12AC460, // lua_newtable
0x12ACD70, // lua_rawseti
// --- Scripts ---
0x0537BB0, // LuaScriptClass::Get_Script_From_State
0x0536FE0, // LuaScriptClass::Map_Var_To_Lua
0x0046f51, // LuaUserVar::Register_Member
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
// --- TheGameText ---
0x18578b0, // TheGameText global object
0x002b4cc, // GameTextClass::Get
// --- Players ---
0x000b0e1, // PlayerListClass::Get_Player_By_Index
0x1913158, // PlayerList global object
0x0F12010, // PlayerWrapper::Create
// --- Scripts ---
0x0537BB0, // LuaScriptClass::Get_Script_From_State
0x0536FE0, // LuaScriptClass::Map_Var_To_Lua
// --- Screen Text ---
0x1916b80, // TheCommandBar global object
0x003c83a, // CommandBarClass::Add_Tutorial_Text inner
0x006e1eb, // CommandBarClass::Remove_Tutorial_Text
// --- Game Object Type Wrapper ---
0x004f6c9, // GameObjectTypeWrapper::GameObjectTypeWrapper()
0x70, // SmartPtr<GameObjectTypeClass> offset within GameObjectTypeWrapper
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 ---
0x0476AE0, // Debug_Print
};
@@ -72,20 +117,42 @@ constexpr LuaRVAs RVAs_StarWarsG = {
0x07b9140, // lua_newtable
0x07b9820, // lua_rawseti
// --- Scripts ---
0x0245790, // LuaScriptClass::Get_Script_From_State
0x0247700, // LuaScriptClass::Map_Var_To_Lua
0x024be40, // LuaUserVar::Register_Member
0x0855a98, // LuaValue<basic_string<>>::vftable
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 ---
0x0a7bc58, // TheGameText global object
0x01fa680, // GameTextClass::Get
// --- Players ---
0x0294bc0, // PlayerListClass::Get_Player_By_Index
0x0a16fd0, // PlayerList global object
0x06019f0, // PlayerWrapper::Create
// --- Scripts ---
0x0245790, // LuaScriptClass::Get_Script_From_State
0x0247700, // LuaScriptClass::Map_Var_To_Lua
// --- Screen Text ---
0x0b27f60, // TheCommandBar
0x0b27f60, // TheCommandBar global object
0x02dfd10, // CommandBarClass::Add_Tutorial_Text inner
0x02ff290, // CommandBarClass::Remove_Tutorial_Text
// --- Game Object Type Wrapper ---
0x0604a10, // GameObjectTypeWrapper::GameObjectTypeWrapper()
0x28, // SmartPtr<GameObjectTypeClass> offset within GameObjectTypeWrapper
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 ---
0, // Debug_Print (not mapped)
};