diff --git a/blender_asset_tracer/blendfile/__init__.py b/blender_asset_tracer/blendfile/__init__.py index cc50c1b..fcb7e33 100644 --- a/blender_asset_tracer/blendfile/__init__.py +++ b/blender_asset_tracer/blendfile/__init__.py @@ -326,6 +326,7 @@ class BlendFileBlock: Points to the data after the block header. """ self.endian = bfile.header.endian + self._id_name = ... # see the id_name property header_struct = bfile.block_header_struct data = bfile.fileobj.read(header_struct.size) @@ -382,6 +383,20 @@ class BlendFileBlock: def dna_type_name(self) -> str: return self.dna_type.dna_type_id.decode('ascii') + @property + def id_name(self) -> typing.Optional[bytes]: + """Same as block[b'id', b'name']; None if there is no such field. + + Evaluated only once, so safe to call multiple times without producing + excessive disk I/O. + """ + if self._id_name is ...: + try: + self._id_name = self[b'id', b'name'] + except KeyError: + self._id_name = None + return self._id_name + def refine_type_from_index(self, sdna_index: int): """Change the DNA Struct associated with this block. @@ -543,7 +558,8 @@ class BlendFileBlock: -> typing.Iterator['BlendFileBlock']: """Dereference pointers from an array-of-pointers field. - Use this function when you have a field like materials: `Mat **mat` + Use this function when you have a field like Mesh materials: + `Mat **mat` :param path: The array-of-pointers field. :param array_size: Number of items in the array. If None, the @@ -571,7 +587,8 @@ class BlendFileBlock: -> typing.Iterator['BlendFileBlock']: """Yield blocks from a fixed-size array field. - Use this function when you have a field like lamp textures: `MTex *mtex[18]` + Use this function when you have a field like lamp textures: + `MTex *mtex[18]` The size of the array is determined automatically by the size in bytes of the field divided by the pointer size of the blend file. diff --git a/blender_asset_tracer/tracer/blocks2assets.py b/blender_asset_tracer/tracer/blocks2assets.py index fc58ddf..06c2c18 100644 --- a/blender_asset_tracer/tracer/blocks2assets.py +++ b/blender_asset_tracer/tracer/blocks2assets.py @@ -5,7 +5,6 @@ From a Blend file data block, iter_assts() yields all the referred-to assets. import functools import logging -import sys import typing from blender_asset_tracer import blendfile, bpathlib @@ -117,10 +116,9 @@ def movie_clip(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockU def object_block(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]: """Object data blocks.""" # 'ob->modifiers[...].filepath' - ob_idname = block[b'id', b'name'] mods = block.get_pointer((b'modifiers', b'first')) for mod_idx, block_mod in enumerate(iterators.listbase(mods, next_path=(b'modifier', b'next'))): - block_name = b'%s.modifiers[%d]' % (ob_idname, mod_idx) + block_name = b'%s.modifiers[%d]' % (block.id_name, mod_idx) mod_type = block_mod[b'modifier', b'type'] log.debug('Tracing modifier %s, type=%d', block_name.decode(), mod_type) diff --git a/tests/test_blendfile_loading.py b/tests/test_blendfile_loading.py index 444ff6b..8ab2f4c 100644 --- a/tests/test_blendfile_loading.py +++ b/tests/test_blendfile_loading.py @@ -139,7 +139,7 @@ class BlendFileBlockTest(AbstractBlendFileTest): ob = self.bf.code_index[b'OB'][0] assert isinstance(ob, blendfile.BlendFileBlock) - self.assertEqual('OBümlaut', ob[b'id', b'name'].decode()) + self.assertEqual('OBümlaut', ob.id_name.decode()) class PointerTest(AbstractBlendFileTest): @@ -150,7 +150,7 @@ class PointerTest(AbstractBlendFileTest): scenes = self.bf.code_index[b'SC'] self.assertEqual(1, len(scenes), 'expecting 1 scene') scene = scenes[0] - self.assertEqual(b'SCScene', scene[b'id', b'name']) + self.assertEqual(b'SCScene', scene.id_name) ed_ptr = scene[b'ed'] self.assertEqual(140051431100936, ed_ptr) @@ -232,7 +232,7 @@ class ArrayTest(AbstractBlendFileTest): name = b'MAMaterial.001' else: name = b'MAMaterial.002' - self.assertEqual(name, material[b'id', b'name']) + self.assertEqual(name, material.id_name) def test_array_of_lamp_textures(self): self.bf = blendfile.BlendFile(self.blendfiles / 'lamp_textures.blend') @@ -242,7 +242,7 @@ class ArrayTest(AbstractBlendFileTest): mtex0 = lamp.get_pointer(b'mtex') tex = mtex0.get_pointer(b'tex') self.assertEqual(b'TE', tex.code) - self.assertEqual(b'TEClouds', tex[b'id', b'name']) + self.assertEqual(b'TEClouds', tex.id_name) for i, mtex in enumerate(lamp.iter_fixed_array_of_pointers(b'mtex')): if i == 0: @@ -254,7 +254,7 @@ class ArrayTest(AbstractBlendFileTest): tex = mtex.get_pointer(b'tex') self.assertEqual(b'TE', tex.code) - self.assertEqual(name, tex[b'id', b'name']) + self.assertEqual(name, tex.id_name) class LoadCompressedTest(AbstractBlendFileTest): diff --git a/tests/test_tracer.py b/tests/test_tracer.py index 746f900..421ad73 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -43,7 +43,7 @@ class AssetHoldingBlocksTest(AbstractTracerTest): # Do some arbitrary tests that convince us stuff is read well. if block.code == b'SC': seen_scene = True - self.assertEqual(b'SCScene', block[b'id', b'name']) + self.assertEqual(b'SCScene', block.id_name) continue if block.code == b'OB':