Python tooling for decoding walker saves and mining game data: sand.py / build_wbt.py / walker_hashes.py / harvest_hashes.py (.wbt codec + hashes), extract_*/loot_probe/odin_read/unitybundle (asset parsing), make_*_wiki + render_wiki (wiki generation), recover_key. Paths point at the local extracted/, wiki/, and Walkers symlink.
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
#!/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))
|