From 2a541f164a222ef7bcd036d37687738acee8d946 Mon Sep 17 00:00:00 2001 From: Bastien Montagne <bastien@blender.org> Date: Fri, 29 Jul 2022 16:02:44 +0200 Subject: [PATCH] `blendfile.py`: add basic support for non-DNA blocks (allocated strings...). Allocated strings or arrays of numbers have no DNA struct, they are 'raw' blocks of data in blendfiles. Reading those was not supported so far, this commit adds basic support for them, although user needs to know and manually call the code for the relevant properties pointers. Propper automated support could be added once T99875 is addressed. --- modules/blendfile.py | 129 ++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/modules/blendfile.py b/modules/blendfile.py index 5381980..b436cf8 100644 --- a/modules/blendfile.py +++ b/modules/blendfile.py @@ -423,6 +423,35 @@ class BlendFileBlock: use_nil=use_nil, use_str=use_str, ) + def get_raw_data(self, dna_type_id, base_index=0): + dna_types_to_size = { + b'char': 1, b'uchar': 1, + b'short': 2, b'ushort': 2, + b'int': 4, b'uint': 4, + b'int64_t': 8, b'uint64_t': 8, + b'float': 4, + b'double': 8, + } + if dna_type_id not in dna_types_to_size: + raise NotImplementedError("Cannot read raw data of type %r" % dna_type_id) + + is_pointer = False + dna_size = dna_types_to_size[dna_type_id] + array_size = self.size // dna_size + + ofs = self.file_offset + if base_index != 0: + assert(base_index < array_size) + ofs += dna_size * base_index + self.file.handle.seek(ofs, os.SEEK_SET) + + print(dna_type_id, array_size, dna_size) + return DNA_IO.read_data(self.file.handle, self.file.header, + is_pointer, + dna_type_id, + dna_size, + array_size) + def get_recursive_iter(self, path, path_root=b"", default=..., sdna_index_refine=None, @@ -777,49 +806,15 @@ class DNAStruct: dna_name = field.dna_name dna_size = field.dna_size - if dna_name.is_pointer: - return DNA_IO.read_pointer(handle, header) - elif dna_type.dna_type_id == b'int': - if dna_name.array_size > 1: - return [DNA_IO.read_int(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_int(handle, header) - elif dna_type.dna_type_id == b'short': - if dna_name.array_size > 1: - return [DNA_IO.read_short(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_short(handle, header) - elif dna_type.dna_type_id == b'ushort': - if dna_name.array_size > 1: - return [DNA_IO.read_ushort(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_ushort(handle, header) - elif dna_type.dna_type_id == b'uint64_t': - if dna_name.array_size > 1: - return [DNA_IO.read_ulong(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_ulong(handle, header) - elif dna_type.dna_type_id == b'float': - if dna_name.array_size > 1: - return [DNA_IO.read_float(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_float(handle, header) - elif dna_type.dna_type_id == b'char': - if dna_size == 1: - # Single char, assume it's bitflag or int value, and not a string/bytes data... - return DNA_IO.read_char(handle, header) - if use_str: - if use_nil: - return DNA_IO.read_string0(handle, dna_name.array_size) - else: - return DNA_IO.read_string(handle, dna_name.array_size) - else: - if use_nil: - return DNA_IO.read_bytes0(handle, dna_name.array_size) - else: - return DNA_IO.read_bytes(handle, dna_name.array_size) - elif dna_type.dna_type_id == b'uchar': - if dna_name.array_size > 1: - return [DNA_IO.read_uchar(handle, header) for i in range(dna_name.array_size)] - return DNA_IO.read_uchar(handle, header) - else: - raise NotImplementedError("%r exists but isn't pointer, can't resolve field %r" % - (path, dna_name.name_only), dna_name, dna_type) + try: + return DNA_IO.read_data(handle, header, + dna_name.is_pointer, + dna_type.dna_type_id, + dna_size, + dna_name.array_size) + except NotImplementedError as e: + raise NotImplementedError("%r exists, but can't resolve field %r" % + (path, dna_name.name_only), dna_name, dna_type) def field_set(self, header, handle, path, value): assert(type(path) == bytes) @@ -854,6 +849,54 @@ class DNA_IO: def __new__(cls, *args, **kwargs): raise RuntimeError("%s should not be instantiated" % cls) + @classmethod + def read_data(cls, + handle, header, + is_pointer, dna_type_id, dna_size, array_size, + use_str=True, use_str_nil=True): + if is_pointer: + return cls.read_pointer(handle, header) + elif dna_type_id == b'int': + if array_size > 1: + return [cls.read_int(handle, header) for i in range(array_size)] + return cls.read_int(handle, header) + elif dna_type_id == b'short': + if array_size > 1: + return [cls.read_short(handle, header) for i in range(array_size)] + return cls.read_short(handle, header) + elif dna_type_id == b'ushort': + if array_size > 1: + return [cls.read_ushort(handle, header) for i in range(array_size)] + return cls.read_ushort(handle, header) + elif dna_type_id == b'uint64_t': + if array_size > 1: + return [cls.read_ulong(handle, header) for i in range(array_size)] + return cls.read_ulong(handle, header) + elif dna_type_id == b'float': + if array_size > 1: + return [cls.read_float(handle, header) for i in range(array_size)] + return cls.read_float(handle, header) + elif dna_type_id == b'char': + if dna_size == 1 and array_size <= 1: + # Single char, assume it's bitflag or int value, and not a string/bytes data... + return cls.read_char(handle, header) + if use_str: + if use_str_nil: + return cls.read_string0(handle, array_size) + else: + return cls.read_string(handle, array_size) + else: + if use_str_nil: + return cls.read_bytes0(handle, array_size) + else: + return cls.read_bytes(handle, array_size) + elif dna_type_id == b'uchar': + if array_size > 1: + return [cls.read_uchar(handle, header) for i in range(array_size)] + return cls.read_uchar(handle, header) + else: + raise NotImplementedError("Reading %r type is not implemented" % dna_type_id) + @staticmethod def write_string(handle, astring, fieldlen): assert(isinstance(astring, str)) -- GitLab