-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Trying to incorporate your code into my base. Your overlay system pattern seemed to be outdated so I just went with my own base instead. But I've stumbled upon this issue with KeyValues
[Internal DLL] Successfully loaded and connected!
[ManualMap] Manual mapping complete!
[MaterialEditor][INFO] === Received InitializeMaterialEditor command ===
[MaterialEditor][INFO] === MaterialEditor Integration Test Started ===
[MaterialEditor][INFO] Signature Manager initialized
[MaterialEditor][INFO] Interface Manager initialized
[MaterialEditor][INFO] === Initializing Material System ===
[MaterialEditor][INFO] --- Finding Signatures ---
[MaterialEditor][INFO] Scanning module 'materialsystem2.dll' (0x0x7FF93F860000 - 0x0x7FF93F9E3000)
[MaterialEditor][SUCCESS] Signature 'CreateMaterial' found at 0x0x7FF93F89BE30
[MaterialEditor][INFO] Scanning module 'client.dll' (0x0x7FF8E62E0000 - 0x0x7FF8E84C7000)
[MaterialEditor][SUCCESS] Signature 'FixupResourceClient' found at 0x0x7FF8E76AD870
[MaterialEditor][INFO] Scanning module 'client.dll' (0x0x7FF8E62E0000 - 0x0x7FF8E84C7000)
[MaterialEditor][SUCCESS] Signature 'SetTypeKV3' found at 0x0x7FF8E76E57D0
[MaterialEditor][INFO] Scanning module 'client.dll' (0x0x7FF8E62E0000 - 0x0x7FF8E84C7000)
[MaterialEditor][SUCCESS] Signature 'GetEntityByIndex' found at 0x0x7FF8E69BAE80
[MaterialEditor][SUCCESS] Signature 'UtlBufferInit' found at 0x0x7FF985FCF460
[MaterialEditor][SUCCESS] Signature 'UtlBufferPutString' found at 0x0x7FF985FD2870
[MaterialEditor][INFO] --- Finding Interfaces ---
[MaterialEditor][INFO] Found interface 'SchemaSystem_001' in register
[MaterialEditor][INFO] Found interface 'ResourceSystem013' in register
[MaterialEditor][SUCCESS] Interface 'ResourceSystem013' found at 0x0x7FF9CD05FDD0
[MaterialEditor][INFO] Found interface 'GameResourceServiceClientV001' in register
[MaterialEditor][SUCCESS] Interface 'GameResourceServiceClientV00' found at 0x0x7FF936657730
[MaterialEditor][INFO] ResourceSystem found, need to query ResourceHandleUtils
[MaterialEditor][INFO] Interface scanning complete
[MaterialEditor][INFO] Signatures:
[MaterialEditor][INFO] CreateMaterial: FOUND (0x0x7FF93F89BE30)
[MaterialEditor][INFO] FixupResourceClient: FOUND (0x0x7FF8E76AD870)
[MaterialEditor][INFO] SetTypeKV3: FOUND (0x0x7FF8E76E57D0)
[MaterialEditor][INFO] LoadKeyValues: FOUND (0x0x7FF985F618D0)
[MaterialEditor][INFO] GetEntityByIndex: FOUND (0x0x7FF8E69BAE80)
[MaterialEditor][INFO] UtlBufferInit: FOUND (0x0x7FF985FCF460)
[MaterialEditor][INFO] UtlBufferPutString: FOUND (0x0x7FF985FD2870)
[MaterialEditor][INFO] Interfaces:
[MaterialEditor][INFO] ResourceSystem: FOUND (0x0x7FF9CD05FDD0)
[MaterialEditor][INFO] GameResourceService: FOUND (0x0x7FF936657730)
[MaterialEditor][INFO] ===============================
[MaterialEditor] Initialization completed successfully
[MaterialEditor][INFO] === Received PrintStatus command ===
[MaterialEditor][INFO] Signatures:
[MaterialEditor][INFO] CreateMaterial: FOUND (0x0x7FF93F89BE30)
[MaterialEditor][INFO] SetTypeKV3: FOUND (0x0x7FF8E76E57D0)
[MaterialEditor][INFO] LoadKeyValues: FOUND (0x0x7FF985F618D0)
[MaterialEditor][INFO] UtlBufferInit: FOUND (0x0x7FF985FCF460)
[MaterialEditor][INFO] UtlBufferPutString: FOUND (0x0x7FF985FD2870)
[MaterialEditor][INFO] SchemaSystem: FOUND (0x0x7FF9CCC256F0)
[MaterialEditor][INFO] ResourceSystem: FOUND (0x0x7FF9CD05FDD0)
[MaterialEditor][INFO] ===============================
[MaterialEditor] Status printed to debug output
[MaterialEditor][SUCCESS] System verification PASSED
[MaterialEditor] System verification PASSED
[MaterialEditor][INFO] === Received TestMaterialCreation command ===
[MaterialEditor][INFO] Testing material creation for: test_material
[MaterialEditor][INFO] Creating CKeyValues3 object...
[MaterialEditor][INFO] CKeyValues3 memory zeroed
[MaterialEditor][SUCCESS] CKeyValues3 initialized with SetTypeKV3
[MaterialEditor][INFO] Creating CUtlBuffer and loading material data...
[MaterialEditor][INFO] CUtlBuffer size: 128, vmatLength: 649
[MaterialEditor][INFO] UtlBufferInit returned: 0x0x6DA66FC510 (should be same as buffer: 0x0x6DA66FC510)
[MaterialEditor][INFO] PutString called with 649 bytes
[MaterialEditor][INFO] Found likely Put at offset 3: value=687
[MaterialEditor][INFO] Resetting Get at offset 2 from 1140662784 to 0
[MaterialEditor][INFO] Buffer content preview (first 64 chars):
[MaterialEditor][INFO] <!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32a
[MaterialEditor][INFO] Loading KeyValues from buffer...
[MaterialEditor][INFO] Calling LoadKeyValues: kv=0x0x6DA66FC360, buf=0x0x6DA66FC510
[MaterialEditor][INFO] KV3ID: name=generic, id0=0xlX, id1=0xlX
[MaterialEditor][INFO] About to call LoadKeyValues function...
[MaterialEditor][INFO] LoadKeyValues returned: 0
[MaterialEditor][ERROR] Failed to load KeyValues from buffer (returned false)
[MaterialEditor][INFO] Buffer first 32 bytes check...
[MaterialEditor] Material creation test failed
#include "MaterialEditorTest.h"
#include <Windows.h>
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include "../../../../usermode/src/fast_ipc.h"
// External string functions (defined in dllmain.cpp)
extern "C" {
int __cdecl custom_strncmp(const char* s1, const char* s2, size_t n);
int __cdecl custom_strncpy_s(char* dest, size_t destSize, const char* src, size_t count);
}
// Wrapper macros to use custom functions
#define strncmp custom_strncmp
// Note: Don't define strncpy_s macro - call custom_strncpy_s directly to avoid conflicts
// IntelliSense suppressions for false positives
#ifdef __INTELLISENSE__
// Suppress IntelliSense errors - the code compiles fine, these are parser issues
#pragma diag_suppress 140 // too many arguments
#pragma diag_suppress 413 // no suitable conversion
#pragma diag_suppress 20 // identifier undefined
#pragma diag_suppress 135 // class has no member
#pragma diag_suppress 144 // value of type void
#pragma diag_suppress 167 // argument incompatible
#pragma diag_suppress 757 // not a type name
// Help IntelliSense understand the types
using MaterialEditorTest::CKeyValues3;
using MaterialEditorTest::CUtlBuffer;
using MaterialEditorTest::CMaterial2;
using MaterialEditorTest::KV3ID_t;
using MaterialEditorTest::CStrongHandle;
using MaterialEditorTest::MaterialSystemData;
#endif
// Safe string length
static size_t safe_strlen(const char* str) {
if (!str) return 0;
size_t len = 0;
while (str[len] && len < 4096) len++;
return len;
}
namespace MaterialEditorTest {
// ========================================================================
// IPC Debug Logger Implementation
// ========================================================================
bool IPCDebugLogger::initialized_ = false;
void IPCDebugLogger::Initialize() {
initialized_ = true;
Log("=== MaterialEditor Integration Test Started ===");
}
// Simple sprintf with va_list (for variadic functions)
static int simple_vsprintf(char* buffer, size_t bufSize, const char* format, va_list args) {
size_t written = 0;
const char* p = format;
while (*p && written < bufSize - 1) {
if (*p == '%') {
p++;
if (*p == 's') {
const char* str = va_arg(args, const char*);
if (str) {
while (*str && written < bufSize - 1) {
buffer[written++] = *str++;
}
}
} else if (*p == 'p') {
void* ptr = va_arg(args, void*);
unsigned long long addr = (unsigned long long)ptr;
// Directly write hex digits to buffer (no intermediate array)
if (written < bufSize - 1) buffer[written++] = '0';
if (written < bufSize - 1) buffer[written++] = 'x';
// Only print significant digits (skip leading zeros except last one)
bool foundNonZero = false;
for (int i = 60; i >= 0; i -= 4) {
int digit = (addr >> i) & 0xF;
if (digit != 0 || foundNonZero || i == 0) {
if (written < bufSize - 1) {
buffer[written++] = digit < 10 ? '0' + digit : 'A' + digit - 10;
}
foundNonZero = true;
}
}
} else if (*p == 'd' || *p == 'i') {
int num = va_arg(args, int);
char numBuf[32];
int numIdx = 0;
if (num < 0) {
buffer[written++] = '-';
num = -num;
}
// Convert to string (backwards)
if (num == 0) {
numBuf[numIdx++] = '0';
} else {
while (num > 0 && numIdx < 31) {
numBuf[numIdx++] = '0' + (num % 10);
num /= 10;
}
}
// Reverse and copy
for (int i = numIdx - 1; i >= 0 && written < bufSize - 1; i--) {
buffer[written++] = numBuf[i];
}
} else if (*p == 'x' || *p == 'X') {
unsigned int num = va_arg(args, unsigned int);
char hexBuf[16];
int hexIdx = 0;
if (num == 0) {
buffer[written++] = '0';
} else {
while (num > 0 && hexIdx < 15) {
int digit = num % 16;
hexBuf[hexIdx++] = digit < 10 ? '0' + digit : 'A' + digit - 10;
num /= 16;
}
// Reverse and copy
for (int i = hexIdx - 1; i >= 0 && written < bufSize - 1; i--) {
buffer[written++] = hexBuf[i];
}
}
} else if (*p == '%') {
buffer[written++] = '%';
}
p++;
} else {
buffer[written++] = *p++;
}
}
buffer[written] = 0;
return written;
}
// Simple sprintf with variadic arguments (for direct calls)
static int simple_sprintf(char* buffer, size_t bufSize, const char* format, ...) {
va_list args;
va_start(args, format);
int result = simple_vsprintf(buffer, bufSize, format, args);
va_end(args);
return result;
}
void IPCDebugLogger::SendIPC(const char* prefix, const char* message) {
// External IPC (declared outside namespace)
extern FastIPC::InternalIPC* g_internal_ipc;
extern bool g_ipc_connected;
// Format: [MaterialEditor][PREFIX] message
char buffer[512];
size_t pos = 0;
// Copy "[MaterialEditor]["
const char* start = "[MaterialEditor][";
while (*start && pos < sizeof(buffer) - 1) {
buffer[pos++] = *start++;
}
// Copy prefix
while (*prefix && pos < sizeof(buffer) - 1) {
buffer[pos++] = *prefix++;
}
// Copy "] "
buffer[pos++] = ']';
buffer[pos++] = ' ';
// Copy message
while (*message && pos < sizeof(buffer) - 1) {
buffer[pos++] = *message++;
}
buffer[pos] = 0;
// Send to usermode via IPC
if (g_ipc_connected && g_internal_ipc) {
g_internal_ipc->Send(buffer, 0);
// Small delay to let usermode read the message before sending next one
Sleep(5); // 5ms delay between messages
}
// Also send to OutputDebugString as backup
OutputDebugStringA(buffer);
}
void IPCDebugLogger::Log(const char* format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
simple_vsprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendIPC("INFO", buffer);
}
void IPCDebugLogger::Success(const char* format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
simple_vsprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendIPC("SUCCESS", buffer);
}
void IPCDebugLogger::Error(const char* format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
simple_vsprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendIPC("ERROR", buffer);
}
void IPCDebugLogger::Warning(const char* format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
simple_vsprintf(buffer, sizeof(buffer), format, args);
va_end(args);
SendIPC("WARNING", buffer);
}
void IPCDebugLogger::SignatureFound(const char* name, void* address) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Signature '%s' found at 0x%p", name, address);
SendIPC("SUCCESS", buffer);
}
void IPCDebugLogger::SignatureFailed(const char* name) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Failed to find signature '%s'", name);
SendIPC("ERROR", buffer);
}
void IPCDebugLogger::InterfaceFound(const char* name, void* address) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Interface '%s' found at 0x%p", name, address);
SendIPC("SUCCESS", buffer);
}
void IPCDebugLogger::InterfaceFailed(const char* name) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Failed to find interface '%s'", name);
SendIPC("ERROR", buffer);
}
// ========================================================================
// Pattern Scanner Implementation
// ========================================================================
void* PatternScanner::GetModuleEnd(void* moduleBase) {
auto dosHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(moduleBase);
auto ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS*>(
reinterpret_cast<uint8_t*>(moduleBase) + dosHeader->e_lfanew);
return reinterpret_cast<uint8_t*>(moduleBase) + ntHeaders->OptionalHeader.SizeOfImage;
}
void* PatternScanner::Scan(void* start, void* end, const int16_t* pattern, size_t patternSize,
int pre_offset, int post_offset) {
uint64_t rangeEnd = reinterpret_cast<uint64_t>(end) - patternSize;
for (uint64_t address = reinterpret_cast<uint64_t>(start); address <= rangeEnd; ++address) {
bool match = true;
for (size_t i = 0; i < patternSize; ++i) {
if (pattern[i] == -1) continue; // Wildcard
if (*reinterpret_cast<uint8_t*>(address + i) != pattern[i]) {
match = false;
break;
}
}
if (match) {
// Handle relative addressing
if (pre_offset != 0) {
address += pre_offset;
address += sizeof(int32_t) + *reinterpret_cast<int32_t*>(address);
}
address += post_offset;
return reinterpret_cast<void*>(address);
}
}
return nullptr;
}
void* PatternScanner::Scan(const char* moduleName, const int16_t* pattern, size_t patternSize,
int pre_offset, int post_offset) {
HMODULE hModule = GetModuleHandleA(moduleName);
if (!hModule) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Module '%s' not found", moduleName);
IPCDebugLogger::SendIPC("ERROR", buffer);
return nullptr;
}
void* moduleBase = reinterpret_cast<void*>(hModule);
void* moduleEnd = GetModuleEnd(moduleBase);
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Scanning module '%s' (0x%p - 0x%p)", moduleName, moduleBase, moduleEnd);
IPCDebugLogger::SendIPC("INFO", buffer);
return Scan(moduleBase, moduleEnd, pattern, patternSize, pre_offset, post_offset);
}
// ========================================================================
// Signature Manager Implementation
// ========================================================================
static LoadedSignature g_signatures[32];
static int g_signature_count = 0;
bool SignatureManager::failed_ = false;
void SignatureManager::Initialize() {
g_signature_count = 0;
failed_ = false;
IPCDebugLogger::Log("Signature Manager initialized");
}
void SignatureManager::AddSignatureImpl(const char* name, const char* module,
const SignatureData& sig, void** out,
int post_offset) {
// Decrypt signature pattern into stack array (no heap allocation)
int16_t pattern[256]; // Max pattern size
if (sig.nLength > 256) {
IPCDebugLogger::SendIPC("ERROR", "Signature pattern too large");
failed_ = true;
return;
}
for (size_t i = 0; i < sig.nLength; i++) {
pattern[i] = sig.pData[i] ^ sig.nKey;
}
// Log pattern for debugging
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Scanning for signature '%s' in '%s'", name, module);
IPCDebugLogger::SendIPC("INFO", buffer);
// Scan for pattern
void* address = PatternScanner::Scan(module, pattern, sig.nLength, sig.nPreOffset, post_offset);
if (!address) {
IPCDebugLogger::SignatureFailed(name);
failed_ = true;
return;
}
IPCDebugLogger::SignatureFound(name, address);
if (out) {
*out = address;
}
// Add to signature list
if (g_signature_count < 32) {
LoadedSignature& newsig = g_signatures[g_signature_count++];
custom_strncpy_s(newsig.name, sizeof(newsig.name), name, sizeof(newsig.name) - 1);
newsig.address = address;
}
}
void SignatureManager::GetImpl(const char* name, void** out) {
*out = nullptr;
for (int i = 0; i < g_signature_count; i++) {
if (strncmp(g_signatures[i].name, name, sizeof(g_signatures[i].name)) == 0) {
*out = g_signatures[i].address;
return;
}
}
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Signature '%s' not found in loaded signatures", name);
IPCDebugLogger::SendIPC("ERROR", buffer);
}
// ========================================================================
// Interface Manager Implementation
// ========================================================================
void InterfaceManager::Initialize() {
IPCDebugLogger::Log("Interface Manager initialized");
}
InterfaceReg* InterfaceManager::GetModuleRegister(const char* moduleName) {
HMODULE hModule = GetModuleHandleA(moduleName);
if (!hModule) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Module '%s' not loaded", moduleName);
IPCDebugLogger::SendIPC("ERROR", buffer);
return nullptr;
}
// Get CreateInterface export
auto createInterface = reinterpret_cast<void*>(GetProcAddress(hModule, "CreateInterface"));
if (!createInterface) {
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "CreateInterface not found in '%s'", moduleName);
IPCDebugLogger::SendIPC("ERROR", buffer);
return nullptr;
}
// Resolve relative address to get interface register
// Pattern: mov rax, qword ptr [rip + offset]
// At offset 0x3, read 4-byte relative offset
uint8_t* addr = reinterpret_cast<uint8_t*>(createInterface);
int32_t offset = *reinterpret_cast<int32_t*>(addr + 0x3);
InterfaceReg** pRegister = reinterpret_cast<InterfaceReg**>(addr + 0x7 + offset);
return *pRegister;
}
void* InterfaceManager::CaptureInterface(const InterfaceReg* reg, const char* interfaceName) {
size_t len = safe_strlen(interfaceName);
for (const InterfaceReg* current = reg; current != nullptr; current = current->pNext) {
if (strncmp(current->szName, interfaceName, len) == 0) {
void* pInterface = current->fnCreate();
char msg[256];
simple_sprintf(msg, sizeof(msg), "Found interface '%s' in register", current->szName);
IPCDebugLogger::Log(msg);
return pInterface;
}
}
return nullptr;
}
void InterfaceManager::GetInterfaceImpl(const char* moduleName, const char* interfaceName, void** out) {
*out = nullptr;
InterfaceReg* reg = GetModuleRegister(moduleName);
if (!reg) {
IPCDebugLogger::InterfaceFailed(interfaceName);
return;
}
void* pInterface = CaptureInterface(reg, interfaceName);
if (!pInterface) {
IPCDebugLogger::InterfaceFailed(interfaceName);
return;
}
IPCDebugLogger::InterfaceFound(interfaceName, pInterface);
*out = pInterface;
}
// ========================================================================
// Material System Data Implementation
// ========================================================================
void MaterialSystemData::Initialize() {
IPCDebugLogger::Log("=== Initializing Material System ===");
FindSignatures();
FindInterfaces();
PrintDebugInfo();
}
void MaterialSystemData::FindSignatures() {
IPCDebugLogger::Log("--- Finding Signatures ---");
// CreateMaterial signature
SignatureManager::AddSignature(
"CreateMaterial",
"materialsystem2.dll",
SignatureString("48 89 5C 24 ? 48 89 6C 24 ? 56 57 41 56 48 81 EC ? ? ? ? 48 8B 05"),
(void**)&fnCreateMaterial
);
// FixupResourceClient signature
SignatureManager::AddSignature(
"FixupResourceClient",
"client.dll",
SignatureString("E8 {} 48 8B D3 C7 85"),
(void**)&fnFixupResourceClient
);
// SetTypeKV3 signature
SignatureManager::AddSignature(
"SetTypeKV3",
"client.dll",
SignatureString("E8 {} 48 8B 5E ? 41 8B 06"),
(void**)&fnSetTypeKV3
);
// GetEntityByIndex signature
SignatureManager::AddSignature(
"GetEntityByIndex",
"client.dll",
SignatureString("4C 8D 49 ? 81 FA"),
(void**)&fnGetEntityByIndex
);
// LoadKeyValues - Use exported function from tier0.dll
// Signature: ?LoadKV3@@YA_NPEAVKeyValues3@@PEAVCUtlString@@PEAVCUtlBuffer@@AEBUKV3ID_t@@PEBDI@Z
HMODULE tier0 = GetModuleHandleA("tier0.dll");
if (tier0) {
fnLoadKeyValues = (LoadKeyValues_t)GetProcAddress(tier0, "?LoadKV3@@YA_NPEAVKeyValues3@@PEAVCUtlString@@PEAVCUtlBuffer@@AEBUKV3ID_t@@PEBDI@Z");
if (fnLoadKeyValues) {
IPCDebugLogger::SignatureFound("LoadKeyValues", fnLoadKeyValues);
} else {
IPCDebugLogger::SignatureFailed("LoadKeyValues");
SignatureManager::SetFailed();
}
}
// CUtlBuffer::Init - Use exported constructor from tier0.dll
// Signature: ??0CUtlBuffer@@QEAA@HHW4BufferFlags_t@0@@Z
if (tier0) {
fnUtlBufferInit = (UtlBufferInit_t)GetProcAddress(tier0, "??0CUtlBuffer@@QEAA@HHW4BufferFlags_t@0@@Z");
if (fnUtlBufferInit) {
IPCDebugLogger::SignatureFound("UtlBufferInit", fnUtlBufferInit);
} else {
IPCDebugLogger::SignatureFailed("UtlBufferInit");
SignatureManager::SetFailed();
}
}
// CUtlBuffer::PutString - Use exported function from tier0.dll
// Signature: ?PutString@CUtlBuffer@@QEAAXPEBD@Z
if (tier0) {
fnUtlBufferPutString = (UtlBufferPutString_t)GetProcAddress(tier0, "?PutString@CUtlBuffer@@QEAAXPEBD@Z");
if (fnUtlBufferPutString) {
IPCDebugLogger::SignatureFound("UtlBufferPutString", fnUtlBufferPutString);
} else {
IPCDebugLogger::SignatureFailed("UtlBufferPutString");
SignatureManager::SetFailed();
}
}
if (SignatureManager::HasFailed()) {
IPCDebugLogger::Warning("Some signatures failed to find");
} else {
IPCDebugLogger::Success("All signatures found successfully");
}
}
void MaterialSystemData::FindInterfaces() {
IPCDebugLogger::Log("--- Finding Interfaces ---");
// SchemaSystem
pSchemaSystem = InterfaceManager::GetInterface<ISchemaSystem>(
"schemasystem.dll", "SchemaSystem_00");
// InputSystem - not critical for material testing
// pInputSystem = InterfaceManager::GetInterface<CInputSystem>(
// "inputsystem.dll", "InputSystemVersion00");
// ResourceSystem
pResourceSystem = InterfaceManager::GetInterface<CResourceSystem>(
"resourcesystem.dll", "ResourceSystem013");
// GameResourceService
pGameResourceService = InterfaceManager::GetInterface<CGameResourceService>(
"engine2.dll", "GameResourceServiceClientV00");
// Get ResourceHandleUtils from ResourceSystem
if (pResourceSystem) {
// QueryInterface is typically at vtable offset 0 or 1
// For now, we'll log that we need to implement this
IPCDebugLogger::Log("ResourceSystem found, need to query ResourceHandleUtils");
}
IPCDebugLogger::Log("Interface scanning complete");
}
void MaterialSystemData::PrintDebugInfo() {
char buffer[512];
IPCDebugLogger::Log("=== Material System Status ===");
IPCDebugLogger::Log("Signatures:");
simple_sprintf(buffer, sizeof(buffer), " CreateMaterial: %s (0x%p)",
fnCreateMaterial ? "FOUND" : "MISSING", fnCreateMaterial);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " FixupResourceClient: %s (0x%p)",
fnFixupResourceClient ? "FOUND" : "MISSING", fnFixupResourceClient);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " SetTypeKV3: %s (0x%p)",
fnSetTypeKV3 ? "FOUND" : "MISSING", fnSetTypeKV3);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " LoadKeyValues: %s (0x%p)",
fnLoadKeyValues ? "FOUND" : "MISSING", fnLoadKeyValues);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " GetEntityByIndex: %s (0x%p)",
fnGetEntityByIndex ? "FOUND" : "MISSING", fnGetEntityByIndex);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " UtlBufferInit: %s (0x%p)",
fnUtlBufferInit ? "FOUND" : "MISSING", fnUtlBufferInit);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " UtlBufferPutString: %s (0x%p)",
fnUtlBufferPutString ? "FOUND" : "MISSING", fnUtlBufferPutString);
IPCDebugLogger::SendIPC("INFO", buffer);
IPCDebugLogger::Log("Interfaces:");
simple_sprintf(buffer, sizeof(buffer), " SchemaSystem: %s (0x%p)",
pSchemaSystem ? "FOUND" : "MISSING", pSchemaSystem);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " ResourceSystem: %s (0x%p)",
pResourceSystem ? "FOUND" : "MISSING", pResourceSystem);
IPCDebugLogger::SendIPC("INFO", buffer);
simple_sprintf(buffer, sizeof(buffer), " GameResourceService: %s (0x%p)",
pGameResourceService ? "FOUND" : "MISSING", pGameResourceService);
IPCDebugLogger::SendIPC("INFO", buffer);
IPCDebugLogger::Log("===============================");
}
// ========================================================================
// Main Test Interface Implementation
// ========================================================================
bool MaterialEditorIntegration::initialized_ = false;
bool MaterialEditorIntegration::Initialize() {
if (initialized_) {
IPCDebugLogger::Warning("MaterialEditorIntegration already initialized");
return true;
}
IPCDebugLogger::Initialize();
SignatureManager::Initialize();
InterfaceManager::Initialize();
MaterialSystemData::Initialize();
initialized_ = true;
return !SignatureManager::HasFailed();
}
bool MaterialEditorIntegration::VerifySystem() {
if (!initialized_) {
IPCDebugLogger::Error("System not initialized");
return false;
}
bool allGood = true;
// Check critical signatures
if (!MaterialSystemData::fnCreateMaterial) {
IPCDebugLogger::Error("VERIFY FAILED: CreateMaterial not found");
allGood = false;
}
// Check critical interfaces
if (!MaterialSystemData::pSchemaSystem) {
IPCDebugLogger::Error("VERIFY FAILED: SchemaSystem not found");
allGood = false;
}
if (!MaterialSystemData::pResourceSystem) {
IPCDebugLogger::Error("VERIFY FAILED: ResourceSystem not found");
allGood = false;
}
if (allGood) {
IPCDebugLogger::Success("System verification PASSED");
} else {
IPCDebugLogger::Error("System verification FAILED");
}
return allGood;
}
void MaterialEditorIntegration::PrintStatus() {
MaterialSystemData::PrintDebugInfo();
}
bool MaterialEditorIntegration::TestMaterialCreation(const char* materialName) {
if (!initialized_) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - system not initialized");
return false;
}
if (!MaterialSystemData::fnCreateMaterial) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - CreateMaterial function not found");
return false;
}
if (!MaterialSystemData::fnSetTypeKV3) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - SetTypeKV3 function not found");
return false;
}
if (!MaterialSystemData::fnLoadKeyValues) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - LoadKeyValues function not found");
return false;
}
if (!MaterialSystemData::fnUtlBufferInit) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - UtlBufferInit function not found");
return false;
}
if (!MaterialSystemData::fnUtlBufferPutString) {
IPCDebugLogger::SendIPC("ERROR", "Cannot test material creation - UtlBufferPutString function not found");
return false;
}
char buffer[512];
simple_sprintf(buffer, sizeof(buffer), "Testing material creation for: %s", materialName);
IPCDebugLogger::SendIPC("INFO", buffer);
// KV3 signature header
const char* kv3_signature = R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
)";
// Test material definition (simple white unlit material)
const char* material_content = R"(shader = "csgo_unlitgeneric.vfx"
F_PAINT_VERTEX_COLORS = 1
F_TRANSLUCENT = 1
F_BLEND_MODE = 1
g_vColorTint = [1, 1, 1, 1]
TextureAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tColor = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tNormal = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tTintMask = resource:"materials/default/default_mask_tga_fde710a5.vtex"
})";
// Build full vmat string
char vmatBuffer[2048];
size_t offset = 0;
// Copy KV3 signature
const char* src = kv3_signature;
while (*src && offset < sizeof(vmatBuffer) - 1) {
vmatBuffer[offset++] = *src++;
}
// Copy material content
src = material_content;
while (*src && offset < sizeof(vmatBuffer) - 1) {
vmatBuffer[offset++] = *src++;
}
// Add closing brace
if (offset < sizeof(vmatBuffer) - 2) {
vmatBuffer[offset++] = '\n';
vmatBuffer[offset++] = '}';
}
vmatBuffer[offset] = '\0';
IPCDebugLogger::SendIPC("INFO", "Creating CKeyValues3 object...");
// Create CKeyValues3 object and zero the memory
CKeyValues3 keyValues;
memset(&keyValues, 0, sizeof(CKeyValues3));
IPCDebugLogger::SendIPC("INFO", "CKeyValues3 memory zeroed");
// Initialize KV3 (SetTypeKV3) - THIS IS REQUIRED
void* kvResult = MaterialSystemData::fnSetTypeKV3(&keyValues, 1u, 6u);
if (!kvResult) {
IPCDebugLogger::SendIPC("ERROR", "Failed to initialize CKeyValues3 (SetTypeKV3 returned null)");
return false;
}
IPCDebugLogger::SendIPC("SUCCESS", "CKeyValues3 initialized with SetTypeKV3");
IPCDebugLogger::SendIPC("INFO", "Creating CUtlBuffer and loading material data...");
// Create CUtlBuffer - try using Init directly with text mode
CUtlBuffer utlBuffer;
int vmatLength = static_cast<int>(offset);
char debugMsg[256];
simple_sprintf(debugMsg, sizeof(debugMsg), "CUtlBuffer size: %d, vmatLength: %d", sizeof(CUtlBuffer), vmatLength);
IPCDebugLogger::SendIPC("INFO", debugMsg);
// Call Init as constructor (returns 'this')
void* initResult = MaterialSystemData::fnUtlBufferInit(&utlBuffer, 0, vmatLength + 10, 1);
simple_sprintf(debugMsg, sizeof(debugMsg), "UtlBufferInit returned: 0x%p (should be same as buffer: 0x%p)", initResult, &utlBuffer);
IPCDebugLogger::SendIPC("INFO", debugMsg);
MaterialSystemData::fnUtlBufferPutString(&utlBuffer, vmatBuffer);
simple_sprintf(debugMsg, sizeof(debugMsg), "PutString called with %d bytes", vmatLength);
IPCDebugLogger::SendIPC("INFO", debugMsg);
// CRITICAL: After PutString, the write position (m_Put) is at the end
// But LoadKeyValues needs to read from position 0
// CUtlBuffer structure (approximately):
// +0x00: char* m_Memory
// +0x08: int m_nAllocationCount (or maxput)
// +0x0C: int m_Get (read position)
// +0x10: int m_Put (write position)
// We need to reset m_Get to 0
// Try to find and reset the read position to 0
// Let's check multiple offsets to find the right one
int* pBufferInternals = (int*)&utlBuffer;
// Log first few ints to understand structure
simple_sprintf(debugMsg, sizeof(debugMsg), "Buffer internals: [0]=%d [1]=%d [2]=%d [3]=%d [4]=%d [5]=%d",
pBufferInternals[0], pBufferInternals[1], pBufferInternals[2],
pBufferInternals[3], pBufferInternals[4], pBufferInternals[5]);
IPCDebugLogger::SendIPC("INFO", debugMsg);
// The Put value should be around 649-660 (our data size + overhead)
// Find which offset has that value
for (int i = 0; i < 10; i++) {
if (pBufferInternals[i] > 640 && pBufferInternals[i] < 700) {
simple_sprintf(debugMsg, sizeof(debugMsg), "Found likely Put at offset %d: value=%d", i, pBufferInternals[i]);
IPCDebugLogger::SendIPC("INFO", debugMsg);
// Get should be just before Put
if (i > 0) {
simple_sprintf(debugMsg, sizeof(debugMsg), "Resetting Get at offset %d from %d to 0", i-1, pBufferInternals[i-1]);
IPCDebugLogger::SendIPC("INFO", debugMsg);
pBufferInternals[i-1] = 0;
}
break;
}
}
IPCDebugLogger::SendIPC("INFO", "Buffer populated and reset, ready for LoadKeyValues");
// Verify buffer content (first 64 chars)
char bufferPreview[80];
int previewLen = (vmatLength < 64) ? vmatLength : 64;
for (int i = 0; i < previewLen; i++) {
char c = vmatBuffer[i];
bufferPreview[i] = (c >= 32 && c < 127) ? c : '.';
}
bufferPreview[previewLen] = '\0';
// Send preview in chunks since simple_sprintf might not handle it well
IPCDebugLogger::SendIPC("INFO", "Buffer content preview (first 64 chars):");
IPCDebugLogger::SendIPC("INFO", bufferPreview);
IPCDebugLogger::SendIPC("SUCCESS", "CUtlBuffer created and populated");
IPCDebugLogger::SendIPC("INFO", "Loading KeyValues from buffer...");
// Load KeyValues from buffer
simple_sprintf(debugMsg, sizeof(debugMsg), "Calling LoadKeyValues: kv=0x%p, buf=0x%p", &keyValues, &utlBuffer);
IPCDebugLogger::SendIPC("INFO", debugMsg);
KV3ID_t kv3ID = { "generic", 0x41B818518343427E, 0xB5F447C23C0CDF8C };
simple_sprintf(debugMsg, sizeof(debugMsg), "KV3ID: name=%s, id0=0x%llX, id1=0x%llX", kv3ID.szName, kv3ID.unk0, kv3ID.unk1);
IPCDebugLogger::SendIPC("INFO", debugMsg);
// Call LoadKeyValues
IPCDebugLogger::SendIPC("INFO", "About to call LoadKeyValues function...");
bool loadResult = MaterialSystemData::fnLoadKeyValues(
&keyValues,
nullptr,
&utlBuffer,
&kv3ID,
nullptr,
nullptr,
nullptr,
nullptr,
"",
0
);
simple_sprintf(debugMsg, sizeof(debugMsg), "LoadKeyValues returned: %d", loadResult ? 1 : 0);
IPCDebugLogger::SendIPC("INFO", debugMsg);
if (!loadResult) {
IPCDebugLogger::SendIPC("ERROR", "Failed to load KeyValues from buffer (returned false)");
// Try to get more info about the buffer state
simple_sprintf(debugMsg, sizeof(debugMsg), "Buffer first 32 bytes check...");
IPCDebugLogger::SendIPC("INFO", debugMsg);
return false;
}
IPCDebugLogger::SendIPC("SUCCESS", "KeyValues loaded from buffer successfully");
IPCDebugLogger::SendIPC("INFO", "Creating material...");
// Create the material
CStrongHandle<CMaterial2> materialHandle;
void* result = MaterialSystemData::fnCreateMaterial(
nullptr,
&materialHandle,
materialName,
&keyValues,
0,
1
);
if (!result) {
IPCDebugLogger::SendIPC("ERROR", "CreateMaterial function returned null");
return false;
}
if (!materialHandle.pBinding) {
IPCDebugLogger::SendIPC("ERROR", "Material handle binding is null");
return false;
}
if (!materialHandle.pBinding->pData) {
IPCDebugLogger::SendIPC("ERROR", "Material data pointer is null");
return false;
}
IPCDebugLogger::SendIPC("SUCCESS", "Material created successfully!");
char infoBuffer[512];
simple_sprintf(infoBuffer, sizeof(infoBuffer), "Material handle: 0x%p", materialHandle.pBinding);
IPCDebugLogger::SendIPC("INFO", infoBuffer);
simple_sprintf(infoBuffer, sizeof(infoBuffer), "Material data: 0x%p", materialHandle.pBinding->pData);
IPCDebugLogger::SendIPC("INFO", infoBuffer);
simple_sprintf(infoBuffer, sizeof(infoBuffer), "Material ref count: %d", materialHandle.pBinding->nRefCount);
IPCDebugLogger::SendIPC("INFO", infoBuffer);
// Access CMaterial2 properties
CMaterial2* pMaterial = static_cast<CMaterial2*>(materialHandle.pBinding->pData);
simple_sprintf(infoBuffer, sizeof(infoBuffer), "Material parameter count: %d", pMaterial->m_nParameterCount);
IPCDebugLogger::SendIPC("INFO", infoBuffer);
IPCDebugLogger::SendIPC("SUCCESS", "Material creation test PASSED!");
return true;
}
} // namespace MaterialEditorTest