bledfile.py: Port over changes from src_utils repo.
commit 45e3ba4a4f8fb39d2a090f1f67c10d6132939d8e blendfile.py: add BlendFileBlock.get_data_hash() 'pointer-invariant' hash generator. This aims at giving some kind of uid to replace addr_old, that does not changes on every .blend saving. commit 81c92e60195f9ff3ce327c26278d07eafb4a0596 blendfile.py: fix/add some 'basic data' reading (mostly arrays of ints/floats/...), add utils to recursively get all items of a struct. New `BlendFileBlock.items_recursive_iter()` will yield all valid key/values pair of a given struct, including sub ones (e.g. 'id.next', 'id.name', etc.).
This commit is contained in:
@@ -410,6 +410,46 @@ class BlendFileBlock:
|
||||
use_nil=use_nil, use_str=use_str,
|
||||
)
|
||||
|
||||
def get_recursive_iter(self, path, path_root=b"",
|
||||
default=...,
|
||||
sdna_index_refine=None,
|
||||
use_nil=True, use_str=True,
|
||||
base_index=0,
|
||||
):
|
||||
path_full = path_root + b"." + path if path_root else 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
|
||||
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)
|
||||
|
||||
def items_recursive_iter(self):
|
||||
for k in self.keys():
|
||||
yield from self.get_recursive_iter(k, use_str=False)
|
||||
|
||||
def get_data_hash(self):
|
||||
"""
|
||||
Generates a 'hash' that can be used instead of addr_old as block id, and that should be 'stable' across .blend
|
||||
file load & save (i.e. it does not changes due to pointer addresses variations).
|
||||
"""
|
||||
# TODO This implementation is most likely far from optimal... and CRC32 is not renown as the best hashing
|
||||
# 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
|
||||
|
||||
hsh = 1
|
||||
for k, v in self.items_recursive_iter():
|
||||
if not _is_pointer(self, k):
|
||||
hsh = zlib.adler32(str(v).encode(), hsh)
|
||||
return hsh
|
||||
|
||||
def set(self, path, value,
|
||||
sdna_index_refine=None,
|
||||
):
|
||||
@@ -470,14 +510,16 @@ class BlendFileBlock:
|
||||
try:
|
||||
yield self[k]
|
||||
except NotImplementedError as err:
|
||||
yield ...
|
||||
msg, dna_name, dna_type = err.args
|
||||
yield "<%s>" % dna_type.dna_type_id.decode('ascii')
|
||||
|
||||
def items(self):
|
||||
for k in self.keys():
|
||||
try:
|
||||
yield (k, self[k])
|
||||
except NotImplementedError as err:
|
||||
yield (k, ...)
|
||||
msg, dna_name, dna_type = err.args
|
||||
yield (k, "<%s>" % dna_type.dna_type_id.decode('ascii'))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -692,10 +734,20 @@ class DNAStruct:
|
||||
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'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 use_str:
|
||||
@@ -709,7 +761,7 @@ class DNAStruct:
|
||||
else:
|
||||
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))
|
||||
raise NotImplementedError("%r exists but isn't pointer, 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)
|
||||
@@ -727,7 +779,7 @@ class DNAStruct:
|
||||
else:
|
||||
return DNA_IO.write_bytes(handle, value, dna_name.array_size)
|
||||
else:
|
||||
raise NotImplementedError("Setting %r is not yet supported" % dna_type[0])
|
||||
raise NotImplementedError("Setting %r is not yet supported" % dna_type[0], dna_name, dna_type)
|
||||
|
||||
|
||||
class DNA_IO:
|
||||
@@ -796,6 +848,13 @@ class DNA_IO:
|
||||
st = DNA_IO.USHORT[fileheader.endian_index]
|
||||
return st.unpack(handle.read(st.size))[0]
|
||||
|
||||
SSHORT = struct.Struct(b'<h'), struct.Struct(b'>h')
|
||||
|
||||
@staticmethod
|
||||
def read_short(handle, fileheader):
|
||||
st = DNA_IO.SSHORT[fileheader.endian_index]
|
||||
return st.unpack(handle.read(st.size))[0]
|
||||
|
||||
UINT = struct.Struct(b'<I'), struct.Struct(b'>I')
|
||||
|
||||
@staticmethod
|
||||
@@ -810,15 +869,11 @@ class DNA_IO:
|
||||
st = DNA_IO.SINT[fileheader.endian_index]
|
||||
return st.unpack(handle.read(st.size))[0]
|
||||
|
||||
FLOAT = struct.Struct(b'<f'), struct.Struct(b'>f')
|
||||
|
||||
@staticmethod
|
||||
def read_float(handle, fileheader):
|
||||
return struct.unpack(fileheader.endian_str + b'f', handle.read(4))[0]
|
||||
|
||||
SSHORT = struct.Struct(b'<h'), struct.Struct(b'>h')
|
||||
|
||||
@staticmethod
|
||||
def read_short(handle, fileheader):
|
||||
st = DNA_IO.SSHORT[fileheader.endian_index]
|
||||
st = DNA_IO.FLOAT[fileheader.endian_index]
|
||||
return st.unpack(handle.read(st.size))[0]
|
||||
|
||||
ULONG = struct.Struct(b'<Q'), struct.Struct(b'>Q')
|
||||
|
Reference in New Issue
Block a user