From 4f45346980416b2aeafae11bb48113bebec57049 Mon Sep 17 00:00:00 2001 From: Hipstercat Date: Tue, 9 Feb 2021 16:09:13 +0100 Subject: [PATCH] skn --- lib/fileutils.py | 9 ++ skn.py | 253 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 skn.py diff --git a/lib/fileutils.py b/lib/fileutils.py index 37dba58..5efaf43 100644 --- a/lib/fileutils.py +++ b/lib/fileutils.py @@ -25,6 +25,15 @@ def read_bytes(fp, size): return bytes(struct.unpack('<' + str(size) + 'B', fp.read(size))) +def read_string_until_none(fp) -> str: + s = "" + c = read_byte(fp) + while c != 0x00: + s += chr(c) + c = read_byte(fp) + return s + + def decompress(compressed_bytes, original_size: int): i = 0 j = 0 diff --git a/skn.py b/skn.py new file mode 100644 index 0000000..8a6cdc2 --- /dev/null +++ b/skn.py @@ -0,0 +1,253 @@ +from lib.fileutils import * + +NAM_FlagHasHandles = (1 << 0) +NAM_FlagCaseInsensitive = (1 << 1) + + +class MAP_KeyContext: + def __init__(self): + self.context = 0 + self.ppLUT = 0 + self.reserve = 0 + self.pData = 0 + + @classmethod + def parse(cls, b): + r = cls() + r.context = read_int(b) + r.ppLUT = read_int(b) + r.reserve = read_int(b) + r.pData = read_int(b) + return r + + +class MAP_Mapper: + def __init__(self): + self.pMemgroup = 0 + self.MemBaseSize = 0 + self.MemBlocks = 0 + self.MemSize = 0 + self.MemFree = 0 + self.pMem = 0 + self.MaxMappings = 0 + self.TotalMappings = 0 + self.TotalKeys = 0 + self.pKeyContexts = 0 + + @classmethod + def parse(cls, b): + r = cls() + r.pMemgroup = read_int(b) + r.MemBaseSize = read_int(b) + r.MemBlocks = read_int(b) + r.MemSize = read_int(b) + r.MemFree = read_int(b) + r.pMem = read_int(b) + r.MaxMappings = read_int(b) + r.TotalMappings = read_int(b) + r.TotalKeys = read_int(b) + r.pKeyContexts = read_int(b) + return r + +class MAP_Mapping: + def __init__(self): + self.index = 0 + + @classmethod + def parse(cls, b): + r = cls() + r.index = read_short(b) + return r + + +def read_int_and_print(n, b): + a = read_int(b) + print("%s=%s" % (n, a)) + return a + + +def read_float_and_print(n, b): + a = read_float(b) + print("%s=%s" % (n, a)) + return a + + +def read_byte_and_print(n, b): + a = read_byte(b) + print("%s=%s" % (n, a)) + return a + + +def read_short_and_print(n, b): + a = read_short(b) + print("%s=%s" % (n, a)) + return a + + +class MAP_MemGroup: + def __init__(self): + self.mem = 0 + self.memgroup = 0 + self.memlist = 0 + self.total_size = 0 + self.total_blocks = 0 + self.base = 0 + + +class MAP_Image: + def __init__(self): + self.size = 0 + self.memgroupoffset = 0 + + + +class ANM_Skin: + def __init__(self): + self.custom = 0 + self.file_version = 0 + self.image = 0 + self.memgroup = 0 + self.node_dictionary = 0 + self.tree_dictionary = 0 + self.default_obj_dictionnary = 0 + self.total_number_trees = 0 + self.total_number_objs = 0 + self.total_number_clusters = 0 + self.name = 0 + + @classmethod + def parse(cls, b): + print("MAP_Image") + read_int_and_print("size", b) + ret = b.tell() + memgroupoffset = read_int_and_print("memgroupoffset", b) + + b.seek(memgroupoffset) + + print("MAP_MemGroup") + print("\tMAP_Mem") + read_int_and_print("\tNode", b) + read_int_and_print("\tSize", b) + read_int_and_print("\tFlags", b) + + read_int_and_print("pMemGroup", b) + read_int_and_print("MemList", b) + read_int_and_print("TotalSize", b) + read_int_and_print("TotalBlocks", b) + base = read_int_and_print("pBase", b) + + b.seek(base) + print("=========") + print("ANM_Skin") + read_int_and_print("pCustom", b) + read_int_and_print("FileVersion", b) + read_int_and_print("pImage", b) + read_int_and_print("pMemGroup", b) + read_int_and_print("pNodeDictionary", b) + tree = read_int_and_print("pTreeDictionary", b) + defaultobj = read_int_and_print("pDefaultObjDictionary", b) + read_int_and_print("TotalNumberOfTrees", b) + read_int_and_print("TotalNumberOfObjs", b) + read_int_and_print("TotalNumberOfClusters", b) + print(read_string_until_none(b)) + print("=========") + + print("ANM_Skin->pTreeDictionary NAM_Dictionary") + b.seek(defaultobj) + read_int_and_print("pMemGroup", b) + namemapper = read_int_and_print("pNameMapper", b) + flags = read_int_and_print("Flags", b) + print("Flags: NAM_FlagHasHandles: %s, NAM_FlagCaseInsensitive: %s" % (flags & NAM_FlagHasHandles, flags & NAM_FlagCaseInsensitive)) + namehandle = read_short_and_print("NameHandle", b) + + + print("MAP_Image->ANM_Skin->NAM_Dictionnary->MAP_Mapper") + b.seek(namemapper) + pMemGroup = read_int_and_print("pMemGroup", b) + read_int_and_print("MemBaseSize", b) + read_int_and_print("MemBlocks", b) + read_int_and_print("MemSize", b) + read_int_and_print("MemFree", b) + mem = read_int_and_print("pMem", b) + read_int_and_print("MaxMappings", b) + totalmappings = read_int_and_print("TotalMappings", b) + read_int_and_print("TotalKeys", b) + pKeyContexts = read_int_and_print("pKeyContexts", b) + + print("MAP_Image->ANM_Skin->NAM_Dictionnary->MAP_Mapper->MAP_KeyContext NAM_STRING_KEY") + b.seek(pKeyContexts) + print("\tMAP_Image->ANM_Skin->NAM_Dictionnary->MAP_Mapper->MAP_KeyContext NAM_STRING_KEY->MAP_BinSearchContext") + read_int_and_print("\tSize", b) + read_int_and_print("\tpCallback", b) + read_int_and_print("\tIndex", b) + read_int_and_print("\tTest", b) + pLUTNAM_STRING_KEY = read_int_and_print("pLUT", b) + read_int_and_print("Reserve", b) + read_int_and_print("pData", b) + + def showdictbyindex(i): + assert(0 <= i < totalmappings) + print("LUT NAM_STRING_KEY") + b.seek(pLUTNAM_STRING_KEY) + pind0_string = read_int_and_print("LUT NAM_STRING_KEY[0]", b) + for _ in range(i): + pind0_string = read_int_and_print("LUT NAM_STRING_KEY[0]", b) + print("NAM_StringData") + b.seek(pind0_string) + print("BELOW IS CUSTOM vv") + pptr = read_short_and_print("pPtr->ANM_Obj", b) + pptr2 = read_short_and_print("pPtr2->ANM_Obj", b) + val = read_int_and_print("Value", b) + print(b.tell()) + print(read_string_until_none(b)) + + b.seek(val) + read_int_and_print("ANM_Obj->Custom", b) + read_int_and_print("ANM_Obj->Skin", b) + read_int_and_print("ANM_Obj->ZeroCluster", b) + total_vertices = read_int_and_print("ANM_Obj->TotalNumberOfVertices", b) + num_clusters = read_int_and_print("ANM_Obj->NumberOfClusters", b) + clusters = read_int_and_print("ANM_Obj->Clusters", b) + + SEEN_VERTICES_INDEX = [] + + b.seek(clusters) + for i in range(num_clusters): + print("READING CLUSTER %s" % i) + read_int_and_print("ANM_Cluster->Custom", b) + read_int_and_print("ANM_Cluster->Obj", b) + read_int_and_print("ANM_Cluster->Handle", b) + print("ANM_Cluster->BoundingBox") + print("ANM_BoundingBox->Box[0]") + read_float_and_print("X", b) + read_float_and_print("Y", b) + read_float_and_print("Z", b) + print("ANM_BoundingBox->Box[1]") + read_float_and_print("X", b) + read_float_and_print("Y", b) + read_float_and_print("Z", b) + numverts = read_int_and_print("ANM_Cluster->NumberOfVertices", b) + + for _ in range(numverts): + read_float_and_print("X", b) + read_float_and_print("Y", b) + read_float_and_print("Z", b) + vindex = read_int_and_print("Index", b) + # read_int_and_print("Weight", b) + assert(vindex not in SEEN_VERTICES_INDEX) + SEEN_VERTICES_INDEX.append(vindex) + + read_int_and_print("Unk", b) + read_int_and_print("Unk", b) + print("%s vertices in this cluster (vs %s reported)" % (len(SEEN_VERTICES_INDEX), total_vertices)) + showdictbyindex(2) + + +def main(): + with open("/home/tasty/Projects/gck-map-extract-objects/anm_skn/mc_l0.skn", "rb") as fp: + # with open("/home/tasty/Projects/gck-map-extract-objects/anm_skn/dust.skn", "rb") as fp: + ANM_Skin.parse(fp) + + +if __name__ == "__main__": + main()