From 7e456278d84d20c1f94fd237dace54fd6f782db1 Mon Sep 17 00:00:00 2001 From: Sunset <26019675+lwd-temp@users.noreply.github.com> Date: Thu, 12 Jan 2023 19:57:09 +0800 Subject: [PATCH] rm unitypack --- requirements.txt | 10 +- unitypack/__init__.py | 20 --- unitypack/asset.py | 205 --------------------------- unitypack/assetbundle.py | 257 ---------------------------------- unitypack/classes.json | 1 - unitypack/engine/__init__.py | 15 -- unitypack/engine/animation.py | 84 ----------- unitypack/engine/audio.py | 85 ----------- unitypack/engine/component.py | 17 --- unitypack/engine/font.py | 12 -- unitypack/engine/mesh.py | 42 ------ unitypack/engine/movie.py | 9 -- unitypack/engine/object.py | 32 ----- unitypack/engine/particle.py | 39 ------ unitypack/engine/physics.py | 43 ------ unitypack/engine/renderer.py | 83 ----------- unitypack/engine/text.py | 65 --------- unitypack/engine/texture.py | 257 ---------------------------------- unitypack/enums.py | 50 ------- unitypack/environment.py | 87 ------------ unitypack/exceptions.py | 5 - unitypack/export.py | 197 -------------------------- unitypack/object.py | 232 ------------------------------ unitypack/resources.py | 18 --- unitypack/strings.dat | Bin 1051 -> 0 bytes unitypack/structs.dat | Bin 295489 -> 0 bytes unitypack/type.py | 164 ---------------------- unitypack/utils.py | 114 --------------- 28 files changed, 3 insertions(+), 2140 deletions(-) delete mode 100644 unitypack/__init__.py delete mode 100644 unitypack/asset.py delete mode 100644 unitypack/assetbundle.py delete mode 100644 unitypack/classes.json delete mode 100644 unitypack/engine/__init__.py delete mode 100644 unitypack/engine/animation.py delete mode 100644 unitypack/engine/audio.py delete mode 100644 unitypack/engine/component.py delete mode 100644 unitypack/engine/font.py delete mode 100644 unitypack/engine/mesh.py delete mode 100644 unitypack/engine/movie.py delete mode 100644 unitypack/engine/object.py delete mode 100644 unitypack/engine/particle.py delete mode 100644 unitypack/engine/physics.py delete mode 100644 unitypack/engine/renderer.py delete mode 100644 unitypack/engine/text.py delete mode 100644 unitypack/engine/texture.py delete mode 100644 unitypack/enums.py delete mode 100644 unitypack/environment.py delete mode 100644 unitypack/exceptions.py delete mode 100644 unitypack/export.py delete mode 100644 unitypack/object.py delete mode 100644 unitypack/resources.py delete mode 100644 unitypack/strings.dat delete mode 100644 unitypack/structs.dat delete mode 100644 unitypack/type.py delete mode 100644 unitypack/utils.py diff --git a/requirements.txt b/requirements.txt index fb1f8f6a..5ae2922a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,3 @@ -astc_decomp==1.0.3 -decrunch==0.4.0.post1 -fsb5==1.0 -lz4==3.1.10 -Pillow==9.0.1 -requests==2.26.0 -setuptools==58.1.0 +Pillow==9.4.0 +requests==2.28.1 +UnityPy==1.9.24 \ No newline at end of file diff --git a/unitypack/__init__.py b/unitypack/__init__.py deleted file mode 100644 index 78ea1442..00000000 --- a/unitypack/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -import pkg_resources - - -__version__ = pkg_resources.require("unitypack")[0].version - - -def load(file, env=None): - from .environment import UnityEnvironment - - if env is None: - env = UnityEnvironment() - return env.load(file) - - -def load_from_file(file, env=None): - from .environment import UnityEnvironment - - if env is None: - env = UnityEnvironment() - return env.get_asset_by_filename(file) diff --git a/unitypack/asset.py b/unitypack/asset.py deleted file mode 100644 index 137a6bce..00000000 --- a/unitypack/asset.py +++ /dev/null @@ -1,205 +0,0 @@ -import logging -import lzma -import os -from binascii import hexlify -from io import BytesIO -from uuid import UUID - -from .exceptions import ArchiveNotFound -from .object import ObjectInfo -from .type import TypeMetadata -from .utils import BinaryReader - -logger = logging.getLogger(__name__) - - -LIBRARY_UNITY_DEFAULT_RESOURCES = "library/unity default resources" - - -class Asset: - @classmethod - def from_bundle(cls, bundle, buf): - ret = cls() - ret.bundle = bundle - ret.environment = bundle.environment - offset = buf.tell() - ret._buf = BinaryReader(buf, endian=">") - - if bundle.is_unityfs: - ret._buf_ofs = buf.tell() - return ret - - if not bundle.compressed: - ret.name = buf.read_string() - header_size = buf.read_uint() - buf.read_uint() # size - else: - header_size = bundle.asset_header_size - - # FIXME: this offset needs to be explored more - ofs = buf.tell() - if bundle.compressed: - dec = lzma.LZMADecompressor() - data = dec.decompress(buf.read()) - ret._buf = BinaryReader(BytesIO(data[header_size:]), endian=">") - ret._buf_ofs = 0 - buf.seek(ofs) - else: - ret._buf_ofs = offset + header_size - 4 - if ret.is_resource: - ret._buf_ofs -= len(ret.name) - - return ret - - @classmethod - def from_file(cls, file, environment=None): - ret = cls() - ret.name = file.name - ret._buf_ofs = file.tell() - ret._buf = BinaryReader(file) - base_path = os.path.abspath(os.path.dirname(file.name)) - if environment is None: - from .environment import UnityEnvironment - ret.environment = UnityEnvironment(base_path=base_path) - return ret - - def get_asset(self, path): - try: - if ":" in path: - return self.environment.get_asset(path) - elif path == LIBRARY_UNITY_DEFAULT_RESOURCES: - logger.warning("Refusing to load " + LIBRARY_UNITY_DEFAULT_RESOURCES) - raise NotImplementedError( - LIBRARY_UNITY_DEFAULT_RESOURCES + " cannot be loaded" - ) - return self.environment.get_asset_by_filename(path) - except ArchiveNotFound as e: - logger.warning(str(e)) - return None - - def __init__(self): - self._buf_ofs = None - self._objects = {} - self.adds = [] - self.asset_refs = [self] - self.types = {} - self.typenames = {} - self.bundle = None - self.name = "" - self.long_object_ids = False - self.tree = TypeMetadata(self) - self.loaded = False - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.name) - - @property - def objects(self): - if not self.loaded: - self.load() - return self._objects - - @property - def is_resource(self): - return self.name.endswith(".resource") or self.name.endswith(".resS") - - def load(self): - if self.is_resource: - self.loaded = True - return - - buf = self._buf - buf.seek(self._buf_ofs) - buf.endian = ">" - - self.metadata_size = buf.read_uint() - self.file_size = buf.read_uint() - self.format = buf.read_uint() - self.data_offset = buf.read_uint() - - if self.format >= 9: - self.endianness = buf.read_uint() - if self.endianness == 0: - buf.endian = "<" - - self.tree.load(buf) - - if 7 <= self.format <= 13: - self.long_object_ids = bool(buf.read_uint()) - - num_objects = buf.read_uint() - for i in range(num_objects): - if self.format >= 14: - buf.align() - obj = ObjectInfo(self) - obj.load(buf) - self.register_object(obj) - - if self.format >= 11: - num_adds = buf.read_uint() - for i in range(num_adds): - if self.format >= 14: - buf.align() - id = self.read_id(buf) - self.adds.append((id, buf.read_int())) - - if self.format >= 6: - num_refs = buf.read_uint() - for i in range(num_refs): - ref = AssetRef(self) - ref.load(buf) - self.asset_refs.append(ref) - - unk_string = buf.read_string() - assert not unk_string, repr(unk_string) - self.loaded = True - - def read_id(self, buf): - if self.format >= 14: - return buf.read_int64() - else: - return buf.read_int() - - def register_object(self, obj): - if obj.type_id in self.tree.type_trees: - self.types[obj.type_id] = self.tree.type_trees[obj.type_id] - elif obj.type_id not in self.types: - trees = TypeMetadata.default(self).type_trees - if obj.class_id in trees: - self.types[obj.type_id] = trees[obj.class_id] - else: - logger.warning("%r absent from structs.dat", obj.class_id) - self.types[obj.type_id] = None - - if obj.path_id in self._objects: - raise ValueError("Duplicate asset object: %r (path_id=%r)" % (obj, obj.path_id)) - - self._objects[obj.path_id] = obj - - def pretty(self): - ret = [] - for id, tree in self.tree.type_trees.items(): - ret.append("%i:" % (id)) - for child in tree.children: - ret.append("\t" + repr(child)) - return "\n".join(ret) - - -class AssetRef: - def __init__(self, source): - self.source = source - - def __repr__(self): - return "<%s (asset_path=%r, guid=%r, type=%r, file_path=%r)>" % ( - self.__class__.__name__, self.asset_path, self.guid, self.type, self.file_path - ) - - def load(self, buf): - self.asset_path = buf.read_string() - self.guid = UUID(hexlify(buf.read(16)).decode("utf-8")) - self.type = buf.read_int() - self.file_path = buf.read_string() - self.asset = None - - def resolve(self): - return self.source.get_asset(self.file_path) diff --git a/unitypack/assetbundle.py b/unitypack/assetbundle.py deleted file mode 100644 index 8c6f7a16..00000000 --- a/unitypack/assetbundle.py +++ /dev/null @@ -1,257 +0,0 @@ -import lzma -import struct -from io import BytesIO - -from .asset import Asset -from .enums import CompressionType -from .utils import BinaryReader, lz4_decompress - - -SIGNATURE_RAW = "UnityRaw" -SIGNATURE_WEB = "UnityWeb" -SIGNATURE_FS = "UnityFS" - - -class AssetBundle: - def __init__(self, environment): - self.environment = environment - self.assets = [] - - def __repr__(self): - if hasattr(self, "name"): - return "<%s %r>" % (self.__class__.__name__, self.name) - return "<%s>" % (self.__class__.__name__) - - @property - def is_unityfs(self): - return self.signature == SIGNATURE_FS - - @property - def compressed(self): - return self.signature == SIGNATURE_WEB - - def load(self, file): - buf = BinaryReader(file, endian=">") - self.path = file.name - - # Verify that the format starts with b"Unity" - position = buf.tell() - if buf.read(5) != b"Unity": - raise NotImplementedError("File does not start with b'Unity': %r" % self.path) - buf.seek(position) - - self.signature = buf.read_string() - self.format_version = buf.read_int() - self.unity_version = buf.read_string() - self.generator_version = buf.read_string() - - if self.is_unityfs: - self.load_unityfs(buf) - elif self.signature in (SIGNATURE_RAW, SIGNATURE_WEB): - self.load_raw(buf) - else: - raise NotImplementedError("Unrecognized file signature %r in %r" % (self.signature, self.path)) - - def load_raw(self, buf): - self.file_size = buf.read_uint() - self.header_size = buf.read_int() - - self.file_count = buf.read_int() - self.bundle_count = buf.read_int() - - if self.format_version >= 2: - self.bundle_size = buf.read_uint() # without header_size - - if self.format_version >= 3: - self.uncompressed_bundle_size = buf.read_uint() # without header_size - - if self.header_size >= 60: - self.compressed_file_size = buf.read_uint() # with header_size - self.asset_header_size = buf.read_uint() - - buf.read_int() - buf.read_byte() - self.name = buf.read_string() - - # Preload assets - buf.seek(self.header_size) - if not self.compressed: - num_assets = buf.read_int() - else: - num_assets = 1 - for i in range(num_assets): - asset = Asset.from_bundle(self, buf) - self.assets.append(asset) - - def read_compressed_data(self, buf, compression): - data = buf.read(self.ciblock_size) - if compression == CompressionType.NONE: - return data - - if compression in (CompressionType.LZ4, CompressionType.LZ4HC): - return lz4_decompress(data, self.uiblock_size) - - raise NotImplementedError("Unimplemented compression method: %r" % (compression)) - - def load_unityfs(self, buf): - self.file_size = buf.read_int64() - self.ciblock_size = buf.read_uint() - self.uiblock_size = buf.read_uint() - flags = buf.read_uint() - if self.format_version >= 7: - buf.seek((buf.tell() + 15) // 16 * 16) - compression = CompressionType(flags & 0x3F) - eof_metadata = flags & 0x80 - if eof_metadata: - orig_pos = buf.tell() - buf.seek(-self.ciblock_size, 2) - data = self.read_compressed_data(buf, compression) - if eof_metadata: - buf.seek(orig_pos) - - blk = BinaryReader(BytesIO(data), endian=">") - self.guid = blk.read(16) - num_blocks = blk.read_int() - blocks = [] - for i in range(num_blocks): - busize, bcsize = blk.read_int(), blk.read_int() - bflags = blk.read_int16() - blocks.append(ArchiveBlockInfo(busize, bcsize, bflags)) - - num_nodes = blk.read_int() - nodes = [] - for i in range(num_nodes): - ofs = blk.read_int64() - size = blk.read_int64() - status = blk.read_int() - name = blk.read_string() - nodes.append((ofs, size, status, name)) - - storage = ArchiveBlockStorage(blocks, buf) - for ofs, size, status, name in nodes: - storage.seek(ofs) - asset = Asset.from_bundle(self, storage) - asset.name = name - self.assets.append(asset) - - # Hacky - self.name = self.assets[0].name - - -class ArchiveBlockInfo: - def __init__(self, usize, csize, flags): - self.uncompressed_size = usize - self.compressed_size = csize - self.flags = flags - - def __repr__(self): - return "<%s: %d %d %r %r>" % ( - self.__class__.__name__, - self.uncompressed_size, self.compressed_size, - self.compressed, self.compression_type - ) - - @property - def compressed(self): - return self.compression_type != CompressionType.NONE - - @property - def compression_type(self): - return CompressionType(self.flags & 0x3f) - - def decompress(self, buf): - if not self.compressed: - return buf - ty = self.compression_type - if ty == CompressionType.LZMA: - props, dict_size = struct.unpack(" 0: - if len(part) == 0: - raise EOFError() - size -= len(part) - self.cursor += len(part) - buf += part - return bytes(buf) - - def seek(self, offset, whence=0): - new_cursor = 0 - if whence == 1: - new_cursor = offset + self.cursor - elif whence == 2: - new_cursor = self.maxpos + offset - else: - new_cursor = offset - if self.cursor != new_cursor: - self._seek(new_cursor) - - def tell(self): - return self.cursor - - def _seek(self, new_cursor): - self.cursor = new_cursor - if not self.in_current_block(new_cursor): - self.seek_to_block(new_cursor) - self.current_stream.seek(new_cursor - self.current_block_start) - - def in_current_block(self, pos): - if self.current_block is None: - return False - end = self.current_block_start + self.current_block.uncompressed_size - return self.current_block_start <= pos and pos < end - - def seek_to_block(self, pos): - baseofs = 0 - ofs = 0 - for b in self.blocks: - if ofs + b.uncompressed_size > pos: - self.current_block = b - break - baseofs += b.compressed_size - ofs += b.uncompressed_size - else: - self.current_block = None - self.current_stream = BytesIO(b"") - return - - self.current_block_start = ofs - self.stream.seek(self.basepos + baseofs) - buf = BytesIO(self.stream.read(self.current_block.compressed_size)) - self.current_stream = self.current_block.decompress(buf) diff --git a/unitypack/classes.json b/unitypack/classes.json deleted file mode 100644 index 520df20c..00000000 --- a/unitypack/classes.json +++ /dev/null @@ -1 +0,0 @@ -{"1": "GameObject", "2": "Component", "3": "LevelGameManager", "4": "Transform", "5": "TimeManager", "6": "GlobalGameManager", "7": "GameManager", "8": "Behaviour", "9": "GameManager", "1034": "NativeFormatImporter", "11": "AudioManager", "12": "ParticleAnimator", "13": "InputManager", "1038": "LibraryAssetImporter", "15": "EllipsoidParticleEmitter", "1040": "ModelImporter", "17": "Pipeline", "18": "EditorExtension", "19": "Physics2DSettings", "20": "Camera", "21": "Material", "1046": "DDSImporter", "23": "MeshRenderer", "1048": "InspectorExpandedState", "25": "Renderer", "26": "ParticleRenderer", "27": "Texture", "28": "Texture2D", "29": "SceneSettings", "30": "GraphicsSettings", "31": "PipelineManager", "33": "MeshFilter", "35": "GameManager", "1030": "DefaultImporter", "41": "OcclusionPortal", "43": "Mesh", "45": "Skybox", "46": "GameManager", "47": "QualitySettings", "48": "Shader", "49": "TextAsset", "50": "Rigidbody2D", "51": "Physics2DManager", "52": "NotificationManager", "53": "Collider2D", "54": "Rigidbody", "55": "PhysicsManager", "56": "Collider", "57": "Joint", "58": "CircleCollider2D", "59": "HingeJoint", "60": "PolygonCollider2D", "61": "BoxCollider2D", "62": "PhysicsMaterial2D", "63": "GameManager", "64": "MeshCollider", "65": "BoxCollider", "66": "SpriteCollider2D", "1035": "MonoImporter", "68": "EdgeCollider2D", "71": "AnimationManager", "72": "ComputeShader", "74": "AnimationClip", "75": "ConstantForce", "76": "WorldParticleCollider", "1101": "AnimatorStateTransition", "78": "TagManager", "1037": "AssetServerCache", "81": "AudioListener", "82": "AudioSource", "83": "AudioClip", "84": "RenderTexture", "1109": "AnimatorTransition", "1110": "SpeedTreeImporter", "87": "MeshParticleEmitter", "88": "ParticleEmitter", "89": "Cubemap", "90": "Avatar", "91": "AnimatorController", "92": "GUILayer", "93": "RuntimeAnimatorController", "94": "ScriptMapper", "95": "Animator", "96": "TrailRenderer", "98": "DelayedCallManager", "102": "TextMesh", "1041": "FBXImporter", "104": "RenderSettings", "108": "Light", "109": "CGProgram", "110": "BaseAnimationTrack", "111": "Animation", "114": "MonoBehaviour", "115": "MonoScript", "116": "MonoManager", "117": "Texture3D", "118": "NewAnimationTrack", "119": "Projector", "120": "LineRenderer", "121": "Flare", "122": "Halo", "123": "LensFlare", "124": "FlareLayer", "125": "HaloLayer", "126": "NavMeshAreas", "127": "HaloManager", "128": "Font", "129": "PlayerSettings", "130": "NamedObject", "131": "GUITexture", "132": "GUIText", "133": "GUIElement", "134": "PhysicMaterial", "135": "SphereCollider", "136": "CapsuleCollider", "137": "SkinnedMeshRenderer", "138": "FixedJoint", "1027": "GUIDSerializer", "140": "RaycastCollider", "141": "BuildSettings", "142": "AssetBundle", "143": "CharacterController", "144": "CharacterJoint", "145": "SpringJoint", "146": "WheelCollider", "147": "ResourceManager", "148": "NetworkView", "149": "NetworkManager", "150": "PreloadData", "1049": "AnnotationManager", "152": "MovieTexture", "153": "ConfigurableJoint", "154": "TerrainCollider", "155": "MasterServerInterface", "156": "TerrainData", "157": "LightmapSettings", "158": "WebCamTexture", "159": "EditorSettings", "160": "InteractiveCloth", "161": "ClothRenderer", "162": "EditorUserSettings", "163": "SkinnedCloth", "164": "AudioReverbFilter", "165": "AudioHighPassFilter", "166": "AudioChorusFilter", "167": "AudioReverbZone", "168": "AudioEchoFilter", "169": "AudioLowPassFilter", "170": "AudioDistortionFilter", "171": "SparseTexture", "1053": "ASTCImporter", "1113": "LightmapParameters", "180": "AudioBehaviour", "181": "AudioFilter", "182": "WindZone", "183": "Cloth", "184": "SubstanceArchive", "185": "ProceduralMaterial", "186": "ProceduralTexture", "1029": "DefaultAsset", "191": "OffMeshLink", "192": "OcclusionArea", "193": "Tree", "194": "NavMeshObsolete", "195": "NavMeshAgent", "196": "NavMeshSettings", "197": "LightProbesLegacy", "198": "ParticleSystem", "199": "ParticleSystemRenderer", "200": "ShaderVariantCollection", "205": "LODGroup", "206": "BlendTree", "207": "Motion", "208": "NavMeshObstacle", "210": "TerrainInstance", "212": "SpriteRenderer", "213": "Sprite", "214": "CachedSpriteAtlas", "215": "ReflectionProbe", "216": "ReflectionProbes", "1031": "TextScriptImporter", "220": "LightProbeGroup", "221": "AnimatorOverrideController", "222": "CanvasRenderer", "223": "Canvas", "224": "RectTransform", "225": "CanvasGroup", "226": "BillboardAsset", "227": "BillboardRenderer", "228": "SpeedTreeWindAsset", "229": "AnchoredJoint2D", "230": "Joint2D", "231": "SpringJoint2D", "232": "DistanceJoint2D", "233": "HingeJoint2D", "234": "SliderJoint2D", "235": "WheelJoint2D", "238": "NavMeshData", "240": "AudioMixer", "241": "AudioMixerController", "243": "AudioMixerGroupController", "244": "AudioMixerEffectController", "245": "AudioMixerSnapshotController", "246": "PhysicsUpdateBehaviour2D", "247": "ConstantForce2D", "248": "Effector2D", "249": "AreaEffector2D", "250": "PointEffector2D", "251": "PlatformEffector2D", "252": "SurfaceEffector2D", "258": "LightProbes", "1045": "EditorBuildSettings", "271": "SampleClip", "272": "AudioMixerSnapshot", "273": "AudioMixerGroup", "1032": "SceneAsset", "1028": "AssetMetaData", "290": "AssetBundleManifest", "300": "RuntimeInitializeOnLoadManager", "1050": "PluginImporter", "1042": "TrueTypeFontImporter", "1051": "EditorUserBuildSettings", "1120": "LightmapSnapshot", "1052": "PVRImporter", "1026": "HierarchyState", "1054": "KTXImporter", "1044": "MovieImporter", "1112": "SubstanceImporter", "1111": "AnimatorTransitionBase", "1102": "AnimatorState", "1107": "AnimatorStateMachine", "1105": "HumanTemplate", "1001": "Prefab", "1002": "EditorExtensionImpl", "1003": "AssetImporter", "1004": "AssetDatabase", "1005": "Mesh3DSImporter", "1006": "TextureImporter", "1007": "ShaderImporter", "1008": "ComputeShaderImporter", "1011": "AvatarMask", "1108": "PreviewAssetType", "1020": "AudioImporter"} diff --git a/unitypack/engine/__init__.py b/unitypack/engine/__init__.py deleted file mode 100644 index e2f73cb0..00000000 --- a/unitypack/engine/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from .animation import ( - Animation, AnimationClip, Animator, AnimatorController, Motion, - ParticleAnimator, RuntimeAnimatorController -) -from .audio import AudioClip, AudioSource, StreamedResource -from .component import Behaviour, Component, Transform -from .font import Font -from .mesh import Mesh, SubMesh, VertexData, MeshFilter -from .movie import MovieTexture -from .object import GameObject -from .particle import EllipsoidParticleEmitter, MeshParticleEmitter, ParticleEmitter, ParticleSystem -from .physics import BoxCollider, BoxCollider2D, Collider, Collider2D, Rigidbody2D -from .renderer import MeshRenderer, ParticleRenderer, ParticleSystemRenderer, Renderer -from .text import TextAsset, TextMesh, Shader -from .texture import Material, Sprite, Texture2D, StreamingInfo diff --git a/unitypack/engine/animation.py b/unitypack/engine/animation.py deleted file mode 100644 index d9607c18..00000000 --- a/unitypack/engine/animation.py +++ /dev/null @@ -1,84 +0,0 @@ -from enum import IntEnum - -from .component import Behaviour, Component -from .object import Object, field - - -class Animation(Behaviour): - animate_physics = field("m_AnimatePhysics", bool) - culling_type = field("m_CullingType") - play_automatically = field("m_PlayAutomatically", bool) - wrap_mode = field("m_WrapMode") - animation = field("m_Animation") - animations = field("m_Animations") - - -class Motion(Object): - pass - - -class AnimationClip(Motion): - pass - - -class RuntimeAnimatorController(Object): - animation_clips = field("m_AnimationClips") - - -class AnimatorController(RuntimeAnimatorController): - controller = field("m_Controller") - controller_size = field("m_ControllerSize") - multithreaded_state_machine = field("m_MultiThreadedStateMachine") - state_machine_behaviours = field("m_StateMachineBehaviours") - state_machine_behaviour_vector_description = field("m_StateMachineBehaviourVectorDescription") - TOS = field("m_TOS") - - -class AnimatorCullingMode(IntEnum): - AlwaysAnimate = 0 - CullUpdateTransforms = 1 - CullCompletely = 2 - BasedOnRenderers = 1 - - -class AnimatorUpdateMode(IntEnum): - Normal = 0 - AnimatePhysics = 1 - unscaledTime = 2 - - -class Animator(Behaviour): - allow_constant_clip_sampling_optimization = field("m_AllowConstantClipSamplingOptimization", bool) - apply_root_motion = field("m_ApplyRootMotion", bool) - avatar = field("m_Avatar") - controller = field("m_Controller") - culling_mode = field("m_CullingMode", AnimatorCullingMode) - has_transform_hierarchy = field("m_HasTransformHierarchy", bool) - linear_velocity_binding = field("m_LinearVelocityBlending", bool) - update_mode = field("m_UpdateMode", AnimatorUpdateMode) - - -class ParticleAnimator(Component): - autodestruct = field("autodestruct", bool) - damping = field("damping") - does_animate_color = field("Does Animate Color?", bool) - force = field("force") - local_rotation_axis = field("localRotationAxis") - rnd_force = field("rndForce") - stop_simulation = field("stopSimulation") - size_grow = field("sizeGrow") - world_rotation_axis = field("worldRotationAxis") - - @property - def color_animation(self): - ret = [] - i = 0 - while True: - k = "colorAnimation[%i]" % (i) - if k in self._obj: - ret.append(self._obj[k]) - else: - break - i += 1 - - return ret diff --git a/unitypack/engine/audio.py b/unitypack/engine/audio.py deleted file mode 100644 index f92be824..00000000 --- a/unitypack/engine/audio.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging -from enum import IntEnum - -from .component import Behaviour -from .object import Object, field - - -class AudioFormat(IntEnum): - UNKNOWN = 0 - ACC = 1 - AIFF = 2 - IT = 10 - MOD = 12 - MPEG = 13 - OGGVORBIS = 14 - S3M = 17 - WAV = 20 - XM = 21 - XMA = 22 - VAG = 23 - AUDIOQUEUE = 24 - - -class AudioRolloffMode(IntEnum): - Logarithmic = 0 - Linear = 1 - Custom = 2 - - -class AudioClip(Object): - bits_per_sample = field("m_BitsPerSample") - channels = field("m_Channels") - compression_format = field("m_CompressionFormat") - frequency = field("m_Frequency") - is_tracker_format = field("m_IsTrackerFormat") - legacy3d = field("m_Legacy3D") - length = field("m_Length") - load_in_background = field("m_LoadInBackground") - load_type = field("m_LoadType") - preload_audio_data = field("m_PreloadAudioData") - subsound_index = field("m_SubsoundIndex") - resource = field("m_Resource") - - @property - def data(self): - if not hasattr(self, "_data"): - self._data = self.resource.get_data() - return self._data - - -class AudioSource(Behaviour): - bypass_effects = field("BypassEffects", bool) - bypass_listener_effects = field("BypassListenerEffects", bool) - bypass_reverb_zones = field("BypassReverbZones", bool) - clip = field("m_audioClip") - doppler_level = field("DopplerLevel") - loop = field("Loop", bool) - max_distance = field("MaxDistance") - min_distance = field("MinDistance") - mute = field("Mute", bool) - output_audio_mixer_group = field("OutputAudioMixerGroup") - pan_stereo = field("Pan2D") - pitch = field("m_Pitch") - play_on_awake = field("m_PlayOnAwake", bool) - priority = field("Priority") - rolloff_mode = field("rolloffMode", AudioRolloffMode) - volume = field("m_Volume") - - rolloff_custom_curve = field("rolloffCustomCurve") - reverb_zone_mix_custom_curve = field("reverbZoneMixCustomCurve") - pan_level_custom_curve = field("panLevelCustomCurve") - spread_custom_curve = field("spreadCustomCurve") - - -class StreamedResource(Object): - offset = field("m_Offset") - source = field("m_Source") - size = field("m_Size") - - def get_data(self): - if not self.asset: - logging.warning("No data available for StreamedResource") - return b"" - self.asset._buf.seek(self.asset._buf_ofs + self.offset) - return self.asset._buf.read(self.size) diff --git a/unitypack/engine/component.py b/unitypack/engine/component.py deleted file mode 100644 index cd14f01f..00000000 --- a/unitypack/engine/component.py +++ /dev/null @@ -1,17 +0,0 @@ -from .object import Object, field - - -class Component(Object): - game_object = field("m_GameObject") - - -class Behaviour(Component): - enabled = field("m_Enabled", bool) - - -class Transform(Component): - position = field("m_LocalPosition") - rotation = field("m_LocalRotation") - scale = field("m_LocalScale") - parent = field("m_Father") - children = field("m_Children") diff --git a/unitypack/engine/font.py b/unitypack/engine/font.py deleted file mode 100644 index 52c5256d..00000000 --- a/unitypack/engine/font.py +++ /dev/null @@ -1,12 +0,0 @@ -from .object import Object, field - - -class Font(Object): - data = field("m_FontData") - ascent = field("m_Ascent", float) - character_padding = field("m_CharacterPadding") - character_spacing = field("m_CharacterSpacing") - font_size = field("m_FontSize", float) - kerning = field("m_Kerning", float) - line_spacing = field("m_LineSpacing", float) - pixel_scale = field("m_PixelScale", float) diff --git a/unitypack/engine/mesh.py b/unitypack/engine/mesh.py deleted file mode 100644 index 6f43aefe..00000000 --- a/unitypack/engine/mesh.py +++ /dev/null @@ -1,42 +0,0 @@ -from .component import Component -from .object import Object, field - - -class Mesh(Object): - usage_flags = field("m_MeshUsageFlags") - keep_indices = field("m_KeepIndices") - baked_convex_collision_mesh = field("m_BakedConvexCollisionMesh") - baked_triangle_collision_mesh = field("m_BakedTriangleCollisionMesh") - compressed_mesh = field("m_CompressedMesh") - is_readable = field("m_IsReadable") - localAABB = field("m_LocalAABB") - root_bone_name_hash = field("m_RootBoneNameHash") - mesh_compression = field("m_MeshCompression") - bone_name_hashes = field("m_BoneNameHashes") - bind_pose = field("m_BindPose") - shapes = field("m_Shapes") - skin = field("m_Skin") - submeshes = field("m_SubMeshes") - keep_vertices = field("m_KeepVertices") - index_buffer = field("m_IndexBuffer") - vertex_data = field("m_VertexData") - - -class SubMesh(Object): - first_byte = field("firstByte") - first_vertex = field("firstVertex") - index_count = field("indexCount") - localAABB = field("localAABB") - topology = field("topology") - vertex_count = field("vertexCount") - - -class VertexData(Object): - channels = field("m_Channels") - current_channels = field("m_CurrentChannels") - data = field("m_DataSize") - vertex_count = field("m_VertexCount") - - -class MeshFilter(Component): - pass diff --git a/unitypack/engine/movie.py b/unitypack/engine/movie.py deleted file mode 100644 index a8aec1a3..00000000 --- a/unitypack/engine/movie.py +++ /dev/null @@ -1,9 +0,0 @@ -from .object import field -from .texture import Texture - - -class MovieTexture(Texture): - audio_clip = field("m_AudioClip") - color_space = field("m_ColorSpace") - loop = field("m_Loop", bool) - movie_data = field("m_MovieData") diff --git a/unitypack/engine/object.py b/unitypack/engine/object.py deleted file mode 100644 index 45b8fc84..00000000 --- a/unitypack/engine/object.py +++ /dev/null @@ -1,32 +0,0 @@ -def field(f, cast=None, **kwargs): - def _inner(self): - if "default" in kwargs: - ret = self._obj.get(f, kwargs["default"]) - else: - ret = self._obj[f] - if cast: - ret = cast(ret) - return ret - return property(_inner) - - -class Object: - def __init__(self, data=None): - if data is None: - data = {} - self._obj = data - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.name) - - def __str__(self): - return self.name - - name = field("m_Name", default="") - - -class GameObject(Object): - active = field("m_IsActive") - component = field("m_Component") - layer = field("m_Layer") - tag = field("m_Tag") diff --git a/unitypack/engine/particle.py b/unitypack/engine/particle.py deleted file mode 100644 index 69083c7d..00000000 --- a/unitypack/engine/particle.py +++ /dev/null @@ -1,39 +0,0 @@ -from .component import Component -from .object import field - - -class ParticleEmitter(Component): - angular_velocity = field("angularVelocity") - emit = field("m_Emit", bool) - emitter_velocity_scale = field("emitterVelocityScale") - max_emission = field("maxEmission") - max_energy = field("maxEnergy") - max_size = field("maxSize") - min_emission = field("minEmission") - min_energy = field("minEnergy") - min_size = field("minSize") - rnd_angular_velocity = field("rndAngularVelocity") - rnd_rotation = field("rndRotation") - rnd_velocity = field("rndVelocity") - use_worldspace = field("Simulate in Worldspace?", bool) - world_velocity = field("worldVelocity") - - local_velocity = field("localVelocity") - one_shot = field("m_OneShot", bool) - tangent_velocity = field("tangentVelocity") - - -class EllipsoidParticleEmitter(ParticleEmitter): - min_emitter_range = field("m_MinEmitterRange") - - -class MeshParticleEmitter(ParticleEmitter): - mesh = field("m_Mesh") - interpolate_triangles = field("m_InterpolateTriangles", bool) - max_normal_velocity = field("m_MaxNormalVelocity") - min_normal_velocity = field("m_MinNormalVelocity") - systematic = field("m_Systematic", bool) - - -class ParticleSystem(Component): - pass diff --git a/unitypack/engine/physics.py b/unitypack/engine/physics.py deleted file mode 100644 index bb3511d3..00000000 --- a/unitypack/engine/physics.py +++ /dev/null @@ -1,43 +0,0 @@ -from enum import IntEnum - -from .component import Behaviour, Component -from .object import field - - -class Collider(Component): - material = field("m_Material") - is_trigger = field("m_IsTrigger", bool) - - -class BoxCollider(Collider): - center = field("m_Center") - size = field("m_Size") - - -class Collider2D(Behaviour): - is_trigger = field("m_IsTrigger") - material = field("m_Material") - offset = field("m_Offset") - used_by_effector = field("m_UsedByEffector", bool) - - -class BoxCollider2D(Collider2D): - size = field("m_Size") - - -class RigidbodySleepMode2D(IntEnum): - NeverSleep = 0 - StartAwake = 1 - StartAsleep = 2 - - -class Rigidbody2D(Component): - angular_drag = field("m_AngularDrag") - collision_detection = field("m_CollisionDetection") - constraints = field("m_Constraints") - drag = field("m_LinearDrag") - gravity_scale = field("m_GravityScale") - interpolate = field("m_Interpolate") - is_kinematic = field("m_IsKinematic", bool) - mass = field("m_Mass") - sleep_mode = field("m_SleepingMode", RigidbodySleepMode2D) diff --git a/unitypack/engine/renderer.py b/unitypack/engine/renderer.py deleted file mode 100644 index d5c836f6..00000000 --- a/unitypack/engine/renderer.py +++ /dev/null @@ -1,83 +0,0 @@ -from enum import IntEnum - -from .component import Component -from .object import field - - -class ReflectionProbeUsage(IntEnum): - Off = 0 - BlendProbes = 1 - BlendProbesAndSkybox = 2 - Simple = 3 - - -class ShadowCastingMode(IntEnum): - Off = 0 - On = 1 - TwoSided = 2 - ShadowsOnly = 3 - - -class Renderer(Component): - enabled = field("m_Enabled", bool) - lightmap_index = field("m_LightmapIndex") - materials = field("m_Materials") - probe_anchor = field("m_ProbeAnchor") - receive_shadows = field("m_ReceiveShadows", bool) - reflection_probe_usage = field("m_ReflectionProbeUsage", ReflectionProbeUsage) - shadow_casting_mode = field("m_CastShadows", ShadowCastingMode) - sorting_layer_id = field("m_SortingLayerID") - sorting_order = field("m_SortingOrder") - use_light_probes = field("m_UseLightProbes", bool) - lightmap_index_dynamic = field("m_LightmapIndexDynamic") - lightmap_tiling_offset = field("m_LightmapTilingOffset") - lightmap_tiling_offset_dynamic = field("m_LightmapTilingOffsetDynamic") - static_batch_root = field("m_StaticBatchRoot") - subset_indices = field("m_SubsetIndices") - - @property - def material(self): - return self.materials[0] - - -class ParticleSystemRenderMode(IntEnum): - Billboard = 0 - Stretch = 1 - HorizontalBillboard = 2 - VerticalBillboard = 3 - Mesh = 4 - - -class ParticleSystemSortMode(IntEnum): - None_ = 0 - Distance = 1 - OldestInFront = 2 - YoungestInFront = 3 - - -class MeshRenderer(Component): - pass - - -class ParticleRenderer(Renderer): - camera_velocity_scale = field("m_CameraVelocityScale") - length_scale = field("m_LengthScale") - max_particle_size = field("m_MaxParticleSize") - velocity_scale = field("m_VelocityScale") - stretch_particles = field("m_StretchParticles") - uv_animation = field("UV Animation") - - -class ParticleSystemRenderer(Renderer): - camera_velocity_scale = field("m_CameraVelocityScale") - length_scale = field("m_LengthScale") - max_particle_size = field("m_MaxParticleSize") - mesh = field("m_Mesh") - mesh1 = field("m_Mesh1") - mesh2 = field("m_Mesh2") - mesh3 = field("m_Mesh3") - normal_direction = field("m_NormalDirection") - render_mode = field("m_RenderMode", ParticleSystemRenderMode) - sort_mode = field("m_SortMode", ParticleSystemSortMode) - sorting_fudge = field("m_SortingFudge") - velocity_scale = field("m_VelocityScale") diff --git a/unitypack/engine/text.py b/unitypack/engine/text.py deleted file mode 100644 index af2b3691..00000000 --- a/unitypack/engine/text.py +++ /dev/null @@ -1,65 +0,0 @@ -from enum import IntEnum - -from .component import Component -from .object import Object, field - - -class FontStyle(IntEnum): - Normal = 0 - Bold = 1 - Italic = 2 - BoldAndItalic = 3 - - -class TextAlignment(IntEnum): - Left = 0 - Center = 1 - Right = 2 - - -class TextAnchor(IntEnum): - UpperLeft = 0 - UpperCenter = 1 - UpperRight = 2 - MiddleLeft = 3 - MiddleCenter = 4 - MiddleRight = 5 - LowerLeft = 6 - LowerCenter = 7 - LoweRight = 8 - - -class TextMesh(Component): - alignment = field("m_Alignment", TextAlignment) - anchor = field("m_Anchor", TextAnchor) - character_size = field("m_CharacterSize") - color = field("m_Color") - font_size = field("m_FontSize") - font = field("m_Font") - font_style = field("m_FontStyle", FontStyle) - line_spacing = field("m_LineSpacing") - offset_z = field("m_OffsetZ") - rich_text = field("m_RichText", bool) - tab_size = field("m_TabSize") - text = field("m_Text") - - def __str__(self): - return self.text - - -class TextAsset(Object): - path = field("m_PathName") - script = field("m_Script") - - @property - def bytes(self): - return self.script - - @property - def text(self): - return self.bytes.decode("utf-8") - - -class Shader(Object): - dependencies = field("m_Dependencies") - script = field("m_Script") diff --git a/unitypack/engine/texture.py b/unitypack/engine/texture.py deleted file mode 100644 index a6aad4c4..00000000 --- a/unitypack/engine/texture.py +++ /dev/null @@ -1,257 +0,0 @@ -import logging -from enum import IntEnum - -from .object import Object, field - - -class TextureFormat(IntEnum): - Alpha8 = 1 - ARGB4444 = 2 - RGB24 = 3 - RGBA32 = 4 - ARGB32 = 5 - RGB565 = 7 - - # Direct3D - DXT1 = 10 - DXT5 = 12 - - RGBA4444 = 13 - BGRA32 = 14 - - BC6H = 24 - BC7 = 25 - - DXT1Crunched = 28 - DXT5Crunched = 29 - - # PowerVR - PVRTC_RGB2 = PVRTC_2BPP_RGB = 30 - PVRTC_RGBA2 = PVRTC_2BPP_RGBA = 31 - PVRTC_RGB4 = PVRTC_4BPP_RGB = 32 - PVRTC_RGBA4 = PVRTC_4BPP_RGBA = 33 - - # Ericsson (Android) - ETC_RGB4 = 34 - ATC_RGB4 = 35 - ATC_RGBA8 = 36 - - # Adobe ATF - ATF_RGB_DXT1 = 38 - ATF_RGBA_JPG = 39 - ATF_RGB_JPG = 40 - - # Ericsson - EAC_R = 41 - EAC_R_SIGNED = 42 - EAC_RG = 43 - EAC_RG_SIGNED = 44 - ETC2_RGB = 45 - ETC2_RGBA1 = 46 - ETC2_RGBA8 = 47 - - # OpenGL / GLES - ASTC_RGB_4x4 = 48 - ASTC_RGB_5x5 = 49 - ASTC_RGB_6x6 = 50 - ASTC_RGB_8x8 = 51 - ASTC_RGB_10x10 = 52 - ASTC_RGB_12x12 = 53 - ASTC_RGBA_4x4 = 54 - ASTC_RGBA_5x5 = 55 - ASTC_RGBA_6x6 = 56 - ASTC_RGBA_8x8 = 57 - ASTC_RGBA_10x10 = 58 - ASTC_RGBA_12x12 = 59 - - @property - def pixel_format(self): - if self == TextureFormat.RGB24: - return "RGB" - elif self == TextureFormat.ARGB32: - return "ARGB" - elif self == TextureFormat.RGB565: - return "RGB;16" - elif self == TextureFormat.Alpha8: - return "A" - elif self == TextureFormat.RGBA4444: - return "RGBA;4B" - elif self == TextureFormat.ARGB4444: - return "RGBA;4B" - return "RGBA" - - -IMPLEMENTED_FORMATS = ( - TextureFormat.Alpha8, - TextureFormat.ARGB4444, - TextureFormat.RGBA4444, - TextureFormat.RGB565, - TextureFormat.RGB24, - TextureFormat.RGBA32, - TextureFormat.ARGB32, - TextureFormat.DXT1, - TextureFormat.DXT1Crunched, - TextureFormat.DXT5, - TextureFormat.DXT5Crunched, - TextureFormat.BC7, - TextureFormat.ASTC_RGB_4x4, - TextureFormat.ASTC_RGB_5x5, - TextureFormat.ASTC_RGB_5x5, - TextureFormat.ASTC_RGB_6x6, - TextureFormat.ASTC_RGB_8x8, - TextureFormat.ASTC_RGB_10x10, - TextureFormat.ASTC_RGB_12x12, - TextureFormat.ASTC_RGBA_4x4, - TextureFormat.ASTC_RGBA_5x5, - TextureFormat.ASTC_RGBA_5x5, - TextureFormat.ASTC_RGBA_6x6, - TextureFormat.ASTC_RGBA_8x8, - TextureFormat.ASTC_RGBA_10x10, - TextureFormat.ASTC_RGBA_12x12 -) - - -class Sprite(Object): - border = field("m_Border") - extrude = field("m_Extrude") - offset = field("m_Offset") - rd = field("m_RD") - rect = field("m_Rect") - pixels_per_unit = field("m_PixelsToUnits") - - -class Material(Object): - global_illumination_flags = field("m_LightmapFlags") - render_queue = field("m_CustomRenderQueue") - shader = field("m_Shader") - shader_keywords = field("m_ShaderKeywords") - - @property - def saved_properties(self): - def _unpack_prop(value): - for vk, vv in value: - if isinstance(vk, str): # Unity 5.6+ - yield vk, vv - else: # Unity <= 5.4 - yield vk["name"], vv - return {k: dict(_unpack_prop(v)) for k, v in self._obj["m_SavedProperties"].items()} - - -class Texture(Object): - height = field("m_Height") - width = field("m_Width") - - -class Texture2D(Texture): - data = field("image data") - lightmap_format = field("m_LightmapFormat") - texture_settings = field("m_TextureSettings") - color_space = field("m_ColorSpace") - is_readable = field("m_IsReadable") - read_allowed = field("m_ReadAllowed") - format = field("m_TextureFormat", TextureFormat) - texture_dimension = field("m_TextureDimension") - mipmap = field("m_MipMap") - complete_image_size = field("m_CompleteImageSize") - stream_data = field("m_StreamData", default=False) - - def __repr__(self): - return "<%s %s (%s %ix%i)>" % ( - self.__class__.__name__, self.name, self.format.name, self.width, self.height - ) - - @property - def image_data(self): - if self.stream_data and self.stream_data.asset: - if not hasattr(self, "_data"): - self._data = self.stream_data.get_data() - return self._data - return self.data - - @property - def image(self): - from PIL import Image - from decrunch import File as CrunchFile - import astc_decomp - - if self.format not in IMPLEMENTED_FORMATS: - raise NotImplementedError("Unimplemented format %r" % (self.format)) - - if self.format in (TextureFormat.DXT1, TextureFormat.DXT1Crunched): - codec = "bcn" - args = (1, ) - elif self.format in (TextureFormat.DXT5, TextureFormat.DXT5Crunched): - codec = "bcn" - args = (3, ) - elif self.format == TextureFormat.BC7: - codec = "bcn" - args = (7, ) - # 4 5 6 8 10 12 - elif self.format == TextureFormat.ASTC_RGB_4x4: - codec = "astc" - args = (4, 4, False) - elif self.format == TextureFormat.ASTC_RGB_5x5: - codec = "astc" - args = (5, 5, False) - elif self.format == TextureFormat.ASTC_RGB_6x6: - codec = "astc" - args = (6, 6, False) - elif self.format == TextureFormat.ASTC_RGB_8x8: - codec = "astc" - args = (8, 8, False) - elif self.format == TextureFormat.ASTC_RGB_10x10: - codec = "astc" - args = (10, 10, False) - elif self.format == TextureFormat.ASTC_RGB_12x12: - codec = "astc" - args = (12, 12, False) - - elif self.format == TextureFormat.ASTC_RGBA_4x4: - codec = "astc" - args = (4, 4, True) - elif self.format == TextureFormat.ASTC_RGBA_5x5: - codec = "astc" - args = (5, 5, True) - elif self.format == TextureFormat.ASTC_RGBA_6x6: - codec = "astc" - args = (6, 6, True) - elif self.format == TextureFormat.ASTC_RGBA_8x8: - codec = "astc" - args = (8, 8, True) - elif self.format == TextureFormat.ASTC_RGBA_10x10: - codec = "astc" - args = (10, 10, True) - elif self.format == TextureFormat.ASTC_RGBA_12x12: - codec = "astc" - args = (12, 12, True) - else: - codec = "raw" - args = (self.format.pixel_format, ) - - mode = "RGB" if self.format.pixel_format in ("RGB", "RGB;16") else "RGBA" - size = (self.width, self.height) - - data = self.image_data - if self.format in (TextureFormat.DXT1Crunched, TextureFormat.DXT5Crunched): - data = CrunchFile(self.image_data).decode_level(0) - - # Pillow wants bytes, not bytearrays - data = bytes(data) - - if not data and size == (0, 0): - return None - - return Image.frombytes(mode, size, data, codec, args) - - -class StreamingInfo(Object): - offset = field("offset") - size = field("size") - path = field("path") - - def get_data(self): - if not self.asset: - logging.warning("No data available for StreamingInfo") - return b"" - self.asset._buf.seek(self.asset._buf_ofs + self.offset) - return self.asset._buf.read(self.size) diff --git a/unitypack/enums.py b/unitypack/enums.py deleted file mode 100644 index 3bc0a276..00000000 --- a/unitypack/enums.py +++ /dev/null @@ -1,50 +0,0 @@ -from enum import IntEnum - - -class CompressionType(IntEnum): - NONE = 0 - LZMA = 1 - LZ4 = 2 - LZ4HC = 3 - LZHAM = 4 - - -class NodeFlags(IntEnum): - Default = 0 - Directory = 1 - Deleted = 2 - SerializedFile = 3 - - -class RuntimePlatform(IntEnum): - OSXEditor = 0 - OSXPlayer = 1 - WindowsPlayer = 2 - OSXWebPlayer = 3 - OSXDashboardPlayer = 4 - WindowsWebPlayer = 5 - WindowsEditor = 7 - IPhonePlayer = 8 - PS3 = 9 - XBOX360 = 10 - Android = 11 - NaCl = 12 - LinuxPlayer = 13 - FlashPlayer = 15 - WebGLPlayer = 17 - MetroPlayerX86 = 18 - WSAPlayerX86 = 18 - MetroPlayerX64 = 19 - WSAPlayerX64 = 19 - MetroPlayerARM = 20 - WSAPlayerARM = 20 - WP8Player = 21 - BB10Player = 22 - BlackBerryPlayer = 22 - TizenPlayer = 23 - PSP2 = 24 - PS4 = 25 - PSM = 26 - PSMPlayer = 26 - XboxOne = 27 - SamsungTVPlayer = 28 diff --git a/unitypack/environment.py b/unitypack/environment.py deleted file mode 100644 index 8971de87..00000000 --- a/unitypack/environment.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -from urllib.parse import urlparse - -from .asset import Asset -from .assetbundle import AssetBundle -from .exceptions import ArchiveNotFound - - -class UnityEnvironment: - def __init__(self, base_path=""): - self.bundles = {} - self.assets = {} - self.base_path = base_path - self.files = [] - - def __del__(self): - for f in self.files: - f.close() - - def __repr__(self): - return "%s(base_path=%r)" % (self.__class__.__name__, self.base_path) - - def load(self, file): - for bundle in self.bundles.values(): - if os.path.abspath(file.name) == os.path.abspath(bundle.path): - return bundle - ret = AssetBundle(self) - ret.load(file) - self.bundles[ret.name.lower()] = ret - for asset in ret.assets: - self.assets[asset.name.lower()] = asset - return ret - - def discover(self, name): - for bundle in list(self.bundles.values()): - dirname = os.path.dirname(os.path.abspath(bundle.path)) - for filename in os.listdir(dirname): - basename = os.path.splitext(os.path.basename(filename))[0] - if name.lower() == "cab-" + basename.lower(): - f = open(os.path.join(dirname, filename), "rb") - self.files.append(f) - self.load(f) - - def get_asset_by_filename(self, name): - if name not in self.assets: - path = os.path.join(self.base_path, name) - if os.path.exists(path): - f = open(path, "rb") - self.files.append(f) - self.assets[name] = Asset.from_file(f) - else: - self.discover(name) - self.populate_assets() - if name not in self.assets: - raise KeyError("No such asset: %r" % (name)) - return self.assets[name] - - def populate_assets(self): - for bundle in self.bundles.values(): - for asset in bundle.assets: - asset_name = asset.name.lower() - if asset_name not in self.assets: - self.assets[asset_name] = asset - - def get_asset(self, url): - if not url: - return None - - u = urlparse(url) - if u.scheme == "archive": - archive, name = os.path.split(u.path.lstrip("/").lower()) - else: - raise NotImplementedError("Unsupported scheme: %r" % (u.scheme)) - - if archive not in self.bundles: - self.discover(archive) - - # Still didn't find it? Give up... - if archive not in self.bundles: - raise ArchiveNotFound("Cannot find %r in %r" % (archive, self.bundles)) - - bundle = self.bundles[archive] - - for asset in bundle.assets: - if asset.name.lower() == name: - return asset - raise KeyError("No such asset: %r" % (name)) diff --git a/unitypack/exceptions.py b/unitypack/exceptions.py deleted file mode 100644 index 0782acbc..00000000 --- a/unitypack/exceptions.py +++ /dev/null @@ -1,5 +0,0 @@ -class UnityPackException(Exception): - pass - -class ArchiveNotFound(UnityPackException): - pass diff --git a/unitypack/export.py b/unitypack/export.py deleted file mode 100644 index 5337bbb5..00000000 --- a/unitypack/export.py +++ /dev/null @@ -1,197 +0,0 @@ -from io import BytesIO - -from .utils import BinaryReader - - -class OBJVector2: - def __init__(self, x=0, y=0): - self.x = x - self.y = y - - def read(self, buf): - self.x = buf.read_float() - self.y = buf.read_float() - return self - - def __str__(self): - return "%s %s" % (self.x, 1 - self.y) - - -class OBJVector3(OBJVector2): - def __init__(self, x=0, y=0, z=0): - super().__init__(x, y) - self.z = z - - def read(self, buf): - super().read(buf) - self.z = buf.read_float() - return self - - def __str__(self): - return "%s %s %s" % (-self.x, self.y, self.z) - - -class OBJVector4(OBJVector3): - def __init__(self, x=0, y=0, z=0, w=0): - super().__init__(x, y, z) - self.w = w - - def read(self, buf): - super().read(buf) - self.w = buf.read_float() - return self - - def read_color(self, buf): - self.x = buf.read_ubyte() - self.y = buf.read_ubyte() - self.z = buf.read_ubyte() - self.w = buf.read_ubyte() - return self - - def __str__(self): - return "%s %s %s %s" % (self.x, self.y, self.z, self.w) - - -class MeshData: - def __init__(self, mesh): - self.mesh = mesh - self.indices = [] - self.triangles = [] - self.vertices = [] - self.normals = [] - self.colors = [] - self.uv1 = [] - self.uv2 = [] - self.uv3 = [] - self.uv4 = [] - self.tangents = [] - self.extract_indices() - self.extract_vertices() - - def extract_indices(self): - for sub in self.mesh.submeshes: - sub_indices = [] - sub_triangles = [] - buf = BinaryReader(BytesIO(self.mesh.index_buffer)) - buf.seek(sub.first_byte) - for i in range(0, sub.index_count): - sub_indices.append(buf.read_uint16()) - if not sub.topology: - sub_triangles.extend(sub_indices) - else: - raise NotImplementedError("(%s) topologies are not supported" % (self.mesh.name)) - - self.indices.append(sub_indices) - self.triangles.append(sub_triangles) - - def extract_vertices(self): - # unity 5+ has 8 channels (6 otherwise) - v5_channel_count = 8 - buf = BinaryReader(BytesIO(self.mesh.vertex_data.data)) - channels = self.mesh.vertex_data.channels - # actual streams attribute 'm_Streams' may only exist in unity 4, - # use of channel data alone seems to be sufficient - stream_count = self.get_num_streams(channels) - channel_count = len(channels) - - for s in range(0, stream_count): - for i in range(0, self.mesh.vertex_data.vertex_count): - for j in range(0, channel_count): - ch = None - if channel_count > 0: - ch = channels[j] - # format == 1, use half-floats (16 bit) - if ch["format"] == 1: - raise NotImplementedError("(%r) 16 bit floats are not supported" % (self.mesh)) - # read the appropriate vertex value into the correct list - if ch and ch["dimension"] > 0 and ch["stream"] == s: - if j == 0: - self.vertices.append(OBJVector3().read(buf)) - elif j == 1: - self.normals.append(OBJVector3().read(buf)) - elif j == 2: - self.colors.append(OBJVector4().read_color(buf)) - elif j == 3: - self.uv1.append(OBJVector2().read(buf)) - elif j == 4: - self.uv2.append(OBJVector2().read(buf)) - elif j == 5: - if channel_count == v5_channel_count: - self.uv3.append(OBJVector2().read(buf)) - else: - self.tangents.append(OBJVector4().read(buf)) - elif j == 6: # for unity 5+ - self.uv4.append(OBJVector2().read(buf)) - elif j == 7: # for unity 5+ - self.tangents.append(OBJVector4().read(buf)) - # TODO investigate possible alignment here, after each stream - - def get_num_streams(self, channels): - streams = [] - # scan the channel's stream value for distinct entries - for c in channels: - if c["stream"] not in streams: - streams.append(c["stream"]) - - return len(streams) - - -class OBJMesh: - def __init__(self, mesh): - if mesh.mesh_compression: - # TODO handle compressed meshes - raise NotImplementedError("(%s) compressed meshes are not supported" % (mesh.name)) - self.mesh_data = MeshData(mesh) - self.mesh = mesh - - @staticmethod - def face_str(indices, coords, normals): - ret = ["f "] - for i in indices[::-1]: - ret.append(str(i + 1)) - if coords or normals: - ret.append("/") - if coords: - ret.append(str(i + 1)) - if normals: - ret.append("/") - ret.append(str(i + 1)) - ret.append(" ") - ret.append("\n") - return "".join(ret) - - def export(self): - ret = [] - verts_per_face = 3 - normals = self.mesh_data.normals - tex_coords = self.mesh_data.uv1 - if not tex_coords: - tex_coords = self.mesh_data.uv2 - - for v in self.mesh_data.vertices: - ret.append("v %s\n" % (v)) - for v in normals: - ret.append("vn %s\n" % (v)) - for v in tex_coords: - ret.append("vt %s\n" % (v)) - ret.append("\n") - - # write group name and set smoothing to 1 - ret.append("g %s\n" % (self.mesh.name)) - ret.append("s 1\n") - - sub_count = len(self.mesh.submeshes) - for i in range(0, sub_count): - if sub_count == 1: - ret.append("usemtl %s\n" % (self.mesh.name)) - else: - ret.append("usemtl %s_%d\n" % (self.mesh.name, i)) - face_tri = [] - for t in self.mesh_data.triangles[i]: - face_tri.append(t) - if len(face_tri) == verts_per_face: - ret.append(self.face_str(face_tri, tex_coords, normals)) - face_tri = [] - ret.append("\n") - - return "".join(ret) diff --git a/unitypack/object.py b/unitypack/object.py deleted file mode 100644 index bbbb1dad..00000000 --- a/unitypack/object.py +++ /dev/null @@ -1,232 +0,0 @@ -from collections import OrderedDict -from io import BytesIO - -from . import engine as UnityEngine -from .resources import UnityClass -from .type import TypeMetadata, TypeTree -from .utils import BinaryReader - - -def load_object(type, obj): - clsname = type.type - if hasattr(UnityEngine, clsname): - obj = getattr(UnityEngine, clsname)(obj) - - return obj - - -class ObjectInfo: - def __init__(self, asset): - self.asset = asset - - def __repr__(self): - return "<%s %i>" % (self.type, self.class_id) - - @property - def type(self): - if self.type_id > 0: - return UnityClass(self.type_id) - elif self.type_id not in self.asset.typenames: - script = self.read()["m_Script"] - if script: - try: - typename = script.resolve()["m_ClassName"] - except NotImplementedError: - typename = script.type.type[5:-1] # Capture type name in PPtr<...> - elif self.type_id in self.asset.tree.type_trees: - typename = self.asset.tree.type_trees[self.type_id].type - else: - typename = str(self.type_id) - self.asset.typenames[self.type_id] = typename - return self.asset.typenames[self.type_id] - - @property - def type_tree(self): - if self.type_id < 0: - type_trees = self.asset.tree.type_trees - if self.type_id in type_trees: - return type_trees[self.type_id] - elif self.class_id in type_trees: - return type_trees[self.class_id] - return TypeMetadata.default(self.asset).type_trees[self.class_id] - return self.asset.types[self.type_id] - - def load(self, buf): - self.path_id = self.read_id(buf) - self.data_offset = buf.read_uint() + self.asset.data_offset - self.size = buf.read_uint() - if self.asset.format < 17: - self.type_id = buf.read_int() - self.class_id = buf.read_int16() - else: - type_id = buf.read_int() - class_id = self.asset.tree.class_ids[type_id] - self.type_id = class_id - self.class_id = class_id - if self.asset.format <= 10: - self.is_destroyed = bool(buf.read_int16()) - if self.asset.format >= 11 and self.asset.format <= 16: - self.unk0 = buf.read_int16() - - if self.asset.format >= 15 and self.asset.format <= 16: - self.unk1 = buf.read_byte() - - def read_id(self, buf): - if self.asset.long_object_ids: - return buf.read_int64() - else: - return self.asset.read_id(buf) - - def read(self): - buf = self.asset._buf - buf.seek(self.asset._buf_ofs + self.data_offset) - object_buf = buf.read(self.size) - return self.read_value(self.type_tree, BinaryReader(BytesIO(object_buf))) - - def read_value(self, type, buf): - align = False - expected_size = type.size - pos_before = buf.tell() - t = type.type - first_child = type.children[0] if type.children else TypeTree(self.asset.format) - if t == "bool": - result = buf.read_boolean() - elif t == "SInt8": - result = buf.read_byte() - elif t == "UInt8": - result = buf.read_ubyte() - elif t == "SInt16": - result = buf.read_int16() - elif t == "UInt16": - result = buf.read_uint16() - elif t == "SInt64": - result = buf.read_int64() - elif t == "UInt64": - result = buf.read_int64() - elif t in ("UInt32", "unsigned int"): - result = buf.read_uint() - elif t in ("SInt32", "int"): - result = buf.read_int() - elif t == "float": - buf.align() - result = buf.read_float() - elif t == "double": - buf.align() - result = buf.read_double() - elif t == "string": - if type.size == -1: - size = buf.read_uint() - else: - size = type.size - result = buf.read_string(size) - align = type.children[0].post_align - else: - if type.is_array: - first_child = type - - if t.startswith("PPtr<"): - result = ObjectPointer(type, self.asset) - result.load(buf) - if not result: - result = None - - elif first_child and first_child.is_array: - align = first_child.post_align - size = buf.read_uint() - array_type = first_child.children[1] - if array_type.type in ("char", "UInt8"): - result = buf.read(size) - else: - result = [] - for i in range(size): - result.append(self.read_value(array_type, buf)) - elif t == "pair": - assert len(type.children) == 2 - first = self.read_value(type.children[0], buf) - second = self.read_value(type.children[1], buf) - result = (first, second) - elif t.startswith("ExposedReference"): - exposed_ref = ExposedReferenceInfo(self.asset) - result = OrderedDict() - - for child in type.children: - result[child.name] = exposed_ref.read_value(child, buf) - - result = load_object(type, result) - else: - result = OrderedDict() - - for child in type.children: - result[child.name] = self.read_value(child, buf) - - result = load_object(type, result) - if t == "StreamedResource": - result.asset = self.resolve_streaming_asset(result.source) - elif t == "StreamingInfo": - result.asset = self.resolve_streaming_asset(result.path) - - # Check to make sure we read at least as many bytes the tree says. - # We allow reading more for the case of alignment. - pos_after = buf.tell() - actual_size = pos_after - pos_before - if expected_size > 0 and actual_size < expected_size: - raise ValueError("Expected read_value(%r) to read %r bytes, but only read %r bytes" % (type, expected_size, actual_size)) - - if align or type.post_align: - buf.align() - - return result - - def resolve_streaming_asset(self, path): - if len(path) > 0: - return self.asset.get_asset(path) - - -class ExposedReferenceInfo(ObjectInfo): - - def read_value(self, type, buf): - if type.name == "exposedName": - buf.read_uint() - return "" - else: - return super().read_value(type, buf) - - -class ObjectPointer: - def __init__(self, type, asset): - self.type = type - self.source_asset = asset - - def __repr__(self): - return "%s(file_id=%r, path_id=%r)" % ( - self.__class__.__name__, self.file_id, self.path_id - ) - - def __bool__(self): - return not (self.file_id == 0 and self.path_id == 0) - - @property - def asset(self): - from .asset import AssetRef - - ret = self.source_asset.asset_refs[self.file_id] - if isinstance(ret, AssetRef): - ret = ret.resolve() - return ret - - @property - def object(self): - asset = self.asset - if asset is None: - return None - return asset.objects[self.path_id] - - def load(self, buf): - self.file_id = buf.read_int() - self.path_id = self.source_asset.read_id(buf) - - def resolve(self): - obj = self.object - if obj is None: - return None - return obj.read() diff --git a/unitypack/resources.py b/unitypack/resources.py deleted file mode 100644 index 3db38291..00000000 --- a/unitypack/resources.py +++ /dev/null @@ -1,18 +0,0 @@ -import json -import os - - -def get_resource(name): - return os.path.join(os.path.dirname(__file__), name) - - -with open(get_resource("strings.dat"), "rb") as f: - STRINGS_DAT = f.read() - - -with open(get_resource("classes.json"), "r") as f: - UNITY_CLASSES = json.load(f) - - -def UnityClass(i): - return UNITY_CLASSES.get(str(i), "" % (i)) diff --git a/unitypack/strings.dat b/unitypack/strings.dat deleted file mode 100644 index 1cfa4177977b90955748f5bba0e5ac2bf9c139f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1051 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

Iw0CfoGLXp zHuBZ625kxI8NPLygu=B9l-#uGCbE2yTRqq5!l7lJ#x@P z9zTCEhVCBf5I=u0))7)j^rbUpG-cR#r^ou<3{o}RX@yh+#L%5<8s(Ff1d zX}Y@uo~N_X-FZ5rhYjOIms&bacRz`oygW^J_dPxBbT+zscXdoMqC8D^TYT@UPU#$F z+~{t{FC^S_cgON&^}>Td%FT55la{Z{SVDJuz2toux*JZQJML4Bk{-h7ZpZz;=wi~- z=x)b9m~hkG9j@79s}z1#IMuz}wHu&wcj+J_(-p@#yYB<#PwP9eMd2sByoq`eD1T>Fy5Kcz*vz zIQM;-esN`f8{M7fx6$2>lV9@H=x&#fJ%{{*CVi*;MtA4(Lw83xhkR^7T%><8$$zcG zJJL^k#f6-(T@E>1;~{^f#y$OKlKfHd&d&7TS;x=Qe^-Hr{MRRZVS9aRl@-?doRd90 zoLisSSXvHMsxhFt8ErRs)q5vBZe-E#B`sG8U}9~l*Yhr^_A+*CT^G;T>T3hA3qW;w!HGMhrT`0*EMaxkpVmn%mT3Lf!3*eN}3_tHnQE zlkTymu5{1Z(;AD%6V|a6t=zt53>R|E>j$5F4Q-}yK%rJR^#)Oz-*6La8{5mLx@Y)o zDC@hUuK0#d>3~e7r4ywr?>g6))~;^p32;a{^c0d%vT>@EG_k2i=?mRU8=LjVPw$Sn ztyIx&{-h8?yR;~MZ%;GIaIBRM5wtUK^fs9F!yS*0-WnE$oZb4RxJvzjPH?@Iw-4)w zIVU~Z*6U3_JY;$?`dj*8*NeG6rXL<8pMIG1mhx|^@uBLi@^g^>rXSvA_dS_#`eDyk z{-WRd;STa$zlL6E`r$oUzUYTNJ-e(tqnxcDHaSDplHzbs#<%E5<(b8AjiIu7v3|J2 zJ%+UBPh|)BvVOP^p5NE{VZ{slht1!fK;`kH-d~{eN&i{-3@eCy8$$04te>|4@ddHtEl&;AI{~|4-b-0KRihO?KQ3k@$z$!{$c$ND(8LRa%K7Kceuu* zeC`16czhlR{<4B_|9}o0a>&~cEIy;qaPlWFZ$tjx&6Mv$hUsD z&$(RwLE?A7bNTecgXGf>gL^mNlkoKxkDVA%(Z@BGN>8@&PF>bJEQ`ZH<;IA-t$Ync9dLGwytb&sPCKR zdn#k=%bOdk%PCCDD;>2+tD_Vi7FSTfrWF*%(c-SPw39f19$*>F88`L`R$=HPpAo>w9Y8nI_5@PP`$*bG=DwoCmpHCN*}A($&_>htycPew2{%37!hbN~p~vgjs1texulAFsAk3d!OuoglUG278)$uIMoPq9@1s(%z g+UIZd8oLSE$$1$TOKbX=jZW2mWL@{&gHl-ie+y4p*Z=?k diff --git a/unitypack/type.py b/unitypack/type.py deleted file mode 100644 index 20b50b69..00000000 --- a/unitypack/type.py +++ /dev/null @@ -1,164 +0,0 @@ -from io import BytesIO - -from .enums import RuntimePlatform -from .resources import STRINGS_DAT, get_resource -from .utils import BinaryReader - - -class TypeTree: - NULL = "(null)" - - def __init__(self, format): - self.children = [] - self.version = 0 - self.is_array = False - self.size = 0 - self.index = 0 - self.flags = 0 - self.type = self.NULL - self.name = self.NULL - self.format = format - - def __repr__(self): - return "<%s %s (size=%r, index=%r, is_array=%r, flags=%r)>" % ( - self.type, self.name, self.size, self.index, self.is_array, self.flags - ) - - @property - def post_align(self): - return bool(self.flags & 0x4000) - - def load(self, buf): - if self.format == 10 or self.format >= 12: - self.load_blob(buf) - else: - self.load_old(buf) - - def load_old(self, buf): - self.type = buf.read_string() - self.name = buf.read_string() - self.size = buf.read_int() - self.index = buf.read_int() - self.is_array = bool(buf.read_int()) - self.version = buf.read_int() - self.flags = buf.read_int() - - num_fields = buf.read_uint() - for i in range(num_fields): - tree = TypeTree(self.format) - tree.load(buf) - self.children.append(tree) - - def load_blob(self, buf): - num_nodes = buf.read_uint() - self.buffer_bytes = buf.read_uint() - node_bytes = 32 if self.format >= 19 else 24 - node_data = BytesIO(buf.read(node_bytes * num_nodes)) - self.data = buf.read(self.buffer_bytes) - - parents = [self] - - buf = BinaryReader(node_data) - - for i in range(num_nodes): - version = buf.read_int16() - depth = buf.read_ubyte() - - if depth == 0: - curr = self - else: - while len(parents) > depth: - parents.pop() - curr = TypeTree(self.format) - parents[-1].children.append(curr) - parents.append(curr) - - curr.version = version - curr.is_array = buf.read_byte() - curr.type = self.get_string(buf.read_int()) - curr.name = self.get_string(buf.read_int()) - curr.size = buf.read_int() - curr.index = buf.read_uint() - curr.flags = buf.read_int() - - # consume unused bytes in the node (always zeros?) - buf.read(node_bytes - 24) - - def get_string(self, offset): - if offset < 0: - offset &= 0x7fffffff - data = STRINGS_DAT - elif offset < self.buffer_bytes: - data = self.data - else: - return self.NULL - return data[offset:].partition(b"\0")[0].decode("utf-8") - - -class TypeMetadata: - default_instance = None - - @classmethod - def default(cls, asset): - if not cls.default_instance: - cls.default_instance = cls(asset) - with open(get_resource("structs.dat"), "rb") as f: - cls.default_instance.load(BinaryReader(f), format=15) - return cls.default_instance - - def __init__(self, asset): - self.class_ids = [] - self.type_trees = {} - self.hashes = {} - self.asset = asset - self.generator_version = "" - self.target_platform = None - - def load(self, buf, format=None): - if format is None: - format = self.asset.format - self.generator_version = buf.read_string() - self.target_platform = RuntimePlatform(buf.read_uint()) - - if format >= 13: - has_type_trees = buf.read_boolean() - num_types = buf.read_int() - - for i in range(num_types): - class_id = buf.read_int() - if format >= 17: - unk0 = buf.read_byte() - script_id = buf.read_int16() - if class_id == 114: - if script_id >= 0: - # make up a fake negative class_id to work like the - # old system. class_id of -1 is taken to mean that - # the MonoBehaviour base class was serialized; that - # shouldn't happen, but it's easy to account for. - class_id = -2 - script_id - else: - class_id = -1 - self.class_ids.append(class_id) - if class_id < 0: - hash = buf.read(0x20) - else: - hash = buf.read(0x10) - - self.hashes[class_id] = hash - - if has_type_trees: - tree = TypeTree(format) - tree.load(buf) - self.type_trees[class_id] = tree - - # 4 unidentified bytes at the end of a type tree in 2019.4 - if format >= 21: - unk1 = buf.read(4) - - else: - num_fields = buf.read_int() - for i in range(num_fields): - class_id = buf.read_int() - tree = TypeTree(format) - tree.load(buf) - self.type_trees[class_id] = tree diff --git a/unitypack/utils.py b/unitypack/utils.py deleted file mode 100644 index 0bcca76b..00000000 --- a/unitypack/utils.py +++ /dev/null @@ -1,114 +0,0 @@ -import struct -from os import SEEK_CUR - - -def lz4_decompress(data, size): - try: - from lz4.block import decompress - except ImportError: - raise RuntimeError("python-lz4 >= 0.9 is required to read UnityFS files") - - return decompress(data, size) - - -def extract_audioclip_samples(d) -> dict: - """ - Extract all the sample data from an AudioClip and - convert it from FSB5 if needed. - """ - ret = {} - - if not d.data: - # eg. StreamedResource not available - return {} - - try: - from fsb5 import FSB5 - except ImportError as e: - raise RuntimeError("python-fsb5 is required to extract AudioClip") - - af = FSB5(d.data) - for i, sample in enumerate(af.samples): - if i > 0: - filename = "%s-%i.%s" % (d.name, i, af.get_sample_extension()) - else: - filename = "%s.%s" % (d.name, af.get_sample_extension()) - try: - sample = af.rebuild_sample(sample) - except ValueError as e: - print("WARNING: Could not extract %r (%s)" % (d, e)) - continue - ret[filename] = sample - - return ret - - -class BinaryReader: - def __init__(self, buf, endian="<"): - self.buf = buf - self.endian = endian - - def align(self): - old = self.tell() - new = (old + 3) & -4 - if new > old: - self.seek(new - old, SEEK_CUR) - - def read(self, *args): - return self.buf.read(*args) - - def seek(self, *args): - return self.buf.seek(*args) - - def tell(self): - return self.buf.tell() - - def read_string(self, size=None, encoding="utf-8"): - if size is None: - ret = self.read_cstring() - else: - ret = struct.unpack(self.endian + "%is" % (size), self.read(size))[0] - try: - return ret.decode(encoding) - except UnicodeDecodeError: - return ret - - def read_cstring(self) -> bytes: - ret = [] - c = b"" - while c != b"\0": - ret.append(c) - c = self.read(1) - if not c: - raise ValueError("Unterminated string: %r" % (ret)) - return b"".join(ret) - - def read_boolean(self) -> bool: - return bool(struct.unpack(self.endian + "b", self.read(1))[0]) - - def read_byte(self) -> int: - return struct.unpack(self.endian + "b", self.read(1))[0] - - def read_ubyte(self) -> int: - return struct.unpack(self.endian + "B", self.read(1))[0] - - def read_int16(self) -> int: - return struct.unpack(self.endian + "h", self.read(2))[0] - - def read_uint16(self) -> int: - return struct.unpack(self.endian + "H", self.read(2))[0] - - def read_int(self) -> int: - return struct.unpack(self.endian + "i", self.read(4))[0] - - def read_uint(self) -> int: - return struct.unpack(self.endian + "I", self.read(4))[0] - - def read_float(self) -> float: - return struct.unpack(self.endian + "f", self.read(4))[0] - - def read_double(self) -> float: - return struct.unpack(self.endian + "d", self.read(8))[0] - - def read_int64(self) -> int: - return struct.unpack(self.endian + "q", self.read(8))[0]