Added BlendFileBlock.id_name property
Evaluated only once, so safe to call multiple times without producing excessive disk I/O. Returns None instead of raising KeyError when there is no (b'id', b'name') field.
This commit is contained in:
parent
2bb9cffa49
commit
7167d51730
@ -326,6 +326,7 @@ class BlendFileBlock:
|
|||||||
Points to the data after the block header.
|
Points to the data after the block header.
|
||||||
"""
|
"""
|
||||||
self.endian = bfile.header.endian
|
self.endian = bfile.header.endian
|
||||||
|
self._id_name = ... # see the id_name property
|
||||||
|
|
||||||
header_struct = bfile.block_header_struct
|
header_struct = bfile.block_header_struct
|
||||||
data = bfile.fileobj.read(header_struct.size)
|
data = bfile.fileobj.read(header_struct.size)
|
||||||
@ -382,6 +383,20 @@ class BlendFileBlock:
|
|||||||
def dna_type_name(self) -> str:
|
def dna_type_name(self) -> str:
|
||||||
return self.dna_type.dna_type_id.decode('ascii')
|
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):
|
def refine_type_from_index(self, sdna_index: int):
|
||||||
"""Change the DNA Struct associated with this block.
|
"""Change the DNA Struct associated with this block.
|
||||||
|
|
||||||
@ -543,7 +558,8 @@ class BlendFileBlock:
|
|||||||
-> typing.Iterator['BlendFileBlock']:
|
-> typing.Iterator['BlendFileBlock']:
|
||||||
"""Dereference pointers from an array-of-pointers field.
|
"""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 path: The array-of-pointers field.
|
||||||
:param array_size: Number of items in the array. If None, the
|
:param array_size: Number of items in the array. If None, the
|
||||||
@ -571,7 +587,8 @@ class BlendFileBlock:
|
|||||||
-> typing.Iterator['BlendFileBlock']:
|
-> typing.Iterator['BlendFileBlock']:
|
||||||
"""Yield blocks from a fixed-size array field.
|
"""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
|
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.
|
of the field divided by the pointer size of the blend file.
|
||||||
|
|||||||
@ -5,7 +5,6 @@ From a Blend file data block, iter_assts() yields all the referred-to assets.
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from blender_asset_tracer import blendfile, bpathlib
|
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]:
|
def object_block(block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
|
||||||
"""Object data blocks."""
|
"""Object data blocks."""
|
||||||
# 'ob->modifiers[...].filepath'
|
# 'ob->modifiers[...].filepath'
|
||||||
ob_idname = block[b'id', b'name']
|
|
||||||
mods = block.get_pointer((b'modifiers', b'first'))
|
mods = block.get_pointer((b'modifiers', b'first'))
|
||||||
for mod_idx, block_mod in enumerate(iterators.listbase(mods, next_path=(b'modifier', b'next'))):
|
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']
|
mod_type = block_mod[b'modifier', b'type']
|
||||||
log.debug('Tracing modifier %s, type=%d', block_name.decode(), mod_type)
|
log.debug('Tracing modifier %s, type=%d', block_name.decode(), mod_type)
|
||||||
|
|
||||||
|
|||||||
@ -139,7 +139,7 @@ class BlendFileBlockTest(AbstractBlendFileTest):
|
|||||||
|
|
||||||
ob = self.bf.code_index[b'OB'][0]
|
ob = self.bf.code_index[b'OB'][0]
|
||||||
assert isinstance(ob, blendfile.BlendFileBlock)
|
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):
|
class PointerTest(AbstractBlendFileTest):
|
||||||
@ -150,7 +150,7 @@ class PointerTest(AbstractBlendFileTest):
|
|||||||
scenes = self.bf.code_index[b'SC']
|
scenes = self.bf.code_index[b'SC']
|
||||||
self.assertEqual(1, len(scenes), 'expecting 1 scene')
|
self.assertEqual(1, len(scenes), 'expecting 1 scene')
|
||||||
scene = scenes[0]
|
scene = scenes[0]
|
||||||
self.assertEqual(b'SCScene', scene[b'id', b'name'])
|
self.assertEqual(b'SCScene', scene.id_name)
|
||||||
|
|
||||||
ed_ptr = scene[b'ed']
|
ed_ptr = scene[b'ed']
|
||||||
self.assertEqual(140051431100936, ed_ptr)
|
self.assertEqual(140051431100936, ed_ptr)
|
||||||
@ -232,7 +232,7 @@ class ArrayTest(AbstractBlendFileTest):
|
|||||||
name = b'MAMaterial.001'
|
name = b'MAMaterial.001'
|
||||||
else:
|
else:
|
||||||
name = b'MAMaterial.002'
|
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):
|
def test_array_of_lamp_textures(self):
|
||||||
self.bf = blendfile.BlendFile(self.blendfiles / 'lamp_textures.blend')
|
self.bf = blendfile.BlendFile(self.blendfiles / 'lamp_textures.blend')
|
||||||
@ -242,7 +242,7 @@ class ArrayTest(AbstractBlendFileTest):
|
|||||||
mtex0 = lamp.get_pointer(b'mtex')
|
mtex0 = lamp.get_pointer(b'mtex')
|
||||||
tex = mtex0.get_pointer(b'tex')
|
tex = mtex0.get_pointer(b'tex')
|
||||||
self.assertEqual(b'TE', tex.code)
|
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')):
|
for i, mtex in enumerate(lamp.iter_fixed_array_of_pointers(b'mtex')):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
@ -254,7 +254,7 @@ class ArrayTest(AbstractBlendFileTest):
|
|||||||
|
|
||||||
tex = mtex.get_pointer(b'tex')
|
tex = mtex.get_pointer(b'tex')
|
||||||
self.assertEqual(b'TE', tex.code)
|
self.assertEqual(b'TE', tex.code)
|
||||||
self.assertEqual(name, tex[b'id', b'name'])
|
self.assertEqual(name, tex.id_name)
|
||||||
|
|
||||||
|
|
||||||
class LoadCompressedTest(AbstractBlendFileTest):
|
class LoadCompressedTest(AbstractBlendFileTest):
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class AssetHoldingBlocksTest(AbstractTracerTest):
|
|||||||
# Do some arbitrary tests that convince us stuff is read well.
|
# Do some arbitrary tests that convince us stuff is read well.
|
||||||
if block.code == b'SC':
|
if block.code == b'SC':
|
||||||
seen_scene = True
|
seen_scene = True
|
||||||
self.assertEqual(b'SCScene', block[b'id', b'name'])
|
self.assertEqual(b'SCScene', block.id_name)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if block.code == b'OB':
|
if block.code == b'OB':
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user