Struct.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. import struct, sys
  2. class StructType(tuple):
  3. def __getitem__(self, value):
  4. return [self] * value
  5. def __call__(self, value, endian='<'):
  6. if isinstance(value, str):
  7. return struct.unpack(endian + tuple.__getitem__(self, 0), value[:tuple.__getitem__(self, 1)])[0]
  8. else:
  9. return struct.pack(endian + tuple.__getitem__(self, 0), value)
  10. class StructException(Exception):
  11. pass
  12. class Struct(object):
  13. __slots__ = ('__attrs__', '__baked__', '__defs__', '__endian__', '__next__', '__sizes__', '__values__')
  14. int8 = StructType(('b', 1))
  15. uint8 = StructType(('B', 1))
  16. int16 = StructType(('h', 2))
  17. uint16 = StructType(('H', 2))
  18. int32 = StructType(('l', 4))
  19. uint32 = StructType(('L', 4))
  20. int64 = StructType(('q', 8))
  21. uint64 = StructType(('Q', 8))
  22. float = StructType(('f', 4))
  23. @classmethod
  24. def string(cls, len, offset=0, encoding=None, stripNulls=False, value=''):
  25. return StructType(('string', (len, offset, encoding, stripNulls, value)))
  26. LE = '<'
  27. BE = '>'
  28. __endian__ = '<'
  29. def __init__(self, func=None, unpack=None, **kwargs):
  30. self.__defs__ = []
  31. self.__sizes__ = []
  32. self.__attrs__ = []
  33. self.__values__ = {}
  34. self.__next__ = True
  35. self.__baked__ = False
  36. if func == None:
  37. self.__format__()
  38. else:
  39. sys.settrace(self.__trace__)
  40. func()
  41. for name in func.func_code.co_varnames:
  42. value = self.__frame__.f_locals[name]
  43. self.__setattr__(name, value)
  44. self.__baked__ = True
  45. if unpack != None:
  46. if isinstance(unpack, tuple):
  47. self.unpack(*unpack)
  48. else:
  49. self.unpack(unpack)
  50. if len(kwargs):
  51. for name in kwargs:
  52. self.__values__[name] = kwargs[name]
  53. def __trace__(self, frame, event, arg):
  54. self.__frame__ = frame
  55. sys.settrace(None)
  56. def __setattr__(self, name, value):
  57. if name in self.__slots__:
  58. return object.__setattr__(self, name, value)
  59. if self.__baked__ == False:
  60. if not isinstance(value, list):
  61. value = [value]
  62. attrname = name
  63. else:
  64. attrname = '*' + name
  65. self.__values__[name] = None
  66. for sub in value:
  67. if isinstance(sub, Struct):
  68. sub = sub.__class__
  69. try:
  70. if issubclass(sub, Struct):
  71. sub = ('struct', sub)
  72. except TypeError:
  73. pass
  74. type_, size = tuple(sub)
  75. if type_ == 'string':
  76. self.__defs__.append(Struct.string)
  77. self.__sizes__.append(size)
  78. self.__attrs__.append(attrname)
  79. self.__next__ = True
  80. if attrname[0] != '*':
  81. self.__values__[name] = size[3]
  82. elif self.__values__[name] == None:
  83. self.__values__[name] = [size[3] for val in value]
  84. elif type_ == 'struct':
  85. self.__defs__.append(Struct)
  86. self.__sizes__.append(size)
  87. self.__attrs__.append(attrname)
  88. self.__next__ = True
  89. if attrname[0] != '*':
  90. self.__values__[name] = size()
  91. elif self.__values__[name] == None:
  92. self.__values__[name] = [size() for val in value]
  93. else:
  94. if self.__next__:
  95. self.__defs__.append('')
  96. self.__sizes__.append(0)
  97. self.__attrs__.append([])
  98. self.__next__ = False
  99. self.__defs__[-1] += type_
  100. self.__sizes__[-1] += size
  101. self.__attrs__[-1].append(attrname)
  102. if attrname[0] != '*':
  103. self.__values__[name] = 0
  104. elif self.__values__[name] == None:
  105. self.__values__[name] = [0 for val in value]
  106. else:
  107. try:
  108. self.__values__[name] = value
  109. except KeyError:
  110. raise AttributeError(name)
  111. def __getattr__(self, name):
  112. if self.__baked__ == False:
  113. return name
  114. else:
  115. try:
  116. return self.__values__[name]
  117. except KeyError:
  118. raise AttributeError(name)
  119. def __len__(self):
  120. ret = 0
  121. arraypos, arrayname = None, None
  122. for i in range(len(self.__defs__)):
  123. sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
  124. if sdef == Struct.string:
  125. size, offset, encoding, stripNulls, value = size
  126. if isinstance(size, str):
  127. size = self.__values__[size] + offset
  128. elif sdef == Struct:
  129. if attrs[0] == '*':
  130. if arrayname != attrs:
  131. arrayname = attrs
  132. arraypos = 0
  133. size = len(self.__values__[attrs[1:]][arraypos])
  134. size = len(self.__values__[attrs])
  135. ret += size
  136. return ret
  137. def unpack(self, data, pos=0):
  138. for name in self.__values__:
  139. if not isinstance(self.__values__[name], Struct):
  140. self.__values__[name] = None
  141. elif self.__values__[name].__class__ == list and len(self.__values__[name]) != 0:
  142. if not isinstance(self.__values__[name][0], Struct):
  143. self.__values__[name] = None
  144. arraypos, arrayname = None, None
  145. for i in range(len(self.__defs__)):
  146. sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
  147. if sdef == Struct.string:
  148. size, offset, encoding, stripNulls, value = size
  149. if isinstance(size, str):
  150. size = self.__values__[size] + offset
  151. temp = data[pos:pos+size]
  152. if len(temp) != size:
  153. raise StructException('Expected %i byte string, got %i' % (size, len(temp)))
  154. if encoding != None:
  155. temp = temp.decode(encoding)
  156. if stripNulls:
  157. temp = temp.rstrip('\0')
  158. if attrs[0] == '*':
  159. name = attrs[1:]
  160. if self.__values__[name] == None:
  161. self.__values__[name] = []
  162. self.__values__[name].append(temp)
  163. else:
  164. self.__values__[attrs] = temp
  165. pos += size
  166. elif sdef == Struct:
  167. if attrs[0] == '*':
  168. if arrayname != attrs:
  169. arrayname = attrs
  170. arraypos = 0
  171. name = attrs[1:]
  172. self.__values__[attrs][arraypos].unpack(data, pos)
  173. pos += len(self.__values__[attrs][arraypos])
  174. arraypos += 1
  175. else:
  176. self.__values__[attrs].unpack(data, pos)
  177. pos += len(self.__values__[attrs])
  178. else:
  179. values = struct.unpack(self.__endian__+sdef, data[pos:pos+size])
  180. pos += size
  181. j = 0
  182. for name in attrs:
  183. if name[0] == '*':
  184. name = name[1:]
  185. if self.__values__[name] == None:
  186. self.__values__[name] = []
  187. self.__values__[name].append(values[j])
  188. else:
  189. self.__values__[name] = values[j]
  190. j += 1
  191. return self
  192. def pack(self):
  193. arraypos, arrayname = None, None
  194. ret = ''
  195. for i in range(len(self.__defs__)):
  196. sdef, size, attrs = self.__defs__[i], self.__sizes__[i], self.__attrs__[i]
  197. if sdef == Struct.string:
  198. size, offset, encoding, stripNulls, value = size
  199. if isinstance(size, str):
  200. size = self.__values__[size]+offset
  201. if attrs[0] == '*':
  202. if arrayname != attrs:
  203. arraypos = 0
  204. arrayname = attrs
  205. temp = self.__values__[attrs[1:]][arraypos]
  206. arraypos += 1
  207. else:
  208. temp = self.__values__[attrs]
  209. if encoding != None:
  210. temp = temp.encode(encoding)
  211. temp = temp[:size]
  212. ret += temp + ('\0' * (size - len(temp)))
  213. elif sdef == Struct:
  214. if attrs[0] == '*':
  215. if arrayname != attrs:
  216. arraypos = 0
  217. arrayname = attrs
  218. ret += self.__values__[attrs[1:]][arraypos].pack()
  219. arraypos += 1
  220. else:
  221. ret += self.__values__[attrs].pack()
  222. else:
  223. values = []
  224. for name in attrs:
  225. if name[0] == '*':
  226. if arrayname != name:
  227. arraypos = 0
  228. arrayname = name
  229. values.append(self.__values__[name[1:]][arraypos])
  230. arraypos += 1
  231. else:
  232. values.append(self.__values__[name])
  233. ret += struct.pack(self.__endian__+sdef, *values)
  234. return ret
  235. def __getitem__(self, value):
  236. return [('struct', self.__class__)] * value
  237. if __name__=='__main__':
  238. class TestStruct(Struct):
  239. __endian__ = Struct.LE
  240. def __format__(self):
  241. self.foo, self.bar = Struct.uint32, Struct.float
  242. self.baz = Struct.string(8)
  243. self.omg = Struct.uint32
  244. self.wtf = Struct.string(self.omg)
  245. class HaxStruct(Struct):
  246. __endian__ = Struct.LE
  247. def __format__(self):
  248. self.thing1 = Struct.uint32
  249. self.thing2 = Struct.uint32
  250. self.hax = HaxStruct
  251. test = TestStruct()
  252. test.unpack('\xEF\xBE\xAD\xDE\x00\x00\x80\x3Fdeadbeef\x04\x00\x00\x00test\xCA\xFE\xBA\xBE\xBE\xBA\xFE\xCA')
  253. assert test.foo == 0xDEADBEEF
  254. assert test.bar == 1.0
  255. assert test.baz == 'deadbeef'
  256. assert test.omg == 4
  257. assert test.wtf == 'test'
  258. assert test.hax.thing1 == 0xBEBAFECA
  259. assert test.hax.thing2 == 0xCAFEBABE
  260. print 'Tests successful'
  261. """
  262. @Struct.LE
  263. def TestStruct():
  264. foo, bar = Struct.uint32, Struct.float
  265. baz = Struct.string(8)
  266. omg = Struct.uint32
  267. wtf = Struct.string(omg)
  268. @Struct.LE
  269. def HaxStruct():
  270. thing1 = Struct.uint32
  271. thing2 = Struct.uint32
  272. hax = HaxStruct()
  273. test = TestStruct()
  274. test.foo = 0xCAFEBABE
  275. test.bar = 0.0
  276. thing = test.hax.thing1
  277. test.hax.thing1 = test.hax.thing2
  278. test.hax.thing2 = thing
  279. assert test.pack() == '\xBE\xBA\xFE\xCA\0\0\0\0deadbeef\x04\x00\x00\x00test\xBE\xBA\xFE\xCA\xCA\xFE\xBA\xBE'
  280. """