"""probe_csurface.py Walk s_Resources, find CSurface entries (vfptr=0x007ca4dc — GR-view). Split live vs lost, sample buffer/field non-NULL counts. CSurface members (primary-relative): +0x58 type, +0x5c handler, +0x60 color, +0x64 solid_index, +0x68 indexed_tex_id +0x6c base1map (ImgTex*) => entry+0x3c (PurgeResource releases this) +0x70 base1pal (Palette*) => entry+0x40 (Destroy releases — PurgeResource MISSES) +0x74 translucency, +0x78 luminosity, +0x7c diffuse +0x80 orig_texture_id, +0x84 orig_palette_id, +0x88 orig_lum, +0x8c orig_diff """ import ctypes, ctypes.wintypes as wt, sys, struct PROCESS_VM_READ = 0x10 PROCESS_QUERY_INFORMATION = 0x400 k = ctypes.windll.kernel32 k.OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD]; k.OpenProcess.restype = wt.HANDLE k.ReadProcessMemory.argtypes = [wt.HANDLE, wt.LPCVOID, wt.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)] k.ReadProcessMemory.restype = wt.BOOL S_RESOURCES_M_DATA = 0x008398C4 S_RESOURCES_M_NUM = 0x008398CC CSURFACE_GR_VTABLE = 0x007ca4dc def rd_u32(h, va): buf = (ctypes.c_ubyte * 4)(); sz = ctypes.c_size_t(0) if not k.ReadProcessMemory(h, va, buf, 4, ctypes.byref(sz)) or sz.value != 4: return None return struct.unpack('