load SDNA structures
This commit is contained in:
parent
01c051ce14
commit
a1d8ba6b8e
111
blendparse.py
111
blendparse.py
@ -11,6 +11,20 @@ class Blendfile(io.FileIO):
|
||||
self.read_header()
|
||||
# Offsets of each file block by code.
|
||||
self._block_offsets = self._read_block_offsets()
|
||||
# SDNA structures by name.
|
||||
self._sdna = self._load_sdna()
|
||||
|
||||
def _read_c_string(self):
|
||||
"""
|
||||
Utility method to read a null-terminated C-string.
|
||||
"""
|
||||
res = bytes("", "utf-8")
|
||||
null_char = bytes("\0", "utf-8")
|
||||
while True:
|
||||
char = self.read(1)
|
||||
if char == null_char:
|
||||
return res.decode("utf-8")
|
||||
res += char
|
||||
|
||||
def _read_block_offsets(self):
|
||||
"""
|
||||
@ -37,18 +51,97 @@ class Blendfile(io.FileIO):
|
||||
|
||||
return block_offsets
|
||||
|
||||
def _read_sdna_indexes(self):
|
||||
def _load_sdna(self):
|
||||
"""
|
||||
Cache the index and offset of each SDNA structure name.
|
||||
Load each SDNA structure.
|
||||
"""
|
||||
pass
|
||||
try:
|
||||
offset = self._block_offsets["DNA1"]
|
||||
except KeyError:
|
||||
raise ValueError("Missing DNA1 file block.")
|
||||
|
||||
def _load_sdna(self, sdna_index):
|
||||
sdna = {}
|
||||
# Skip file block header.
|
||||
self.seek(offset)
|
||||
# File block code.
|
||||
self.seek(4, io.SEEK_CUR)
|
||||
# Size of file block after this header.
|
||||
block_size = int.from_bytes(self.read(4), self.endianness)
|
||||
self.seek(8 + self.pointer_size, io.SEEK_CUR)
|
||||
block_end = offset + block_size
|
||||
|
||||
# Identifier; should be "SDNA"
|
||||
self.seek(4, io.SEEK_CUR)
|
||||
# Name; should be "NAME"
|
||||
self.seek(4, io.SEEK_CUR)
|
||||
|
||||
# List of structure names.
|
||||
total_names = int.from_bytes(self.read(4), self.endianness)
|
||||
names = []
|
||||
for _ in range(total_names):
|
||||
names.append(self._read_c_string())
|
||||
sdna["names"] = names
|
||||
|
||||
# List of types
|
||||
# Align at 4 bytes.
|
||||
if self.tell() % 4 != 0:
|
||||
self.seek(4 - (self.tell() % 4), io.SEEK_CUR)
|
||||
# Type identifier; should be "TYPE"
|
||||
type_identifier = self.read(4).decode("utf-8")
|
||||
assert(type_identifier == "TYPE")
|
||||
# Number of types follows.
|
||||
total_types = int.from_bytes(self.read(4), self.endianness)
|
||||
# Avoiding collision with builtin types module.
|
||||
_types = []
|
||||
for _ in range(total_types):
|
||||
_types.append(self._read_c_string())
|
||||
sdna["types"] = _types
|
||||
|
||||
# Length of each type.
|
||||
# Align at 4 bytes.
|
||||
if self.tell() % 4 != 0:
|
||||
self.seek(4 - (self.tell() % 4), io.SEEK_CUR)
|
||||
# Type length identifier; should be "TLEN"
|
||||
len_identifier = self.read(4).decode("utf-8")
|
||||
assert(len_identifier == "TLEN")
|
||||
type_lengths = []
|
||||
for _ in range(total_types):
|
||||
type_lengths.append(int.from_bytes(self.read(2), self.endianness))
|
||||
sdna["tlen"] = type_lengths
|
||||
|
||||
# Align at 4 bytes.
|
||||
if self.tell() % 4 != 0:
|
||||
self.seek(4 - (self.tell() % 4), io.SEEK_CUR)
|
||||
# Structure identifier; should be "STRC".
|
||||
struct_identifier = self.read(4).decode("utf-8")
|
||||
assert(struct_identifier == "STRC")
|
||||
structs = {}
|
||||
# Number of structures follows.
|
||||
total_structs = int.from_bytes(self.read(4), self.endianness)
|
||||
for _ in range(total_structs):
|
||||
# Index in types containing the name of the structure.
|
||||
type_index = int.from_bytes(self.read(2), self.endianness)
|
||||
fields = {}
|
||||
# Number of fields in this structure.
|
||||
total_fields = int.from_bytes(self.read(2), self.endianness)
|
||||
for _ in range(total_fields):
|
||||
# Index in type
|
||||
field_type = int.from_bytes(self.read(2), self.endianness)
|
||||
# Index in name
|
||||
field_name = int.from_bytes(self.read(2), self.endianness)
|
||||
fields[names[field_name]] = _types[field_type]
|
||||
structs[_types[type_index]] = fields
|
||||
sdna["structs"] = structs
|
||||
|
||||
return sdna
|
||||
|
||||
def _load_struct(self, struct_name):
|
||||
"""
|
||||
Load an SDNA struct as a dict describing the structures.
|
||||
Load a struct according to the SDNA.
|
||||
"""
|
||||
# We could consider caching recently loaded SDNA structs.
|
||||
pass
|
||||
fields = self._sdna["structs"][struct_name]
|
||||
print(fields)
|
||||
return {}
|
||||
|
||||
def read_header(self):
|
||||
self.seek(0)
|
||||
@ -105,9 +198,9 @@ class Blendfile(io.FileIO):
|
||||
sdna_index = int.from_bytes(self.read(4), self.endianness)
|
||||
count = int.from_bytes(self.read(4), self.endianness)
|
||||
|
||||
sdna_struct = self._load_sdna(sdna_index)
|
||||
struct_name = list(self._sdna["structs"])[sdna_index]
|
||||
|
||||
# TODO load structs according to SDNA.
|
||||
for _ in range(count):
|
||||
translated = {}
|
||||
translated = self._load_struct(struct_name)
|
||||
yield translated
|
Loading…
x
Reference in New Issue
Block a user