SWBF-msh-Blender-IO/addons/io_scene_swbf_msh/zaa_reader.py

146 lines
3.9 KiB
Python

"""
Basically the same as msh reader but with a couple additional
methods for making TADA easier to navigate and treats the whole
file as an initial dummy chunk to avoid the oddities of SMNA and
to handle both zaa and zaabin.
"""
import io
import struct
import os
class ZAAReader:
def __init__(self, file, parent=None, indent=0):
self.file = file
self.size: int = 0
self.size_pos = None
self.parent = parent
self.indent = " " * indent #for print debugging
def __enter__(self):
self.size_pos = self.file.tell()
if self.parent is not None:
self.header = self.read_bytes(4).decode("utf-8")
else:
self.header = "HEAD"
if self.parent is not None:
self.size = self.read_u32()
else:
self.size = os.path.getsize(self.file.name) - 8
padding_length = 4 - (self.size % 4) if self.size % 4 > 0 else 0
self.end_pos = self.size_pos + padding_length + self.size + 8
if self.parent is not None:
print(self.indent + "Begin " + self.header + ", Size: " + str(self.size) + ", Pos: " + str(self.size_pos))
else:
print(self.indent + "Begin head, Size: " + str(self.size) + ", Pos: " + str(self.size_pos))
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.size > self.MAX_SIZE:
raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}")
print(self.indent + "End " + self.header)
self.file.seek(self.end_pos)
def read_bytes(self,num_bytes):
return self.file.read(num_bytes)
def read_string(self):
last_byte = self.read_bytes(1)
result = b''
while last_byte[0] != 0x0:
result += last_byte
last_byte = self.read_bytes(1)
return result.decode("utf-8")
def read_i8(self, num=1):
buf = self.read_bytes(num)
result = struct.unpack(f"<{num}b", buf)
return result[0] if num == 1 else result
def read_u8(self, num=1):
buf = self.read_bytes(num)
result = struct.unpack(f"<{num}B", buf)
return result[0] if num == 1 else result
def read_i16(self, num=1):
buf = self.read_bytes(num * 2)
result = struct.unpack(f"<{num}h", buf)
return result[0] if num == 1 else result
def read_u16(self, num=1):
buf = self.read_bytes(num * 2)
result = struct.unpack(f"<{num}H", buf)
return result[0] if num == 1 else result
def read_i32(self, num=1):
buf = self.read_bytes(num * 4)
result = struct.unpack(f"<{num}i", buf)
return result[0] if num == 1 else result
def read_u32(self, num=1):
buf = self.read_bytes(num * 4)
result = struct.unpack(f"<{num}I", buf)
return result[0] if num == 1 else result
def read_f32(self, num=1):
buf = self.read_bytes(num * 4)
result = struct.unpack(f"<{num}f", buf)
return result[0] if num == 1 else result
def read_child(self):
child = ZAAReader(self.file, parent=self, indent=int(len(self.indent) / 2) + 1)
return child
def skip_bytes(self,num):
self.file.seek(num,1)
def peak_next_header(self):
buf = self.read_bytes(4);
self.file.seek(-4,1)
try:
result = buf.decode("utf-8")
return result
except:
return ""
def get_current_pos(self):
return self.file.tell()
def reset_pos(self):
self.file.seek(self.size_pos - self.file.tell() + 8, 1)
def how_much_left(self, pos):
return self.end_pos - pos
def skip_until(self, header):
while (self.could_have_child() and header not in self.peak_next_header()):
self.skip_bytes(1)
def could_have_child(self):
return self.end_pos - self.file.tell() >= 8
MAX_SIZE: int = 2147483647 - 8