122 lines
3.6 KiB
Python
122 lines
3.6 KiB
Python
from .ikv_loader import parse_ikv_text, extract_handles_recursive
|
|
|
|
|
|
def try_load_material_from_bytes(data):
|
|
try:
|
|
txt = data.decode("utf-8", errors="strict")
|
|
except Exception:
|
|
try:
|
|
txt = data.decode("utf-8", errors="ignore")
|
|
except Exception:
|
|
return None
|
|
|
|
s = txt.lstrip()
|
|
if not (s.startswith("ikv1") or s.startswith("{") or s.startswith('"')):
|
|
return None
|
|
|
|
obj = parse_ikv_text(txt)
|
|
|
|
root_handle = None
|
|
if isinstance(obj, dict) and "ihandle" in obj and isinstance(obj["ihandle"], dict):
|
|
d = obj["ihandle"]
|
|
if all(k in d for k in ("value", "type", "meta")) and all(
|
|
isinstance(d[k], int) for k in ("value", "type", "meta")
|
|
):
|
|
v = int(d["value"]) & 0xFFFFFFFF
|
|
t = int(d["type"]) & 0xFFFF
|
|
m = int(d["meta"]) & 0xFFFF
|
|
if t != 0:
|
|
root_handle = (v, t, m)
|
|
|
|
refs = extract_handles_recursive(obj)
|
|
|
|
if root_handle is not None:
|
|
rk = root_handle[0] | (root_handle[1] << 32) | (root_handle[2] << 48)
|
|
refs = [h for h in refs if ((h[0] | (h[1] << 32) | (h[2] << 48)) != rk)]
|
|
|
|
info = {}
|
|
if isinstance(obj, dict):
|
|
if "shader_id" in obj:
|
|
info["shader_id"] = obj.get("shader_id")
|
|
if "flags" in obj:
|
|
info["flags"] = obj.get("flags")
|
|
|
|
return {
|
|
"handle": root_handle,
|
|
"refs": refs,
|
|
"obj": obj,
|
|
"info": info,
|
|
"text_len": len(txt),
|
|
}
|
|
|
|
|
|
def pack_value_from_index_gen(index, generation):
|
|
return ((int(generation) & 0xFFFF) << 16) | (int(index) & 0xFFFF)
|
|
|
|
|
|
def extract_handles_recursive(node):
|
|
out = []
|
|
|
|
def is_valid_triplet(v, t, m):
|
|
v = int(v) & 0xFFFFFFFF
|
|
t = int(t) & 0xFFFF
|
|
m = int(m) & 0xFFFF
|
|
if t == 0:
|
|
return False
|
|
return True
|
|
|
|
def maybe_add_value_type_meta(d):
|
|
if not isinstance(d, dict):
|
|
return
|
|
if "value" in d and "type" in d and "meta" in d:
|
|
v = d["value"]
|
|
t = d["type"]
|
|
m = d["meta"]
|
|
if isinstance(v, int) and isinstance(t, int) and isinstance(m, int):
|
|
if is_valid_triplet(v, t, m):
|
|
out.append((int(v) & 0xFFFFFFFF, int(t) & 0xFFFF, int(m) & 0xFFFF))
|
|
|
|
def maybe_add_index_gen(d):
|
|
if not isinstance(d, dict):
|
|
return
|
|
if "index" in d and "generation" in d and "type" in d and "meta" in d:
|
|
idx = d["index"]
|
|
gen = d["generation"]
|
|
t = d["type"]
|
|
m = d["meta"]
|
|
if (
|
|
isinstance(idx, int)
|
|
and isinstance(gen, int)
|
|
and isinstance(t, int)
|
|
and isinstance(m, int)
|
|
):
|
|
if (idx == 0 and gen == 0) or int(t) == 0:
|
|
return
|
|
v = pack_value_from_index_gen(idx, gen)
|
|
if is_valid_triplet(v, t, m):
|
|
out.append((int(v) & 0xFFFFFFFF, int(t) & 0xFFFF, int(m) & 0xFFFF))
|
|
|
|
def visit(x):
|
|
if isinstance(x, dict):
|
|
if "ihandle" in x and isinstance(x["ihandle"], dict):
|
|
maybe_add_value_type_meta(x["ihandle"])
|
|
maybe_add_index_gen(x["ihandle"])
|
|
maybe_add_value_type_meta(x)
|
|
maybe_add_index_gen(x)
|
|
for vv in x.values():
|
|
visit(vv)
|
|
elif isinstance(x, list):
|
|
for vv in x:
|
|
visit(vv)
|
|
|
|
visit(node)
|
|
|
|
seen = set()
|
|
uniq = []
|
|
for h in out:
|
|
k = h[0] | (h[1] << 32) | (h[2] << 48)
|
|
if k not in seen:
|
|
seen.add(k)
|
|
uniq.append(h)
|
|
return uniq
|