Compare commits
8 Commits
70b5191fcd
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e3235b7d4 | |||
| 4987ee81f6 | |||
| 67adfd1e08 | |||
| 7deb2ff293 | |||
| f7b488cbf7 | |||
| 9670037eae | |||
| 958dff3a7e | |||
| 3215ecb9f6 |
@@ -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
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -4,11 +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.
|
||||
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.
|
||||
@@ -24,3 +24,7 @@ Not supported on disc / GoG versions (sorry).
|
||||
- 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.
|
||||
@@ -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);
|
||||
|
||||
109
functions/game_object_type.cpp
Normal file
109
functions/game_object_type.cpp
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
117
lua_hook.cpp
117
lua_hook.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
30
lua_hook.h
30
lua_hook.h
@@ -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
93
rvas.h
@@ -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)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user