diff --git a/src/il2cpp_resolver.cpp b/src/il2cpp_resolver.cpp index 442e00c..8a883fd 100644 --- a/src/il2cpp_resolver.cpp +++ b/src/il2cpp_resolver.cpp @@ -361,6 +361,208 @@ void Il2CppResolver::dumpDiagnostics(){ #endif } +void Il2CppResolver::inspectInventoryState(){ +#ifdef _WIN32 + try { + char exe[MAX_PATH]; GetModuleFileNameA(nullptr, exe, MAX_PATH); + char* slash = strrchr(exe, '\\'); if(!slash) slash = strrchr(exe, '/'); + std::string logPath = slash ? std::string(exe, slash - exe) + "\\DevourUnlocker.log" : "DevourUnlocker.log"; + std::ofstream f(logPath, std::ios::app); + f << "[inv] inspectInventoryState BEGIN\n" << std::unitbuf; + + auto domain_get = reinterpret_cast(getProc("il2cpp_domain_get")); + auto klass_from_name = reinterpret_cast(getProc("il2cpp_class_from_name")); + auto field_from_name = reinterpret_cast(getProc("il2cpp_class_get_field_from_name")); + auto field_static_get = reinterpret_cast(getProc("il2cpp_field_static_get_value")); // (field, out) + auto field_get_value = reinterpret_cast(getProc("il2cpp_field_get_value")); + auto obj_get_class = reinterpret_cast(getProc("il2cpp_object_get_class")); + auto class_get_name_fn = reinterpret_cast(getProc("il2cpp_class_get_name")); + auto arr_elem_size_cls = reinterpret_cast(getProc("il2cpp_class_array_element_size")); + auto fields_iter = reinterpret_cast(getProc("il2cpp_class_get_fields")); + auto field_name_fn = reinterpret_cast(getProc("il2cpp_field_get_name")); + auto field_off = reinterpret_cast(getProc("il2cpp_field_get_offset")); + + if(!domain_get || !klass_from_name || !field_from_name || !field_static_get || !field_get_value || !obj_get_class || !class_get_name_fn){ + f << "[inv] missing exports\n"; return; + } + void* domain = domain_get(); + if(!domain){ f << "[inv] no domain\n"; return; } + + // find SteamInventoryManager class across assemblies + auto dom_asm = reinterpret_cast(getProc("il2cpp_domain_get_assemblies")); + auto asm_img = reinterpret_cast(getProc("il2cpp_assembly_get_image")); + if(!dom_asm || !asm_img){ f << "[inv] no asm exports\n"; return; } + size_t n=0; const void** asms = dom_asm(domain,&n); + void* simClass=nullptr; + for(size_t i=0;i: _items @0x10, _size @0x18 + unsigned char* lst=(unsigned char*)val; + void* arr = *(void**)(lst+0x10); + int32_t lsize = *(int32_t*)(lst+0x18); + f << "[inv] _items="<(getProc("il2cpp_domain_get")); + auto thread_attach = reinterpret_cast(getProc("il2cpp_thread_attach")); + auto klass_from_name = reinterpret_cast(getProc("il2cpp_class_from_name")); + auto field_from_name = reinterpret_cast(getProc("il2cpp_class_get_field_from_name")); + auto field_static_get = reinterpret_cast(getProc("il2cpp_field_static_get_value")); + auto field_get_value = reinterpret_cast(getProc("il2cpp_field_get_value")); + auto obj_get_class = reinterpret_cast(getProc("il2cpp_object_get_class")); + auto class_get_name_fn = reinterpret_cast(getProc("il2cpp_class_get_name")); + auto get_method = reinterpret_cast(getProc("il2cpp_class_get_method_from_name")); + auto invoke_fn = reinterpret_cast(getProc("il2cpp_runtime_invoke")); + if(!domain_get || !thread_attach || !klass_from_name || !field_from_name || !field_static_get || + !field_get_value || !obj_get_class || !class_get_name_fn || !get_method || !invoke_fn) return -1; + void* domain = domain_get(); + if(!domain) return -1; + if(thread_attach) thread_attach(domain); + + auto dom_asm = reinterpret_cast(getProc("il2cpp_domain_get_assemblies")); + auto asm_img = reinterpret_cast(getProc("il2cpp_assembly_get_image")); + if(!dom_asm || !asm_img) return -1; + size_t n=0; const void** asms = dom_asm(domain,&n); + + // SteamInventoryManager class + Steamworks.SteamItemDef_t enum class + void* simClass=nullptr; void* defEnumClass=nullptr; + for(size_t i=0;i, enum is 4-byte int despite array_element_size saying 8) + std::vector have; + if(uArr){ + unsigned char* data=(unsigned char*)uArr+0x20; + for(int i=0;i= cSize && cSize>0) return 0; // already fully populated + void* addMethod = get_method(obj_get_class(userList),"Add",1); + if(!addMethod) return -1; + + unsigned char* cData=(unsigned char*)cArr+0x20; + + // DIAGNOSTIC: hex-dump sample ids so mismatches are visible + auto dumpIds=[&](const char* tag){ + try{ + char exe[MAX_PATH]; GetModuleFileNameA(nullptr,exe,MAX_PATH); + char* s=strrchr(exe,'\\'); if(!s) s=strrchr(exe,'/'); if(!s) return; + std::ofstream f(std::string(exe,s-exe)+"\\DevourUnlocker.log",std::ios::app); + f<<"[dbg] "<0){ + unsigned char* dNow=(unsigned char*)uArrNow+0x20; + uint64_t last=(uint64_t)*(uint32_t*)(dNow+(size_t)(szNow-1)*4); + try{ + char exe[MAX_PATH]; GetModuleFileNameA(nullptr,exe,MAX_PATH); + char* s=strrchr(exe,'\\'); if(!s) s=strrchr(exe,'/'); + if(s){ std::ofstream f(std::string(exe,s-exe)+"\\DevourUnlocker.log",std::ios::app); + f<::Add). + // Returns number newly injected. Safe to call repeatedly — the game recreates the + // list on inventory refresh, so run this periodically. + static int fabricateOwnership(); + // Accessors used by the inspector/fabricator + static void* getProcPub(const char* name) { return getProc(name); } static uintptr_t gameAssemblyBase(); static size_t gameAssemblySize(); }; diff --git a/src/patches.cpp b/src/patches.cpp index 1db6103..6469028 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -324,6 +324,25 @@ bool installPatches() { } g_installed = any; logPatch(any ? "[DevourUnlocker] Patches installed (some may retry)" : "[DevourUnlocker] No patches installed"); + // Ownership fabricator + introspection thread: + // - every 15s inject missing catalog ids into userItems (list is recreated on refresh) + // - one layout dump at 30s for diagnostics + CreateThread(nullptr, 0, [](LPVOID)->DWORD{ + Sleep(30000); + logPatch("[DevourUnlocker] inspectInventoryState @30s"); + Il2CppResolver::inspectInventoryState(); + int total=0; + for(int cycle=0; cycle<240; ++cycle){ // ~60 min of refills + Sleep(15000); + int n = Il2CppResolver::fabricateOwnership(); + if(n>0){ + total+=n; + char b[128]; snprintf(b,sizeof(b),"[DevourUnlocker] ownership injected +%d (session total %d)",n,total); + logPatch(b); + } + } + return 0; + }, nullptr, 0, nullptr); return any; }