Archived
1
0
Fork 0
This repository has been archived on 2024-02-06. You can view files and clone it, but cannot push or open issues or pull requests.
BlenderCOL/btypes/types.py
2018-05-10 18:43:57 +01:00

211 lines
5.3 KiB
Python

import struct as _struct
class BasicType:
def __init__(self,format_character,endianess):
self.format_character = format_character
self.endianess = endianess
self.format_string = endianess + format_character
self.size = _struct.calcsize(self.format_string)
def pack(self,stream,value):
stream.write(_struct.pack(self.format_string,value))
def unpack(self,stream):
return _struct.unpack(self.format_string,stream.read(self.size))[0]
def sizeof(self):
return self.size
class FixedPointConverter:
def __init__(self,integer_type,scale):
self.integer_type = integer_type
self.scale = scale
def pack(self,stream,value):
self.integer_type.pack(stream,int(value/self.scale))
def unpack(self,stream):
return self.integer_type.unpack(stream)*self.scale
def sizeof(self):
return self.integer_type.sizeof()
class ByteString:
def __init__(self,length):
self.length = length
def pack(self,stream,string):
if len(string) != self.length:
raise ValueError('wrong string length')
stream.write(string)
def unpack(self,stream):
return stream.read(self.length)
def sizeof(self):
return self.length
class Array:
def __init__(self,element_type,length):
self.element_type = element_type
self.length = length
def pack(self,stream,array):
if len(array) != self.length:
raise ValueError('wrong array length')
for value in array:
self.element_type.pack(stream,value)
def unpack(self,stream):
return [self.element_type.unpack(stream) for i in range(self.length)]
def sizeof(self):
return self.length*self.element_type.sizeof()
class CString:
def __init__(self,encoding):
self.encoding = encoding
def pack(self,stream,string):
stream.write((string + '\0').encode(self.encoding))
def unpack(self,stream):
#XXX: This might not work for all encodings
null = '\0'.encode(self.encoding)
string = b''
while True:
c = stream.read(len(null))
if c == null: break
string += c
return string.decode(self.encoding)
def sizeof(self):
return None
class PString:
def __init__(self,length_type,encoding):
self.length_type = length_type
self.encoding = encoding
def pack(self,stream,string):
string = string.encode(self.encoding)
self.length_type.pack(stream,len(string))
stream.write(string)
def unpack(self,stream):
length = self.length_type.unpack(stream)
return stream.read(length).decode(self.encoding)
def sizeof(self):
return None
class Field:
def __init__(self,name,field_type):
self.name = name
self.field_type = field_type
def pack(self,stream,struct):
self.field_type.pack(stream,getattr(struct,self.name))
def unpack(self,stream,struct):
setattr(struct,self.name,self.field_type.unpack(stream))
def sizeof(self):
return self.field_type.sizeof()
def equal(self,struct,other):
return getattr(struct,self.name) == getattr(other,self.name)
class Padding:
def __init__(self,length,padding=b'\xFF'):
self.length = length
self.padding = padding
def pack(self,stream,struct):
stream.write(self.padding*self.length)
def unpack(self,stream,struct):
stream.read(self.length)
def sizeof(self):
return self.length
def equal(self,struct,other):
return True
class StructClassDictionary(dict):
def __init__(self):
super().__init__()
self.struct_fields = []
def __setitem__(self,key,value):
if not key[:2] == key[-2:] == '__' and not hasattr(value,'__get__'):
self.struct_fields.append(Field(key,value))
elif key == '__padding__':
self.struct_fields.append(value)
else:
super().__setitem__(key,value)
class StructMetaClass(type):
@classmethod
def __prepare__(metacls,cls,bases):
return StructClassDictionary()
def __new__(metacls,cls,bases,classdict):
if any(field.sizeof() is None for field in classdict.struct_fields):
struct_size = None
else:
struct_size = sum(field.sizeof() for field in classdict.struct_fields)
struct_class = type.__new__(metacls,cls,bases,classdict)
struct_class.struct_fields = classdict.struct_fields
struct_class.struct_size = struct_size
return struct_class
def __init__(self,cls,bases,classdict):
super().__init__(cls,bases,classdict)
class Struct(metaclass=StructMetaClass):
__slots__ = tuple()
def __eq__(self,other):
return all(field.equal(self,other) for field in self.struct_fields)
@classmethod
def pack(cls,stream,struct):
for field in cls.struct_fields:
field.pack(stream,struct)
@classmethod
def unpack(cls,stream):
struct = cls.__new__(cls) #TODO: what if __init__ does something important?
for field in cls.struct_fields:
field.unpack(stream,struct)
return struct
@classmethod
def sizeof(cls):
return cls.struct_size