# Copyright 2013-2014 Facundo Batista
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
#
# For further info, check http://github.com/facundobatista/yaswfp
"""Some helpers for the SWF parser."""
import itertools
import struct
def grouper(n, iterable, fillvalue=None):
"""Collect data into fixed-length chunks or blocks.
This is taken from the itertools docs.
"""
# grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return itertools.zip_longest(*args, fillvalue=fillvalue)
def unpack_si16(src):
"""Read and unpack signed integer 16b."""
return struct.unpack(" self._count:
self._count, quant, toget = 0, quant - self._count, self._count
else:
self._count, quant, toget = self._count - quant, 0, quant
read, self._bits = self._bits[:toget], self._bits[toget:]
bits.append(read)
data = int("".join(bits), 2)
return data
def s_get(self, quant):
"""Return a number using the given quantity of signed bits."""
if quant < 2:
# special case, just return that unsigned value
# quant can also be 0
return self.u_get(quant)
sign = self.u_get(1)
raw_number = self.u_get(quant - 1)
if sign == 0:
# positive, simplest case
number = raw_number
else:
# negative, complemento a 2
complement = 2 ** (quant - 1) - 1
number = -1 * ((raw_number ^ complement) + 1)
return number
def fb_get(self, quant, fb=16):
"""Return a fixed bit number
quant: number of bits to read
fb: number of bits in the integer and decimal part of the output
default is 16, resulting in a 16.16 fixed bit"""
raw_number = self.s_get(quant)
if quant == 1:
# special case, just return that unsigned value
return raw_number
return raw_number / (1 << fb)
class ReadQuantityController:
"""A context manager that will complain if bad quantity is read."""
def __init__(self, src, should):
self._src = src
self._should = should
self._started = None
def __enter__(self):
"""Enter the guarded block."""
self._started = self._src.tell()
def __exit__(self, *exc):
"""Exit the guarded block."""
cur_pos = self._src.tell()
if cur_pos != self._started + self._should:
t = "Bad reading quantity: started={} should={} ended={}".format(
self._started, self._should, cur_pos)
raise ValueError(t)