move read/write into IO class
This commit is contained in:
249
blendfile.py
249
blendfile.py
@@ -61,8 +61,8 @@ def open_blend(filename, access="rb"):
|
|||||||
Known issue: does not support packaged blend files
|
Known issue: does not support packaged blend files
|
||||||
"""
|
"""
|
||||||
handle = open(filename, access)
|
handle = open(filename, access)
|
||||||
magic = read_string(handle, 7)
|
magic = handle.read(7)
|
||||||
if magic == "BLENDER":
|
if magic == b"BLENDER":
|
||||||
log.debug("normal blendfile detected")
|
log.debug("normal blendfile detected")
|
||||||
handle.seek(0, os.SEEK_SET)
|
handle.seek(0, os.SEEK_SET)
|
||||||
res = BlendFile(handle)
|
res = BlendFile(handle)
|
||||||
@@ -89,112 +89,6 @@ def open_blend(filename, access="rb"):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
|
||||||
# Write a string to the file.
|
|
||||||
######################################################
|
|
||||||
def write_string(handle, astring, fieldlen):
|
|
||||||
assert(isinstance(astring, str))
|
|
||||||
stringw = ""
|
|
||||||
if len(astring) >= fieldlen:
|
|
||||||
stringw = astring[0:fieldlen]
|
|
||||||
else:
|
|
||||||
stringw = astring + '\0'
|
|
||||||
handle.write(stringw.encode('utf-8'))
|
|
||||||
|
|
||||||
|
|
||||||
def write_bytes(handle, astring, fieldlen):
|
|
||||||
assert(isinstance(astring, (bytes, bytearray)))
|
|
||||||
stringw = b''
|
|
||||||
if len(astring) >= fieldlen:
|
|
||||||
stringw = astring[0:fieldlen]
|
|
||||||
else:
|
|
||||||
stringw = astring + b'\0'
|
|
||||||
print(stringw)
|
|
||||||
print(handle)
|
|
||||||
handle.write(stringw)
|
|
||||||
|
|
||||||
|
|
||||||
def _string_struct(length):
|
|
||||||
if length < len(_string_struct.STRING):
|
|
||||||
st = _string_struct.STRING[length]
|
|
||||||
else:
|
|
||||||
st = struct.Struct("%ds" % length)
|
|
||||||
return st
|
|
||||||
_string_struct.STRING = [struct.Struct("%ds" % i) for i in range(0, 2048)]
|
|
||||||
|
|
||||||
|
|
||||||
def read_string(handle, length):
|
|
||||||
"""
|
|
||||||
read_string reads a String of given length from a file handle
|
|
||||||
"""
|
|
||||||
st = _string_struct(length)
|
|
||||||
return st.unpack(handle.read(st.size))[0].decode('utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
def read_string0(data, offset):
|
|
||||||
"""
|
|
||||||
read_string0 reads a zero terminating String from a file handle
|
|
||||||
"""
|
|
||||||
add = 0
|
|
||||||
|
|
||||||
# TODO, faster method!
|
|
||||||
while data[offset + add] != 0:
|
|
||||||
add += 1
|
|
||||||
|
|
||||||
st = _string_struct(add)
|
|
||||||
|
|
||||||
result = st.unpack_from(data, offset)[0].decode('utf-8')
|
|
||||||
return result
|
|
||||||
|
|
||||||
######################################################
|
|
||||||
# ReadUShort reads an unsigned short from a file handle
|
|
||||||
######################################################
|
|
||||||
USHORT = [struct.Struct("<H"), struct.Struct(">H")]
|
|
||||||
def ReadUShort(handle, fileheader):
|
|
||||||
us = USHORT[fileheader.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
|
||||||
# ReadUInt reads an unsigned integer from a file handle
|
|
||||||
######################################################
|
|
||||||
UINT = [struct.Struct("<I"), struct.Struct(">I")]
|
|
||||||
def ReadUInt(handle, fileheader):
|
|
||||||
us = UINT[fileheader.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
|
|
||||||
|
|
||||||
def ReadInt(handle, fileheader):
|
|
||||||
return struct.unpack(fileheader.endian_str + "i", handle.read(4))[0]
|
|
||||||
|
|
||||||
|
|
||||||
def ReadFloat(handle, fileheader):
|
|
||||||
return struct.unpack(fileheader.endian_str + "f", handle.read(4))[0]
|
|
||||||
|
|
||||||
|
|
||||||
SSHORT = [struct.Struct("<h"), struct.Struct(">h")]
|
|
||||||
def ReadShort(handle, fileheader):
|
|
||||||
us = SSHORT[fileheader.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
|
|
||||||
|
|
||||||
ULONG = [struct.Struct("<Q"), struct.Struct(">Q")]
|
|
||||||
def ReadULong(handle, fileheader):
|
|
||||||
us = ULONG[fileheader.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
|
||||||
# ReadPointer reads an pointerfrom a file handle
|
|
||||||
# the pointersize is given by the header (BlendFileHeader)
|
|
||||||
######################################################
|
|
||||||
def ReadPointer(handle, header):
|
|
||||||
if header.pointer_size == 4:
|
|
||||||
us = UINT[header.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
if header.pointer_size == 8:
|
|
||||||
us = ULONG[header.endian_index]
|
|
||||||
return us.unpack(handle.read(us.size))[0]
|
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
@@ -433,9 +327,9 @@ class DNACatalog:
|
|||||||
|
|
||||||
def __init__(self, header, block, handle):
|
def __init__(self, header, block, handle):
|
||||||
log.debug("building DNA catalog")
|
log.debug("building DNA catalog")
|
||||||
shortstruct = USHORT[header.endian_index]
|
shortstruct = DNA_IO.USHORT[header.endian_index]
|
||||||
shortstruct2 = struct.Struct(str(USHORT[header.endian_index].format.decode() + 'H'))
|
shortstruct2 = struct.Struct(str(DNA_IO.USHORT[header.endian_index].format.decode() + 'H'))
|
||||||
intstruct = UINT[header.endian_index]
|
intstruct = DNA_IO.UINT[header.endian_index]
|
||||||
data = handle.read(block.size)
|
data = handle.read(block.size)
|
||||||
self.names = []
|
self.names = []
|
||||||
self.types = []
|
self.types = []
|
||||||
@@ -447,7 +341,7 @@ class DNACatalog:
|
|||||||
|
|
||||||
log.debug("building #%d names" % names_len)
|
log.debug("building #%d names" % names_len)
|
||||||
for i in range(names_len):
|
for i in range(names_len):
|
||||||
tName = read_string0(data, offset)
|
tName = DNA_IO.read_string0(data, offset)
|
||||||
offset = offset + len(tName) + 1
|
offset = offset + len(tName) + 1
|
||||||
self.names.append(DNAName(tName))
|
self.names.append(DNAName(tName))
|
||||||
del names_len
|
del names_len
|
||||||
@@ -458,7 +352,7 @@ class DNACatalog:
|
|||||||
offset += 4
|
offset += 4
|
||||||
log.debug("building #"+str(types_len)+" types")
|
log.debug("building #"+str(types_len)+" types")
|
||||||
for i in range(types_len):
|
for i in range(types_len):
|
||||||
tType = read_string0(data, offset)
|
tType = DNA_IO.read_string0(data, offset)
|
||||||
self.types.append([tType, 0, None])
|
self.types.append([tType, 0, None])
|
||||||
offset += len(tType) + 1
|
offset += len(tType) + 1
|
||||||
|
|
||||||
@@ -588,15 +482,15 @@ class DNAStructure:
|
|||||||
if len(rest) == 0:
|
if len(rest) == 0:
|
||||||
|
|
||||||
if fname.is_pointer:
|
if fname.is_pointer:
|
||||||
return ReadPointer(handle, header)
|
return DNA_IO.read_pointer(handle, header)
|
||||||
elif ftype[0] == "int":
|
elif ftype[0] == "int":
|
||||||
return ReadInt(handle, header)
|
return DNA_IO.read_int(handle, header)
|
||||||
elif ftype[0] == "short":
|
elif ftype[0] == "short":
|
||||||
return ReadShort(handle, header)
|
return DNA_IO.read_short(handle, header)
|
||||||
elif ftype[0] == "float":
|
elif ftype[0] == "float":
|
||||||
return ReadFloat(handle, header)
|
return DNA_IO.read_float(handle, header)
|
||||||
elif ftype[0] == "char":
|
elif ftype[0] == "char":
|
||||||
return read_string(handle, fname.array_size)
|
return DNA_IO.read_string(handle, fname.array_size)
|
||||||
else:
|
else:
|
||||||
return ftype[2].field_get(header, handle, rest)
|
return ftype[2].field_get(header, handle, rest)
|
||||||
|
|
||||||
@@ -618,9 +512,9 @@ class DNAStructure:
|
|||||||
if len(rest) == 0:
|
if len(rest) == 0:
|
||||||
if ftype[0] == "char":
|
if ftype[0] == "char":
|
||||||
if type(value) is str:
|
if type(value) is str:
|
||||||
return write_string(handle, value, fname.array_size)
|
return DNA_IO.write_string(handle, value, fname.array_size)
|
||||||
else:
|
else:
|
||||||
return write_bytes(handle, value, fname.array_size)
|
return DNA_IO.write_bytes(handle, value, fname.array_size)
|
||||||
else:
|
else:
|
||||||
return ftype[2].field_set(header, handle, rest, value)
|
return ftype[2].field_set(header, handle, rest, value)
|
||||||
else:
|
else:
|
||||||
@@ -628,6 +522,121 @@ class DNAStructure:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
class DNA_IO:
|
||||||
|
"""
|
||||||
|
Module like class, for read-write utility functions.
|
||||||
|
|
||||||
|
Only stores static methods & constants.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
# ----
|
||||||
|
# Methods for read/write,
|
||||||
|
# these are only here to avoid clogging global-namespace
|
||||||
|
@staticmethod
|
||||||
|
def write_string(handle, astring, fieldlen):
|
||||||
|
assert(isinstance(astring, str))
|
||||||
|
stringw = ""
|
||||||
|
if len(astring) >= fieldlen:
|
||||||
|
stringw = astring[0:fieldlen]
|
||||||
|
else:
|
||||||
|
stringw = astring + '\0'
|
||||||
|
handle.write(stringw.encode('utf-8'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def write_bytes(handle, astring, fieldlen):
|
||||||
|
assert(isinstance(astring, (bytes, bytearray)))
|
||||||
|
stringw = b''
|
||||||
|
if len(astring) >= fieldlen:
|
||||||
|
stringw = astring[0:fieldlen]
|
||||||
|
else:
|
||||||
|
stringw = astring + b'\0'
|
||||||
|
print(stringw)
|
||||||
|
print(handle)
|
||||||
|
handle.write(stringw)
|
||||||
|
|
||||||
|
_STRING = [struct.Struct("%ds" % i) for i in range(0, 2048)]
|
||||||
|
@staticmethod
|
||||||
|
def _string_struct(length):
|
||||||
|
if length < len(DNA_IO._STRING):
|
||||||
|
st = DNA_IO._STRING[length]
|
||||||
|
else:
|
||||||
|
st = struct.Struct("%ds" % length)
|
||||||
|
return st
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_string(handle, length):
|
||||||
|
"""
|
||||||
|
read_string reads a String of given length from a file handle
|
||||||
|
"""
|
||||||
|
st = DNA_IO._string_struct(length)
|
||||||
|
return st.unpack(handle.read(st.size))[0].decode('utf-8')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_string0(data, offset):
|
||||||
|
"""
|
||||||
|
read_string0 reads a zero terminating String from a file handle
|
||||||
|
"""
|
||||||
|
add = 0
|
||||||
|
|
||||||
|
# TODO, faster method!
|
||||||
|
while data[offset + add] != 0:
|
||||||
|
add += 1
|
||||||
|
|
||||||
|
st = DNA_IO._string_struct(add)
|
||||||
|
|
||||||
|
result = st.unpack_from(data, offset)[0].decode('utf-8')
|
||||||
|
return result
|
||||||
|
|
||||||
|
USHORT = struct.Struct("<H"), struct.Struct(">H")
|
||||||
|
@staticmethod
|
||||||
|
def read_ushort(handle, fileheader):
|
||||||
|
st = USHORT[fileheader.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
UINT = struct.Struct("<I"), struct.Struct(">I")
|
||||||
|
@staticmethod
|
||||||
|
def read_uint(handle, fileheader):
|
||||||
|
st = UINT[fileheader.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
SINT = struct.Struct("<i"), struct.Struct(">i")
|
||||||
|
@staticmethod
|
||||||
|
def read_int(handle, fileheader):
|
||||||
|
st = SINT[fileheader.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_float(handle, fileheader):
|
||||||
|
return struct.unpack(fileheader.endian_str + "f", handle.read(4))[0]
|
||||||
|
|
||||||
|
|
||||||
|
SSHORT = struct.Struct("<h"), struct.Struct(">h")
|
||||||
|
@staticmethod
|
||||||
|
def read_short(handle, fileheader):
|
||||||
|
st = SSHORT[fileheader.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
|
||||||
|
ULONG = struct.Struct("<Q"), struct.Struct(">Q")
|
||||||
|
@staticmethod
|
||||||
|
def read_ulong(handle, fileheader):
|
||||||
|
st = ULONG[fileheader.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_pointer(handle, header):
|
||||||
|
"""
|
||||||
|
ReadPointer reads an pointerfrom a file handle
|
||||||
|
the pointersize is given by the header (BlendFileHeader)
|
||||||
|
"""
|
||||||
|
if header.pointer_size == 4:
|
||||||
|
st = UINT[header.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
if header.pointer_size == 8:
|
||||||
|
st = ULONG[header.endian_index]
|
||||||
|
return st.unpack(handle.read(st.size))[0]
|
||||||
|
|
||||||
|
|
||||||
class DNAField:
|
class DNAField:
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user