helpers.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # Copyright 2013-2014 Facundo Batista
  2. #
  3. # This program is free software: you can redistribute it and/or modify it
  4. # under the terms of the GNU General Public License version 3, as published
  5. # by the Free Software Foundation.
  6. #
  7. # This program is distributed in the hope that it will be useful, but
  8. # WITHOUT ANY WARRANTY; without even the implied warranties of
  9. # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
  10. # PURPOSE. See the GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License along
  13. # with this program. If not, see <http://www.gnu.org/licenses/>.
  14. #
  15. # For further info, check http://github.com/facundobatista/yaswfp
  16. """Some helpers for the SWF parser."""
  17. import itertools
  18. import struct
  19. def grouper(n, iterable, fillvalue=None):
  20. """Collect data into fixed-length chunks or blocks.
  21. This is taken from the itertools docs.
  22. """
  23. # grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
  24. args = [iter(iterable)] * n
  25. return itertools.zip_longest(*args, fillvalue=fillvalue)
  26. def unpack_si16(src):
  27. """Read and unpack signed integer 16b."""
  28. return struct.unpack("<h", src.read(2))[0]
  29. def unpack_ui8(src):
  30. """Read and unpack unsigned integer 8b."""
  31. return struct.unpack("<B", src.read(1))[0]
  32. def unpack_ui16(src):
  33. """Read and unpack unsigned integer 16b."""
  34. return struct.unpack("<H", src.read(2))[0]
  35. def unpack_ui32(src):
  36. """Read and unpack unsigned integer 32b."""
  37. return struct.unpack("<I", src.read(4))[0]
  38. def unpack_fixed8(src):
  39. """Get a FIXED8 value."""
  40. dec_part = unpack_ui8(src)
  41. int_part = unpack_ui8(src)
  42. return int_part + dec_part / 256
  43. def unpack_fixed16(src):
  44. """Get a FIXED16 value (called plainly FIXED in the spec)."""
  45. dec_part = unpack_ui16(src)
  46. int_part = unpack_ui16(src)
  47. return int_part + dec_part / 65536
  48. def unpack_float16(src):
  49. """Read and unpack a 16b float.
  50. The structure is:
  51. - 1 bit for the sign
  52. . 5 bits for the exponent, with an exponent bias of 16
  53. - 10 bits for the mantissa
  54. """
  55. bc = BitConsumer(src)
  56. sign = bc.u_get(1)
  57. exponent = bc.u_get(5)
  58. mantissa = bc.u_get(10)
  59. exponent -= 16
  60. mantissa /= 2 ** 10
  61. num = (-1 ** sign) * mantissa * (10 ** exponent)
  62. return num
  63. def unpack_float(src):
  64. """Read and unpack a 32b float."""
  65. return struct.unpack("<f", src.read(4))[0]
  66. def unpack_double(src):
  67. """Read and unpack a 64b float."""
  68. return struct.unpack("<d", src.read(8))[0]
  69. class BitConsumer:
  70. """Get a byte source, yield bunch of bits."""
  71. def __init__(self, src):
  72. self.src = src
  73. self._bits = None
  74. self._count = 0
  75. def u_get(self, quant):
  76. """Return a number using the given quantity of unsigned bits."""
  77. if not quant:
  78. return 0
  79. bits = []
  80. while quant:
  81. if self._count == 0:
  82. byte = self.src.read(1)
  83. number = struct.unpack("<B", byte)[0]
  84. self._bits = bin(number)[2:].zfill(8)
  85. self._count = 8
  86. if quant > self._count:
  87. self._count, quant, toget = 0, quant - self._count, self._count
  88. else:
  89. self._count, quant, toget = self._count - quant, 0, quant
  90. read, self._bits = self._bits[:toget], self._bits[toget:]
  91. bits.append(read)
  92. data = int("".join(bits), 2)
  93. return data
  94. def s_get(self, quant):
  95. """Return a number using the given quantity of signed bits."""
  96. if quant < 2:
  97. # special case, just return that unsigned value
  98. # quant can also be 0
  99. return self.u_get(quant)
  100. sign = self.u_get(1)
  101. raw_number = self.u_get(quant - 1)
  102. if sign == 0:
  103. # positive, simplest case
  104. number = raw_number
  105. else:
  106. # negative, complemento a 2
  107. complement = 2 ** (quant - 1) - 1
  108. number = -1 * ((raw_number ^ complement) + 1)
  109. return number
  110. def fb_get(self, quant, fb=16):
  111. """Return a fixed bit number
  112. quant: number of bits to read
  113. fb: number of bits in the integer and decimal part of the output
  114. default is 16, resulting in a 16.16 fixed bit"""
  115. raw_number = self.s_get(quant)
  116. if quant == 1:
  117. # special case, just return that unsigned value
  118. return raw_number
  119. return raw_number / (1 << fb)
  120. class ReadQuantityController:
  121. """A context manager that will complain if bad quantity is read."""
  122. def __init__(self, src, should):
  123. self._src = src
  124. self._should = should
  125. self._started = None
  126. def __enter__(self):
  127. """Enter the guarded block."""
  128. self._started = self._src.tell()
  129. def __exit__(self, *exc):
  130. """Exit the guarded block."""
  131. cur_pos = self._src.tell()
  132. if cur_pos != self._started + self._should:
  133. t = "Bad reading quantity: started={} should={} ended={}".format(
  134. self._started, self._should, cur_pos)
  135. raise ValueError(t)