From c69612b26484ef8d00336263fda39d3bfec3b658 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 24 Nov 2025 14:53:15 +0100 Subject: [PATCH] BlendFileBlock.get to support accessing different items in the file-block (#92898) Add an `array_index` parameter to `block.get(property_name)` to get a specific item from an array. Example: ```python verts = self.bf.block_from_addr[verts_ptr] assert verts.get(b"co") == [-1.0, -1.0, -1.0] # index 0 assert verts.get(b"co", array_index=1) == [-1.0, -1.0, 1.0] ``` Reviewed-on: https://projects.blender.org/blender/blender-asset-tracer/pulls/92898 --- blender_asset_tracer/blendfile/__init__.py | 15 ++++++++++++++- blender_asset_tracer/blendfile/iterators.py | 3 +++ tests/test_blendfile_loading.py | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/blender_asset_tracer/blendfile/__init__.py b/blender_asset_tracer/blendfile/__init__.py index fe4c952..8a2e1b1 100644 --- a/blender_asset_tracer/blendfile/__init__.py +++ b/blender_asset_tracer/blendfile/__init__.py @@ -596,6 +596,7 @@ class BlendFileBlock: null_terminated=True, as_str=False, return_field=False, + array_index=0, ) -> typing.Any: """Read a property and return the value. @@ -612,8 +613,20 @@ class BlendFileBlock: (assumes UTF-8 encoding). :param return_field: When True, returns tuple (dna.Field, value). Otherwise just returns the value. + :param array_index: If the property is an array, this determines the + index of the returned item from that array. Also see + `blendfile.iterators.dynamic_array()` for iterating such arrays. """ - self.bfile.fileobj.seek(self.file_offset, os.SEEK_SET) + file_offset = self.file_offset + if array_index: + if not (0 <= array_index < self.count): + raise IndexError( + "Invalid 'array_index' for file-block. " + f"Expected int value in range 0-{self.count - 1}, got {array_index}." + ) + file_offset += array_index * self.dna_type.size + + self.bfile.fileobj.seek(file_offset, os.SEEK_SET) dna_struct = self.bfile.structs[self.sdna_index] field, value = dna_struct.field_get( diff --git a/blender_asset_tracer/blendfile/iterators.py b/blender_asset_tracer/blendfile/iterators.py index 6fcdfc7..5268310 100644 --- a/blender_asset_tracer/blendfile/iterators.py +++ b/blender_asset_tracer/blendfile/iterators.py @@ -81,6 +81,9 @@ def dynamic_array(block: BlendFileBlock) -> typing.Iterator[BlendFileBlock]: pointer. BAT interprets these as a single data block, making it hard to access individual elements. This function divides the array into individual blocks by creating modified copies of the original block. + + See `some_block.get(b'name', array_index)` if you want to access elements by + index (instead of iterating). """ element_size = block.dna_type.size diff --git a/tests/test_blendfile_loading.py b/tests/test_blendfile_loading.py index 355d1a1..c28b706 100644 --- a/tests/test_blendfile_loading.py +++ b/tests/test_blendfile_loading.py @@ -46,6 +46,12 @@ class BlendFileBlockTest(AbstractBlendFileTest): mname = mesh.get((b"id", b"name"), as_str=True) self.assertEqual("MECube³", mname) + # Try to access different file-block items. + verts_ptr = mesh.get(b"mvert") + verts = self.bf.block_from_addr[verts_ptr] + assert verts.get(b"co") == [-1.0, -1.0, -1.0] + assert verts.get(b"co", array_index=1) == [-1.0, -1.0, 1.0] + def test_get_recursive_iter(self): ob = self.bf.code_index[b"OB"][0] assert isinstance(ob, blendfile.BlendFileBlock)