rendered paste bodyimport structclass StructType(tuple): def __getitem__(self, value): return [self] * value def __call__(self, value, endian='<'): if isinstance(value, str): return struct.unpack(endian + tuple.__getitem__(self, 0), value[:tuple.__getitem__(self, 1)])[0] else: return struct.pack(endian + tuple.__getitem__(self, 0), value)class Struct(object): __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__') int8 = StructType(('b', 1)) uint8 = StructType(('B', 1)) int16 = StructType(('h', 2)) uint16 = StructType(('H', 2)) int32 = StructType(('l', 4)) uint32 = StructType(('L', 4)) float = StructType(('f', 4)) @classmethod def string(cls, len, encoding=None, stripNulls=False): return StructType(('string', (len, encoding, stripNulls))) LE = '<' BE = '>' __endian__ = '<' def __init__(self, unpack=None, *args, **kwargs): self.__defs__ = [] self.__sizes__ = [] self.__attrs__ = [] self.__values__ = {} self.__next__ = True self.__baked__ = False self.__format__() self.__baked__ = True if unpack != None: if isinstance(unpack, tuple): self.unpack(*unpack) else: self.unpack(unpack) if len(args): i = 0 while len(args) > i and len(self.__attrs__) > i: self.__values__[self.__attrs__[i]] = args[i] if len(kwargs): for name in kwargs: self.__values__[name] = kwargs[name] def __setattr__(self, name, value): if name in self.__slots__: return object.__setattr__(self, name, value) if self.__baked__ == False: if not isinstance(value, list): value = [value] attrname = name else: attrname = '*' + name for sub in value: if isinstance(sub, Struct): sub = sub.__class__ try: if issubclass(sub, Struct): sub = ('struct', sub) except TypeError: pass type, size = sub if type == 'string': self.__defs__.append(Struct.string) self.__sizes__.append(size) self.__attrs__.append(attrname) self.__values__[name] = '' self.__next__ = True elif type == 'struct': self.__defs__.append(Struct) self.__sizes__.append(size) self.__attrs__.append(attrname) self.__values__[name] = size() self.__next__ = True else: if self.__next__: self.__defs__.append('') self.__sizes__.append(0) self.__attrs__.append([]) self.__next__ = False self.__defs__[-1] += type self.__sizes__[-1] += size self.__attrs__[-1].append(attrname) if attrname[0] != '*': self.__values__[name] = 0 else: self.__values__[name] = [0 for val in value] else: self.__values__[name] = value def __getattr__(self, name): if self.__baked__ == False: return name else: try: return self.__values__[name] except KeyError: raise AttributeError(name) def __len__(self): ret = 0 for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] if sdef == Struct.string: size, encoding, stripNulls = size if isinstance(size, str): size = self.__values__[size] elif sdef == Struct: size = len(self.__values__[size]) ret += size return ret def unpack(self, data, pos=0): for name in self.__values__: if not isinstance(self.__values__[name], Struct): self.__values__[name] = None for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] if sdef == Struct.string: size, encoding, stripNulls = size if isinstance(size, str): size = self.__values__[size] temp = data[pos:pos+size] if encoding != None: temp = temp.decode(encoding) if stripNulls: temp = temp.rstrip('\0') if attrs[0] == '*': name = attrs[1:] if self.__values__[name] == None: self.__values__[name] = [] self.__values__[name].append(temp) else: self.__values__[attrs] = temp pos += size elif sdef == Struct: self.__values__[attrs].unpack(data, pos) pos += len(self.__values__[attrs]) else: values = struct.unpack(self.__endian__+sdef, data[pos:pos+size]) pos += size j = 0 for name in attrs: if name[0] == '*': name = name[1:] if self.__values__[name] == None: self.__values__[name] = [] self.__values__[name].append(values[j]) else: self.__values__[name] = values[j] j += 1 return self def pack(self): arraypos, arrayname = None, None ret = '' for i in range(len(self.__defs__)): sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i] if sdef == Struct.string: size, encoding, stripNulls = size if isinstance(size, str): size = self.__values__[size] if attrs[0] == '*': if arrayname != attrs: arraypos = 0 arrayname = attrs temp = self.__values__[attrs[1:]][arraypos] arraypos += 1 else: temp = self.__values__[attrs] if encoding != None: temp = temp.encode(encoding) temp = temp[:size] ret += temp + ('\0' * (size - len(temp))) elif sdef == Struct: ret += self.__values__[attrs].pack() else: values = [] for name in attrs: if name[0] == '*': if arrayname != name: arraypos = 0 arrayname = name values.append(self.__values__[name[1:]][arraypos]) arraypos += 1 else: values.append(self.__values__[name]) ret += struct.pack(self.__endian__+sdef, *values) return retif __name__=='__main__': class TestStruct(Struct): __endian__ = Struct.LE def __format__(self): self.foo, self.bar = Struct.uint32, Struct.float self.baz = Struct.string(8) self.omg = Struct.uint32 self.wtf = Struct.string(self.omg) class HaxStruct(Struct): __endian__ = Struct.LE def __format__(self): self.thing1 = Struct.uint32 self.thing2 = Struct.uint32 self.hax = HaxStruct test = TestStruct() test.unpack('\xEF\xBE\xAD\xDE\x00\x00\x80\x3Fdeadbeef\x04\x00\x00\x00test\xCA\xFE\xBA\xBE\xBE\xBA\xFE\xCA') assert test.foo == 0xDEADBEEF assert test.bar == 1.0 assert test.baz == 'deadbeef' assert test.omg == 4 assert test.wtf == 'test' assert test.hax.thing1 == 0xBEBAFECA assert test.hax.thing2 == 0xCAFEBABE test.foo = 0xCAFEBABE test.bar = 0.0 thing = test.hax.thing1 test.hax.thing1 = test.hax.thing2 test.hax.thing2 = thing assert test.pack() == '\xBE\xBA\xFE\xCA\0\0\0\0deadbeef\x04\x00\x00\x00test\xBE\xBA\xFE\xCA\xCA\xFE\xBA\xBE'