diff --git a/bam/blend/blendfile.py b/bam/blend/blendfile.py index e5fbb2c..0739a1b 100644 --- a/bam/blend/blendfile.py +++ b/bam/blend/blendfile.py @@ -416,18 +416,25 @@ class BlendFileBlock: use_nil=True, use_str=True, base_index=0, ): - path_full = path_root + b"." + path if path_root else path + if path_root: + path_full = ( + (path_root if type(path_root) is tuple else (path_root, )) + + (path if type(path) is tuple else (path, ))) + else: + path_full = path + try: yield (path_full, self.get(path_full, default, sdna_index_refine, use_nil, use_str, base_index)) - except NotImplementedError as err: - msg, dna_name, dna_type = err.args + except NotImplementedError as ex: + msg, dna_name, dna_type = ex.args struct_index = self.file.sdna_index_from_id.get(dna_type.dna_type_id, None) if struct_index is None: yield (path_full, "<%s>" % dna_type.dna_type_id.decode('ascii')) else: struct = self.file.structs[struct_index] for f in struct.fields: - yield from self.get_recursive_iter(f.dna_name.name_only, path_full, default, None, use_nil, use_str, 0) + yield from self.get_recursive_iter( + f.dna_name.name_only, path_full, default, None, use_nil, use_str, 0) def items_recursive_iter(self): for k in self.keys(): @@ -442,7 +449,8 @@ class BlendFileBlock: # algo either. But for now does the job! import zlib def _is_pointer(self, k): - return self.file.structs[self.sdna_index].field_from_path(self.file.header, self.file.handle, k).dna_name.is_pointer + return self.file.structs[self.sdna_index].field_from_path( + self.file.header, self.file.handle, k).dna_name.is_pointer hsh = 1 for k, v in self.items_recursive_iter(): @@ -485,7 +493,6 @@ class BlendFileBlock: assert(self.file.structs[sdna_index_refine].field_from_path( self.file.header, self.file.handle, path).dna_name.is_pointer) - if result != 0: # possible (but unlikely) # that this fails and returns None @@ -688,19 +695,28 @@ class DNAStruct: self.user_data = None def field_from_path(self, header, handle, path): - assert(type(path) == bytes) - # support 'id.name' - name, _, name_tail = path.partition(b'.') + """ + Support lookups as bytes or a tuple of bytes and optional index. - # support 'mtex[1].tex' - # note, multi-dimensional arrays not supported - # FIXME: 'mtex[1]' works, but not 'mtex[1].tex', why is this??? - if name.endswith(b']'): - name, _, index = name[:-1].partition(b'[') - index = int(index) + C style 'id.name' --> (b'id', b'name') + C style 'array[4]' --> ('array', 4) + """ + if type(path) is tuple: + name = path[0] + if len(path) >= 2 and type(path[1]) is not bytes: + name_tail = path[2:] + index = path[1] + assert(type(index) is int) + else: + name_tail = path[1:] + index = 0 else: + name = path + name_tail = None index = 0 + assert(type(name) is bytes) + field = self.field_from_name.get(name) if field is not None: @@ -712,7 +728,7 @@ class DNAStruct: index_offset = field.dna_type.size * index assert(index_offset < field.dna_size) handle.seek(index_offset, os.SEEK_CUR) - if name_tail == b'': + if not name_tail: # None or () return field else: return field.dna_type.field_from_path(header, handle, name_tail) @@ -721,14 +737,13 @@ class DNAStruct: default=..., use_nil=True, use_str=True, ): - assert(type(path) == bytes) - field = self.field_from_path(header, handle, path) if field is None: if default is not ...: return default else: - raise KeyError("%r not found in %r (%r)" % (path, [f.dna_name.name_only for f in self.fields], self.dna_type_id)) + raise KeyError("%r not found in %r (%r)" % + (path, [f.dna_name.name_only for f in self.fields], self.dna_type_id)) dna_type = field.dna_type dna_name = field.dna_name @@ -764,7 +779,7 @@ class DNAStruct: return DNA_IO.read_bytes(handle, dna_name.array_size) else: raise NotImplementedError("%r exists but isn't pointer, can't resolve field %r" % - (path, dna_name.name_only)) + (path, dna_name.name_only), dna_name, dna_type) def field_set(self, header, handle, path, value): assert(type(path) == bytes) diff --git a/bam/blend/blendfile_path_walker.py b/bam/blend/blendfile_path_walker.py index 7338008..8c3dac1 100644 --- a/bam/blend/blendfile_path_walker.py +++ b/bam/blend/blendfile_path_walker.py @@ -307,7 +307,7 @@ class FilePath: expand_codes_idlib.setdefault(block[b'lib'], set()).add(block[b'name']) return False else: - id_name = block[b'id.name'] + id_name = block[b'id', b'name'] # if we touched this already, don't touch again # (else we may modify the same path multiple times) @@ -374,7 +374,7 @@ class FilePath: else: def iter_blocks_id(code): for block in blend.find_blocks_from_code(code): - if block[b'id.name'] in block_codes: + if block[b'id', b'name'] in block_codes: yield from block_expand(block, code) if block_codes_idlib is not None: @@ -517,8 +517,10 @@ class FilePath: @staticmethod def _from_block_OB(block, basedir, extra_info, level): # 'ob->modifiers[...].filepath' - for block_mod in bf_utils.iter_ListBase(block.get_pointer(b'modifiers.first'), next_item=b'modifier.next'): - item_md_type = block_mod[b'modifier.type'] + for block_mod in bf_utils.iter_ListBase( + block.get_pointer((b'modifiers', b'first')), + next_item=(b'modifier', b'next')): + item_md_type = block_mod[b'modifier', b'type'] if item_md_type == C_defs.eModifierType_MeshCache: yield FPElem_block_path(basedir, level, (block_mod, b'filepath')), extra_info @@ -558,9 +560,9 @@ class FilePath: @staticmethod def _from_block_ME(block, basedir, extra_info, level): - block_external = block.get_pointer(b'ldata.external', None) + block_external = block.get_pointer((b'ldata', b'external'), None) if block_external is None: - block_external = block.get_pointer(b'fdata.external', None) + block_external = block.get_pointer((b'fdata', b'external'), None) if block_external is not None: yield FPElem_block_path(basedir, level, (block_external, b'filename')), extra_info @@ -585,7 +587,7 @@ class FilePath: pass elif item_type == C_defs.SEQ_TYPE_META: yield from seqbase(bf_utils.iter_ListBase( - item.get_pointer(b'seqbase.first', sdna_index_refine=sdna_index_Sequence))) + item.get_pointer((b'seqbase', b'first'), sdna_index_refine=sdna_index_Sequence))) else: item_strip = item.get_pointer(b'strip', sdna_index_refine=sdna_index_Sequence) if item_strip is None: # unlikely! @@ -599,7 +601,7 @@ class FilePath: yield FPElem_sequence_single( basedir, level, (item_strip, b'dir', item_stripdata, b'name')), extra_info - yield from seqbase(bf_utils.iter_ListBase(block_ed.get_pointer(b'seqbase.first'))) + yield from seqbase(bf_utils.iter_ListBase(block_ed.get_pointer((b'seqbase', b'first')))) @staticmethod def _from_block_LI(block, basedir, extra_info, level): @@ -668,8 +670,7 @@ class ExpandID: array_len = field.dna_size // block.file.header.pointer_size for i in range(array_len): - path = ('mtex[%d]' % i).encode('ascii') - item = block.get_pointer(path) + item = block.get_pointer((b'mtex', i)) if item: yield item.get_pointer(b'tex') yield item.get_pointer(b'object') @@ -679,7 +680,7 @@ class ExpandID: assert(block.dna_type.dna_type_id == b'bNodeTree') sdna_index_bNode = block.file.sdna_index_from_id[b'bNode'] - for item in bf_utils.iter_ListBase(block.get_pointer(b'nodes.first')): + for item in bf_utils.iter_ListBase(block.get_pointer((b'nodes', b'first'))): item_type = item.get(b'type', sdna_index_refine=sdna_index_bNode) if item_type != 221: # CMP_NODE_R_LAYERS @@ -725,7 +726,7 @@ class ExpandID: if block_pose is not None: assert(block_pose.dna_type.dna_type_id == b'bPose') sdna_index_bPoseChannel = block_pose.file.sdna_index_from_id[b'bPoseChannel'] - for item in bf_utils.iter_ListBase(block_pose.get_pointer(b'chanbase.first')): + for item in bf_utils.iter_ListBase(block_pose.get_pointer((b'chanbase', b'first'))): item_custom = item.get_pointer(b'custom', sdna_index_refine=sdna_index_bPoseChannel) if item_custom is not None: yield item_custom @@ -733,7 +734,8 @@ class ExpandID: # 'ob->particlesystem[...].part' sdna_index_ParticleSystem = block.file.sdna_index_from_id.get(b'ParticleSystem') if sdna_index_ParticleSystem is not None: - for item in bf_utils.iter_ListBase(block.get_pointer(b'particlesystem.first')): + for item in bf_utils.iter_ListBase( + block.get_pointer((b'particlesystem', b'first'))): item_part = item.get_pointer(b'part', sdna_index_refine=sdna_index_ParticleSystem) if item_part is not None: yield item_part @@ -820,7 +822,7 @@ class ExpandID: yield block.get_pointer(b'clip', None) sdna_index_Base = block.file.sdna_index_from_id[b'Base'] - for item in bf_utils.iter_ListBase(block.get_pointer(b'base.first')): + for item in bf_utils.iter_ListBase(block.get_pointer((b'base', b'first'))): yield item.get_pointer(b'object', sdna_index_refine=sdna_index_Base) block_ed = block.get_pointer(b'ed') @@ -834,7 +836,8 @@ class ExpandID: if item_type >= C_defs.SEQ_TYPE_EFFECT: pass elif item_type == C_defs.SEQ_TYPE_META: - yield from seqbase(bf_utils.iter_ListBase(item.get_pointer(b'seqbase.first', sdna_index_refine=sdna_index_Sequence))) + yield from seqbase(bf_utils.iter_ListBase( + item.get_pointer((b'seqbase' b'first'), sdna_index_refine=sdna_index_Sequence))) else: if item_type == C_defs.SEQ_TYPE_SCENE: yield item.get_pointer(b'scene') @@ -845,12 +848,13 @@ class ExpandID: elif item_type == C_defs.SEQ_TYPE_SOUND_RAM: yield item.get_pointer(b'sound') - yield from seqbase(bf_utils.iter_ListBase(block_ed.get_pointer(b'seqbase.first'))) + yield from seqbase(bf_utils.iter_ListBase( + block_ed.get_pointer((b'seqbase', b'first')))) @staticmethod def expand_GR(block): # 'Group' sdna_index_GroupObject = block.file.sdna_index_from_id[b'GroupObject'] - for item in bf_utils.iter_ListBase(block.get_pointer(b'gobject.first')): + for item in bf_utils.iter_ListBase(block.get_pointer((b'gobject', b'first'))): yield item.get_pointer(b'ob', sdna_index_refine=sdna_index_GroupObject) # expand_GR --> {b'GR': expand_GR, ...}