See More

import types from .PPtr import PPtr from ..enums import BuildTarget, ClassIDType from ..streams import EndianBinaryWriter from ..files import ObjectReader class Object(object): type_tree: dict def __init__(self, reader: ObjectReader): self.reader = reader self.type = reader.type self.m_PathID = reader.path_id self.version = reader.version self.build_type = reader.build_type self.platform = reader.platform self.serialized_type = reader.serialized_type self.byte_size = reader.byte_size self.assets_file = reader.assets_file if self.platform == BuildTarget.NoTarget: self._object_hide_flags = reader.read_u_int() self.container = ( self.assets_file._container[self.m_PathID] if self.type != ClassIDType.AssetBundle and self.m_PathID in self.assets_file._container else None ) self.reader.reset() if type(self) == Object: self.read_typetree() def has_struct_member(self, name: str) -> bool: nodes = self.reader.get_typetree_nodes() return any(node.m_Name == name for node in nodes) def dump_typetree(self, nodes: list = None) -> str: return self.reader.dump_typetree(nodes=nodes) def dump_typetree_structure(self) -> str: return self.reader.dump_typetree_structure() def read_typetree(self, nodes: list = None, wrap: bool = False, all_trees=None) -> dict: tree = self.reader.read_typetree(nodes, all_trees=all_trees) self.type_tree = NodeHelper(tree, self.assets_file) return self.type_tree if wrap else tree def save_typetree(self, nodes: list = None, writer: EndianBinaryWriter = None): def class_to_dict(value): if isinstance(value, list): return [class_to_dict(val) for val in value] elif isinstance(value, dict): return {key: class_to_dict(val) for key, val in value.items()} elif hasattr(value, "__dict__"): if isinstance(value, PPtr): return {"m_FileID": value.m_FileID, "m_PathID": value.m_PathID} return { key: class_to_dict(val) for key, val in value.__dict__.items() if not isinstance(value, (types.FunctionType, types.MethodType)) and not key in ["type_tree", "assets_file"] } else: return value obj = class_to_dict(self if not self.type_tree else self.type_tree) return self.reader.save_typetree(obj, nodes, writer) def get_raw_data(self) -> bytes: return self.reader.get_raw_data() def set_raw_data(self, data: bytes): self.reader.set_raw_data(data) def save(self, writer: EndianBinaryWriter = None, intern_call=False): if not writer: writer = EndianBinaryWriter(endian=self.reader.endian) if intern_call: if self.platform == BuildTarget.NoTarget: writer.write_u_int(self._object_hide_flags) else: # save for objects WITHOUT specific save function # so we have to use the typetree if it exists self.save_typetree() def __getattr__(self, name): """ If item not found in __dict__, read type_tree and check if it is in there. """ if name == "type_tree" or self.type_tree == None: old_pos = self.reader.Position self.read_typetree() self.reader.Position = old_pos if name == "type_tree": return self.type_tree elif name == "read": return lambda: self return getattr(self.type_tree, name) def get(self, key, default=None): return getattr(self, key, default) def __repr__(self): return f"<{self.__class__.__name__} path_id={self.m_PathID}>" def __hash__(self): if self.assets_file and hasattr(self.assets_file, "name"): return hash((self.assets_file.name, self.m_PathID)) return hash(self.m_PathID) def __eq__(self, other): same_file = True if self.assets_file and other.assets_file and hasattr(self.assets_file, "name") and hasattr(other.assets_file, "name"): same_file = (self.assets_file.name == other.assets_file.name) if isinstance(other, Object): return self.m_PathID == other.m_PathID and same_file elif isinstance(other, int): return self.m_PathID == other and same_file return False class NodeHelper: def __init__(self, data, assets_file): if "m_PathID" in data and "m_FileID" in data: # used to make pointers directly usable self.m_FileID = data["m_FileID"] self.m_PathID = data["m_PathID"] self.m_Index = data.get("m_Index", -2) self.assets_file = assets_file self._obj = None self.__class__ = PPtr else: self.__dict__ = { key: NodeHelper(val, assets_file) for key, val in data.items() } pass def __new__(cls, data, assets_file): if isinstance(data, dict): return super(NodeHelper, cls).__new__(cls) if isinstance(data, list): return [NodeHelper(x, assets_file) for x in data] if isinstance(data, tuple): return tuple(NodeHelper(x, assets_file) for x in data) return data def __getitem__(self, item): return getattr(self, item) def to_dict(self): def dump(val): return ( val.to_dict() if isinstance(val, NodeHelper) else [dump(item) for item in val] if isinstance(val, list) else {"m_FileID": val.m_FileID, "m_PathID": val.m_PathID} if isinstance(val, PPtr) else [x for x in val] if isinstance(val, (bytearray, bytes)) else val ) return {key: dump(val) for key, val in self.__dict__.items()} def items(self): return self.__dict__.items() def values(self): return self.__dict__.values() def keys(self): return self.__dict__.keys() def save(self, *args, **kwargs): raise NotImplementedError( "This is a helper structure, please create its proper parser"+ " or use Object's save_typetree() method." ) def __repr__(self): name = getattr(self, "m_Name", None) return f"" if name else ""