#!/usr/bin/env python3 """Dump the raw Odin SerializedBytes for the two LootTablesConfig assets, and do a first-pass analysis: hexdump head, and list ASCII strings (>=3 chars) found.""" import os, sys, json, re, UnityPy from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator GAME = "/mnt/d/SteamLibrary/steamapps/common/Sand Playtest" BD = os.path.join(GAME, "Sand_Data/StreamingAssets/aa/StandaloneWindows64") META = os.path.join(GAME, "Sand_Data/il2cpp_data/Metadata/global-metadata.dat") DLL = os.path.join(GAME, "GameAssembly.dll") OUT = "/home/downloadpizza/sand_tools/extracted" gen = TypeTreeGenerator("6000.0.40f1") gen.load_il2cpp(open(DLL, "rb").read(), open(META, "rb").read()) env = UnityPy.load(os.path.join(BD, "configuration_assets_all.bundle"), os.path.join(BD, "sand_monoscripts.bundle")) for o in env.objects: if o.type.name != "MonoBehaviour": continue try: d = o.read(); nm = getattr(d, "m_Name", "") or "" except Exception: continue if "LootTables" not in nm: continue script = d.m_Script.read() full = (script.m_Namespace + "." if script.m_Namespace else "") + script.m_ClassName nodes = json.loads(gen.get_nodes_as_json(script.m_AssemblyName, full)) tree = o.read_typetree(nodes) sb = tree["serializationData"]["SerializedBytes"] data = bytes(sb) out_bin = os.path.join(OUT, f"{nm}.odin.bin") open(out_bin, "wb").write(data) print(f"\n=== {nm}: {len(data)} bytes -> {out_bin}") print("head hex:", data[:64].hex()) # ascii strings strings = re.findall(rb"[\x20-\x7e]{3,}", data) uniq = [] seen = set() for s in strings: t = s.decode() if t not in seen: seen.add(t); uniq.append(t) print(f"{len(strings)} ascii runs, {len(uniq)} unique. First 60 unique:") for t in uniq[:60]: print(" ", repr(t))