mirror of
https://github.com/Jacobwasbeast/LegacyWeaveLoader.git
synced 2026-05-21 21:24:30 +00:00
Fix creative inventory: inject mod items before TabSpec page count calculation
- Inject items before Original_CreativeStaticCtor so TabSpec constructors include mod items when calculating m_staticItems and m_pages - Add UpdateTabPageCounts() to recalculate TabSpec page counts after staticCtor as safety measure (handles different binary layouts) - Resolve specs symbol for TabSpec array access - Add diagnostic logging for ItemInstance vtable, shared_ptr, vector sizes
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#include "CreativeInventory.h"
|
||||
#include "SymbolResolver.h"
|
||||
#include "PdbParser.h"
|
||||
#include "LogUtil.h"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace CreativeInventory
|
||||
{
|
||||
@@ -10,6 +12,7 @@ void* pCategoryGroups = nullptr;
|
||||
void* pItemInstanceCtor = nullptr;
|
||||
void* pSharedPtrCtor = nullptr;
|
||||
void* pVectorPushBack = nullptr;
|
||||
void* pSpecs = nullptr;
|
||||
|
||||
std::vector<PendingCreativeItem> s_pendingItems;
|
||||
|
||||
@@ -18,6 +21,27 @@ static const int SIZEOF_MSVC_VECTOR = 24;
|
||||
static const int SIZEOF_MSVC_SHARED_PTR = 16;
|
||||
static const int ITEMINSTANCE_ALLOC_SIZE = 256;
|
||||
|
||||
// TabSpec field offsets (MSVC x64 layout, derived from IUIScene_CreativeMenu.h)
|
||||
// LPCWSTR m_icon @ 0 (8)
|
||||
// int m_descriptionId @ 8 (4)
|
||||
// int m_staticGroupsCount @ 12 (4)
|
||||
// ECreative_Inventory_Groups* @ 16 (8)
|
||||
// int m_dynamicGroupsCount @ 24 (4) + 4 pad
|
||||
// ECreative_Inventory_Groups* @ 32 (8)
|
||||
// int m_debugGroupsCount @ 40 (4) + 4 pad
|
||||
// ECreative_Inventory_Groups* @ 48 (8)
|
||||
// uint m_pages @ 56 (4)
|
||||
// uint m_staticPerPage @ 60 (4)
|
||||
// uint m_staticItems @ 64 (4)
|
||||
// uint m_debugItems @ 68 (4)
|
||||
static const int TABSPEC_STATIC_GROUPS_COUNT_OFF = 12;
|
||||
static const int TABSPEC_STATIC_GROUPS_A_OFF = 16;
|
||||
static const int TABSPEC_PAGES_OFF = 56;
|
||||
static const int TABSPEC_STATIC_ITEMS_OFF = 64;
|
||||
static const int TAB_COUNT = 8;
|
||||
static const int COLUMNS = 10;
|
||||
static const int ROWS = 5;
|
||||
|
||||
typedef void (__fastcall *ItemInstanceCtor_fn)(void* thisPtr, int id, int count, int auxValue);
|
||||
typedef void (__fastcall *SharedPtrCtor_fn)(void* sharedPtrThis, void* rawItemPtr);
|
||||
typedef void (__fastcall *VectorPushBackMove_fn)(void* vectorThis, void* sharedPtrRvalueRef);
|
||||
@@ -43,6 +67,9 @@ bool ResolveSymbols(SymbolResolver& resolver)
|
||||
"?push_back@?$vector@V?$shared_ptr@VItemInstance@@@std@@V?$allocator@V?$shared_ptr@VItemInstance@@@std@@@2@@std@@"
|
||||
"QEAAX$$QEAV?$shared_ptr@VItemInstance@@@2@@Z");
|
||||
|
||||
pSpecs = resolver.Resolve(
|
||||
"?specs@IUIScene_CreativeMenu@@1PEAPEAUTabSpec@1@EA");
|
||||
|
||||
if (pCategoryGroups) LogUtil::Log("[LegacyForge] categoryGroups @ %p", pCategoryGroups);
|
||||
else LogUtil::Log("[LegacyForge] MISSING: categoryGroups");
|
||||
|
||||
@@ -55,9 +82,78 @@ bool ResolveSymbols(SymbolResolver& resolver)
|
||||
if (pVectorPushBack) LogUtil::Log("[LegacyForge] vector::push_back @ %p", pVectorPushBack);
|
||||
else LogUtil::Log("[LegacyForge] MISSING: vector<shared_ptr<II>>::push_back");
|
||||
|
||||
if (pSpecs) LogUtil::Log("[LegacyForge] specs @ %p", pSpecs);
|
||||
else
|
||||
{
|
||||
LogUtil::Log("[LegacyForge] MISSING: specs (page counts won't be updated)");
|
||||
PdbParser::DumpMatching("specs@IUIScene_CreativeMenu");
|
||||
}
|
||||
|
||||
return pCategoryGroups && pItemInstanceCtor && pSharedPtrCtor && pVectorPushBack;
|
||||
}
|
||||
|
||||
static size_t ReadVectorSize(char* vec)
|
||||
{
|
||||
char* first = *reinterpret_cast<char**>(vec);
|
||||
char* last = *reinterpret_cast<char**>(vec + 8);
|
||||
if (!first || last <= first) return 0;
|
||||
return static_cast<size_t>((last - first) / SIZEOF_MSVC_SHARED_PTR);
|
||||
}
|
||||
|
||||
void UpdateTabPageCounts()
|
||||
{
|
||||
if (!pSpecs || !pCategoryGroups)
|
||||
{
|
||||
LogUtil::Log("[LegacyForge] Cannot update tab page counts: specs=%p categoryGroups=%p",
|
||||
pSpecs, pCategoryGroups);
|
||||
return;
|
||||
}
|
||||
|
||||
void** specsArray = *reinterpret_cast<void***>(pSpecs);
|
||||
if (!specsArray)
|
||||
{
|
||||
LogUtil::Log("[LegacyForge] specs pointer is null, TabSpec array not yet allocated");
|
||||
return;
|
||||
}
|
||||
|
||||
char* groups = reinterpret_cast<char*>(pCategoryGroups);
|
||||
|
||||
for (int tabIdx = 0; tabIdx < TAB_COUNT; ++tabIdx)
|
||||
{
|
||||
char* tab = reinterpret_cast<char*>(specsArray[tabIdx]);
|
||||
if (!tab) continue;
|
||||
|
||||
int staticGroupsCount = *reinterpret_cast<int*>(tab + TABSPEC_STATIC_GROUPS_COUNT_OFF);
|
||||
int* staticGroupsA = *reinterpret_cast<int**>(tab + TABSPEC_STATIC_GROUPS_A_OFF);
|
||||
if (!staticGroupsA || staticGroupsCount <= 0) continue;
|
||||
|
||||
unsigned int totalItems = 0;
|
||||
for (int i = 0; i < staticGroupsCount; ++i)
|
||||
{
|
||||
int groupIdx = staticGroupsA[i];
|
||||
if (groupIdx < 0 || groupIdx >= CREATIVE_GROUP_COUNT) continue;
|
||||
totalItems += static_cast<unsigned int>(
|
||||
ReadVectorSize(groups + groupIdx * SIZEOF_MSVC_VECTOR));
|
||||
}
|
||||
|
||||
unsigned int oldItems = *reinterpret_cast<unsigned int*>(tab + TABSPEC_STATIC_ITEMS_OFF);
|
||||
unsigned int oldPages = *reinterpret_cast<unsigned int*>(tab + TABSPEC_PAGES_OFF);
|
||||
|
||||
*reinterpret_cast<unsigned int*>(tab + TABSPEC_STATIC_ITEMS_OFF) = totalItems;
|
||||
|
||||
int totalRows = (totalItems + COLUMNS - 1) / COLUMNS;
|
||||
int newPages = totalRows - ROWS + 1;
|
||||
if (newPages < 1) newPages = 1;
|
||||
*reinterpret_cast<unsigned int*>(tab + TABSPEC_PAGES_OFF) = static_cast<unsigned int>(newPages);
|
||||
|
||||
if (totalItems != oldItems)
|
||||
{
|
||||
LogUtil::Log("[LegacyForge] Tab %d: staticItems %u -> %u, pages %u -> %u",
|
||||
tabIdx, oldItems, totalItems, oldPages, static_cast<unsigned int>(newPages));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InjectItems()
|
||||
{
|
||||
if (!pCategoryGroups || !pItemInstanceCtor || !pSharedPtrCtor || !pVectorPushBack)
|
||||
@@ -76,6 +172,8 @@ void InjectItems()
|
||||
auto spCtorFn = reinterpret_cast<SharedPtrCtor_fn>(pSharedPtrCtor);
|
||||
auto pushFn = reinterpret_cast<VectorPushBackMove_fn>(pVectorPushBack);
|
||||
|
||||
// categoryGroups is a static array of vectors (vector<...> categoryGroups[15]).
|
||||
// pCategoryGroups is the address of the first vector; no dereference needed.
|
||||
char* groups = reinterpret_cast<char*>(pCategoryGroups);
|
||||
|
||||
for (auto& item : s_pendingItems)
|
||||
@@ -91,15 +189,29 @@ void InjectItems()
|
||||
memset(rawItem, 0, ITEMINSTANCE_ALLOC_SIZE);
|
||||
ctorFn(rawItem, item.itemId, item.count, item.auxValue);
|
||||
|
||||
// Verify ItemInstance vtable was set (first 8 bytes should be non-null)
|
||||
void* vtable = *reinterpret_cast<void**>(rawItem);
|
||||
LogUtil::Log("[LegacyForge] ItemInstance(%d,%d,%d) @ %p, vtable=%p",
|
||||
item.itemId, item.count, item.auxValue, rawItem, vtable);
|
||||
|
||||
char spBuf[16];
|
||||
memset(spBuf, 0, sizeof(spBuf));
|
||||
spCtorFn(spBuf, rawItem);
|
||||
|
||||
// Log shared_ptr contents (ptr + control block)
|
||||
void* spPtr = *reinterpret_cast<void**>(spBuf);
|
||||
void* spCtrl = *reinterpret_cast<void**>(spBuf + 8);
|
||||
LogUtil::Log("[LegacyForge] shared_ptr: ptr=%p ctrl=%p", spPtr, spCtrl);
|
||||
|
||||
char* vec = groups + item.groupIndex * SIZEOF_MSVC_VECTOR;
|
||||
size_t sizeBefore = ReadVectorSize(vec);
|
||||
|
||||
pushFn(vec, spBuf);
|
||||
|
||||
LogUtil::Log("[LegacyForge] Injected item id=%d into creative group %d",
|
||||
item.itemId, item.groupIndex);
|
||||
size_t sizeAfter = ReadVectorSize(vec);
|
||||
LogUtil::Log("[LegacyForge] Injected item id=%d into creative group %d "
|
||||
"(vector @ %p, size: %zu -> %zu)",
|
||||
item.itemId, item.groupIndex, vec, sizeBefore, sizeAfter);
|
||||
}
|
||||
|
||||
LogUtil::Log("[LegacyForge] Injected %zu items into creative inventory", s_pendingItems.size());
|
||||
|
||||
@@ -16,11 +16,13 @@ namespace CreativeInventory
|
||||
void AddPending(int itemId, int count, int auxValue, int groupIndex);
|
||||
bool ResolveSymbols(SymbolResolver& resolver);
|
||||
void InjectItems();
|
||||
void UpdateTabPageCounts();
|
||||
|
||||
extern void* pCategoryGroups;
|
||||
extern void* pItemInstanceCtor;
|
||||
extern void* pSharedPtrCtor;
|
||||
extern void* pVectorPushBack;
|
||||
extern void* pSpecs;
|
||||
|
||||
extern std::vector<PendingCreativeItem> s_pendingItems;
|
||||
}
|
||||
|
||||
@@ -164,11 +164,18 @@ namespace GameHooks
|
||||
|
||||
void Hooked_CreativeStaticCtor()
|
||||
{
|
||||
// Inject mod items BEFORE vanilla staticCtor so they are included in the
|
||||
// TabSpec page-count calculation that happens at the end of staticCtor.
|
||||
LogUtil::Log("[LegacyForge] Hook: CreativeStaticCtor -- injecting modded items first");
|
||||
CreativeInventory::InjectItems();
|
||||
|
||||
LogUtil::Log("[LegacyForge] Hook: CreativeStaticCtor -- building vanilla creative lists");
|
||||
Original_CreativeStaticCtor();
|
||||
|
||||
LogUtil::Log("[LegacyForge] Hook: CreativeStaticCtor -- injecting modded items");
|
||||
CreativeInventory::InjectItems();
|
||||
// Safety: recalculate TabSpec page counts in case the injection-before
|
||||
// approach didn't fully account for all items (e.g. different binary).
|
||||
LogUtil::Log("[LegacyForge] Hook: CreativeStaticCtor -- updating tab page counts");
|
||||
CreativeInventory::UpdateTabPageCounts();
|
||||
}
|
||||
|
||||
void __fastcall Hooked_MainMenuCustomDraw(void* thisPtr, void* region)
|
||||
|
||||
Reference in New Issue
Block a user