import struct, math from .data import * from .actions import * from .filters import SWFFilterFactory from functools import reduce class SWFStream(object): """ SWF File stream """ FLOAT16_EXPONENT_BASE = 15 def __init__(self, file): """ Initialize with a file object """ self.f = file self._bits_pending = 0 self._partial_byte = None self._make_masks() def bin(self, s): """ Return a value as a binary string """ return str(s) if s<=1 else bin(s>>1) + str(s&1) def calc_max_bits(self, signed, values): """ Calculates the maximim needed bits to represent a value """ b = 0 vmax = -10000000 for val in values: if signed: b = b | val if val >= 0 else b | ~val << 1 vmax = val if vmax < val else vmax else: b |= val; bits = 0 if b > 0: bits = len(self.bin(b)) - 2 if signed and vmax > 0 and len(self.bin(vmax)) - 2 >= bits: bits += 1 return bits def close(self): """ Closes the stream """ if self.f: self.f.close() def _make_masks(self): self._masks = [(1 << x) - 1 for x in range(9)] def _read_bytes_aligned(self, bytes): buf = self.f.read(bytes) return reduce(lambda x, y: x << 8 | ord(chr(y)), buf, 0) def readbits(self, bits): """ Read the specified number of bits from the stream. Returns 0 for bits == 0. """ if bits == 0: return 0 # fast byte-aligned path if bits % 8 == 0 and self._bits_pending == 0: return self._read_bytes_aligned(bits // 8) out = 0 masks = self._masks def transfer_bits(x, y, n, t): """ transfers t bits from the top of y_n to the bottom of x. then returns x and the remaining bits in y """ if n == t: # taking all return (x << t) | y, 0 mask = masks[t] # (1 << t) - 1 remainmask = masks[n - t] # (1 << n - t) - 1 taken = ((y >> n - t) & mask) return (x << t) | taken, y & remainmask while bits > 0: if self._bits_pending > 0: assert self._partial_byte is not None take = min(self._bits_pending, bits) out, self._partial_byte = transfer_bits(out, self._partial_byte, self._bits_pending, take) if take == self._bits_pending: # we took them all self._partial_byte = None self._bits_pending -= take bits -= take continue r = self.f.read(1) if r == '': raise EOFError self._partial_byte = ord(r) self._bits_pending = 8 return out def readFB(self, bits): """ Read a float using the specified number of bits """ return float(self.readSB(bits)) / 65536.0 def readSB(self, bits): """ Read a signed int using the specified number of bits """ shift = 32 - bits return int32(self.readbits(bits) << shift) >> shift def readUB(self, bits): """ Read a unsigned int using the specified number of bits """ return self.readbits(bits) def readSI8(self): """ Read a signed byte """ self.reset_bits_pending(); return struct.unpack('b', self.f.read(1))[0] def readUI8(self): """ Read a unsigned byte """ self.reset_bits_pending(); return struct.unpack('B', self.f.read(1))[0] def readSI16(self): """ Read a signed short """ self.reset_bits_pending(); return struct.unpack('h', self.f.read(2))[0] def readUI16(self): """ Read a unsigned short """ self.reset_bits_pending(); return struct.unpack('H', self.f.read(2))[0] def readSI32(self): """ Read a signed int """ self.reset_bits_pending(); return struct.unpack('> 10) & 0x1f significand = word & 0x3ff if exponent == 0: if significand == 0: return 0.0 else: return sign * math.pow(2, 1 - SWFStream.FLOAT16_EXPONENT_BASE) * (significand / 1024.0) if exponent == 31: if significand == 0: return float('-inf') if sign < 0 else float('inf') else: return float('nan') # normal number return sign * math.pow(2, exponent - SWFStream.FLOAT16_EXPONENT_BASE) * (1 + significand / 1024.0) def readFIXED(self): """ Read a 16.16 fixed value """ self.reset_bits_pending() return self.readSI32() / 65536.0 def readFIXED8(self): """ Read a 8.8 fixed value """ self.reset_bits_pending() return self.readSI16() / 256.0 def readCXFORM(self): """ Read a SWFColorTransform """ return SWFColorTransform(self) def readCXFORMWITHALPHA(self): """ Read a SWFColorTransformWithAlpha """ return SWFColorTransformWithAlpha(self) def readGLYPHENTRY(self, glyphBits, advanceBits): """ Read a SWFGlyphEntry """ return SWFGlyphEntry(self, glyphBits, advanceBits) def readGRADIENT(self, level=1): """ Read a SWFGradient """ return SWFGradient(self, level) def readFOCALGRADIENT(self, level=1): """ Read a SWFFocalGradient """ return SWFFocalGradient(self, level) def readGRADIENTRECORD(self, level=1): """ Read a SWFColorTransformWithAlpha """ return SWFGradientRecord(self, level) def readKERNINGRECORD(self, wideCodes): """ Read a SWFKerningRecord """ return SWFKerningRecord(self, wideCodes) def readLANGCODE(self): """ Read a language code """ self.reset_bits_pending() return self.readUI8() def readMATRIX(self): """ Read a SWFMatrix """ return SWFMatrix(self) def readRECT(self): """ Read a SWFMatrix """ r = SWFRectangle() r.parse(self) return r def readSHAPE(self, unit_divisor=20): """ Read a SWFShape """ return SWFShape(self, 1, unit_divisor) def readSHAPEWITHSTYLE(self, level=1, unit_divisor=20): """ Read a SWFShapeWithStyle """ return SWFShapeWithStyle(self, level, unit_divisor) def readCURVEDEDGERECORD(self, num_bits): """ Read a SWFShapeRecordCurvedEdge """ return SWFShapeRecordCurvedEdge(self, num_bits) def readSTRAIGHTEDGERECORD(self, num_bits): """ Read a SWFShapeRecordStraightEdge """ return SWFShapeRecordStraightEdge(self, num_bits) def readSTYLECHANGERECORD(self, states, fill_bits, line_bits, level = 1): """ Read a SWFShapeRecordStyleChange """ return SWFShapeRecordStyleChange(self, states, fill_bits, line_bits, level) def readFILLSTYLE(self, level=1): """ Read a SWFFillStyle """ return SWFFillStyle(self, level) def readTEXTRECORD(self, glyphBits, advanceBits, previousRecord=None, level=1): """ Read a SWFTextRecord """ if self.readUI8() == 0: return None else: self.seek(self.tell() - 1) return SWFTextRecord(self, glyphBits, advanceBits, previousRecord, level) def readLINESTYLE(self, level=1): """ Read a SWFLineStyle """ return SWFLineStyle(self, level) def readLINESTYLE2(self, level=1): """ Read a SWFLineStyle2 """ return SWFLineStyle2(self, level) def readMORPHFILLSTYLE(self, level=1): """ Read a SWFMorphFillStyle """ return SWFMorphFillStyle(self, level) def readMORPHLINESTYLE(self, level=1): """ Read a SWFMorphLineStyle """ return SWFMorphLineStyle(self, level) def readMORPHLINESTYLE2(self, level=1): """ Read a SWFMorphLineStyle2 """ return SWFMorphLineStyle2(self, level) def readMORPHGRADIENT(self, level=1): """ Read a SWFTextRecord """ return SWFMorphGradient(self, level) def readMORPHGRADIENTRECORD(self): """ Read a SWFTextRecord """ return SWFMorphGradientRecord(self) def readACTIONRECORD(self): """ Read a SWFActionRecord """ action = None actionCode = self.readUI8() if actionCode != 0: actionLength = self.readUI16() if actionCode >= 0x80 else 0 #print "0x%x"%actionCode, actionLength action = SWFActionFactory.create(actionCode, actionLength) action.parse(self) return action def readACTIONRECORDs(self): """ Read zero or more button records (zero-terminated) """ out = [] while 1: action = self.readACTIONRECORD() if action: out.append(action) else: break return out def readCLIPACTIONS(self, version): """ Read a SWFClipActions """ return SWFClipActions(self, version) def readCLIPACTIONRECORD(self, version): """ Read a SWFClipActionRecord """ pos = self.tell() flags = self.readUI32() if version >= 6 else self.readUI16() if flags == 0: return None else: self.seek(pos) return SWFClipActionRecord(self, version) def readCLIPEVENTFLAGS(self, version): """ Read a SWFClipEventFlags """ return SWFClipEventFlags(self, version) def readRGB(self): """ Read a RGB color """ self.reset_bits_pending(); r = self.readUI8() g = self.readUI8() b = self.readUI8() return (0xff << 24) | (r << 16) | (g << 8) | b def readRGBA(self): """ Read a RGBA color """ self.reset_bits_pending(); r = self.readUI8() g = self.readUI8() b = self.readUI8() a = self.readUI8() return (a << 24) | (r << 16) | (g << 8) | b def readSYMBOL(self): """ Read a SWFSymbol """ return SWFSymbol(self) def readString(self): """ Read a string """ s = self.f.read(1) string = "" while ord(s) > 0: string += str(s) s = self.f.read(1) return string def readFILTER(self): """ Read a SWFFilter """ filterId = self.readUI8() filter = SWFFilterFactory.create(filterId) filter.parse(self) return filter def readFILTERLIST(self): """ Read a length-prefixed list of FILTERs """ number = self.readUI8() return [self.readFILTER() for _ in range(number)] def readZONEDATA(self): """ Read a SWFZoneData """ return SWFZoneData(self) def readZONERECORD(self): """ Read a SWFZoneRecord """ return SWFZoneRecord(self) def readSOUNDINFO(self): """ Read a SWFSoundInfo """ return SWFSoundInfo(self) def readSOUNDENVELOPE(self): """ Read a SWFSoundEnvelope """ return SWFSoundEnvelope(self) def readBUTTONRECORD(self, version): rc = SWFButtonRecord(data = self, version = version) return rc if rc.valid else None def readBUTTONRECORDs(self, version): """ Read zero or more button records (zero-terminated) """ out = [] while 1: button = self.readBUTTONRECORD(version) if button: out.append(button) else: break return out def readBUTTONCONDACTION(self): """ Read a size-prefixed BUTTONCONDACTION """ size = self.readUI16() if size == 0: return None return SWFButtonCondAction(self) def readBUTTONCONDACTIONSs(self): """ Read zero or more button-condition actions """ out = [] while 1: action = self.readBUTTONCONDACTION() if action: out.append(action) else: break return out def readEXPORT(self): """ Read a SWFExport """ return SWFExport(self) def readMORPHFILLSTYLEARRAY(self): count = self.readUI8() if count == 0xff: count = self.readUI16() return [self.readMORPHFILLSTYLE() for _ in range(count)] def readMORPHLINESTYLEARRAY(self, version): count = self.readUI8() if count == 0xff: count = self.readUI16() kind = self.readMORPHLINESTYLE if version == 1 else self.readMORPHLINESTYLE2 return [kind() for _ in range(count)] def readraw_tag(self): """ Read a SWFRawTag """ return SWFRawTag(self) def readtag_header(self): """ Read a tag header """ pos = self.tell() tag_type_and_length = self.readUI16() tag_length = tag_type_and_length & 0x003f if tag_length == 0x3f: # The SWF10 spec sez that this is a signed int. # Shouldn't it be an unsigned int? tag_length = self.readSI32(); return SWFRecordHeader(tag_type_and_length >> 6, tag_length, self.tell() - pos) def skip_bytes(self, length): """ Skip over the specified number of bytes """ self.f.seek(self.tell() + length) def reset_bits_pending(self): """ Reset the bit array """ self._bits_pending = 0 def read(self, count=0): """ Read """ return self.f.read(count) if count > 0 else self.f.read() def seek(self, pos, whence=0): """ Seek """ self.f.seek(pos, whence) def tell(self): """ Tell """ return self.f.tell() def int32(x): """ Return a signed or unsigned int """ if x>0xFFFFFFFF: raise OverflowError if x>0x7FFFFFFF: x=int(0x100000000-x) if x<2147483648: return -x else: return -2147483648 return x