from .consts import * from .utils import * class _dumb_repr(object): def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.__dict__) class SWFRawTag(_dumb_repr): def __init__(self, s=None): if not s is None: self.parse(s) def parse(self, s): pos = s.tell() self.header = s.readtag_header() self.pos_content = s.tell() s.f.seek(pos) #self.bytes = s.f.read(self.header.tag_length()) #s.f.seek(self.pos_content) class SWFStraightEdge(_dumb_repr): def __init__(self, start, to, line_style_idx, fill_style_idx): self.start = start self.to = to self.line_style_idx = line_style_idx self.fill_style_idx = fill_style_idx def reverse_with_new_fillstyle(self, new_fill_idx): return SWFStraightEdge(self.to, self.start, self.line_style_idx, new_fill_idx) class SWFCurvedEdge(SWFStraightEdge): def __init__(self, start, control, to, line_style_idx, fill_style_idx): super(SWFCurvedEdge, self).__init__(start, to, line_style_idx, fill_style_idx) self.control = control def reverse_with_new_fillstyle(self, new_fill_idx): return SWFCurvedEdge(self.to, self.control, self.start, self.line_style_idx, new_fill_idx) class SWFShape(_dumb_repr): def __init__(self, data=None, level=1, unit_divisor=20.0): self._records = [] self._fillStyles = [] self._lineStyles = [] self._postLineStyles = {} self._edgeMapsCreated = False self.unit_divisor = unit_divisor self.fill_edge_maps = [] self.line_edge_maps = [] self.current_fill_edge_map = {} self.current_line_edge_map = {} self.num_groups = 0 self.coord_map = {} if not data is None: self.parse(data, level) def get_dependencies(self): s = set() for x in self._fillStyles: s.update(x.get_dependencies()) for x in self._lineStyles: s.update(x.get_dependencies()) return s def parse(self, data, level=1): data.reset_bits_pending() fillbits = data.readUB(4) linebits = data.readUB(4) self.read_shape_records(data, fillbits, linebits, level) def export(self, handler=None): self._create_edge_maps() if handler is None: from export import SVGShapeExporter handler = SVGShapeExporter() handler.begin_shape() for i in range(0, self.num_groups): self._export_fill_path(handler, i) self._export_line_path(handler, i) handler.end_shape() return handler @property def records(self): return self._records def read_shape_records(self, data, fill_bits, line_bits, level=1): shape_record = None record_id = 0 while type(shape_record) != SWFShapeRecordEnd: # The SWF10 spec says that shape records are byte aligned. # In reality they seem not to be? # bitsPending = 0; edge_record = (data.readUB(1) == 1) if edge_record: straight_flag = (data.readUB(1) == 1) num_bits = data.readUB(4) + 2 if straight_flag: shape_record = data.readSTRAIGHTEDGERECORD(num_bits) else: shape_record = data.readCURVEDEDGERECORD(num_bits) else: states= data.readUB(5) if states == 0: shape_record = SWFShapeRecordEnd() else: style_change_record = data.readSTYLECHANGERECORD(states, fill_bits, line_bits, level) if style_change_record.state_new_styles: fill_bits = style_change_record.num_fillbits line_bits = style_change_record.num_linebits shape_record = style_change_record shape_record.record_id = record_id self._records.append(shape_record) record_id += 1 #print shape_record.tostring() def _create_edge_maps(self): if self._edgeMapsCreated: return xPos = 0 yPos = 0 sub_path = [] fs_offset = 0 ls_offset = 0 curr_fs_idx0 = 0 curr_fs_idx1 = 0 curr_ls_idx = 0 self.fill_edge_maps = [] self.line_edge_maps = [] self.current_fill_edge_map = {} self.current_line_edge_map = {} self.num_groups = 0 for i in range(0, len(self._records)): rec = self._records[i] if rec.type == SWFShapeRecord.TYPE_STYLECHANGE: if rec.state_line_style or rec.state_fill_style0 or rec.state_fill_style1: if len(sub_path): self._process_sub_path(sub_path, curr_ls_idx, curr_fs_idx0, curr_fs_idx1, rec.record_id) sub_path = [] if rec.state_new_styles: fs_offset = len(self._fillStyles) ls_offset = len(self._lineStyles) self._append_to(self._fillStyles, rec.fill_styles) self._append_to(self._lineStyles, rec.line_styles) if rec.state_line_style and rec.state_fill_style0 and rec.state_fill_style1 and \ rec.line_style == 0 and rec.fill_style0 == 0 and rec.fill_style1 == 0: # new group (probably) self._clean_edge_map(self.current_fill_edge_map) self._clean_edge_map(self.current_line_edge_map) self.fill_edge_maps.append(self.current_fill_edge_map) self.line_edge_maps.append(self.current_line_edge_map) self.current_fill_edge_map = {} self.current_line_edge_map = {} self.num_groups += 1 curr_fs_idx0 = 0 curr_fs_idx1 = 0 curr_ls_idx = 0 else: if rec.state_line_style: curr_ls_idx = rec.line_style if curr_ls_idx > 0: curr_ls_idx += ls_offset if rec.state_fill_style0: curr_fs_idx0 = rec.fill_style0 if curr_fs_idx0 > 0: curr_fs_idx0 += fs_offset if rec.state_fill_style1: curr_fs_idx1 = rec.fill_style1 if curr_fs_idx1 > 0: curr_fs_idx1 += fs_offset if rec.state_moveto: xPos = rec.move_deltaX yPos = rec.move_deltaY elif rec.type == SWFShapeRecord.TYPE_STRAIGHTEDGE: start = [NumberUtils.round_pixels_400(xPos), NumberUtils.round_pixels_400(yPos)] if rec.general_line_flag: xPos += rec.deltaX yPos += rec.deltaY else: if rec.vert_line_flag: yPos += rec.deltaY else: xPos += rec.deltaX to = [NumberUtils.round_pixels_400(xPos), NumberUtils.round_pixels_400(yPos)] sub_path.append(SWFStraightEdge(start, to, curr_ls_idx, curr_fs_idx1)) elif rec.type == SWFShapeRecord.TYPE_CURVEDEDGE: start = [NumberUtils.round_pixels_400(xPos), NumberUtils.round_pixels_400(yPos)] xPosControl = xPos + rec.control_deltaX yPosControl = yPos + rec.control_deltaY xPos = xPosControl + rec.anchor_deltaX yPos = yPosControl + rec.anchor_deltaY control = [xPosControl, yPosControl] to = [NumberUtils.round_pixels_400(xPos), NumberUtils.round_pixels_400(yPos)] sub_path.append(SWFCurvedEdge(start, control, to, curr_ls_idx, curr_fs_idx1)) elif rec.type == SWFShapeRecord.TYPE_END: # We're done. Process the last subpath, if any if len(sub_path) > 0: self._process_sub_path(sub_path, curr_ls_idx, curr_fs_idx0, curr_fs_idx1, rec.record_id) self._clean_edge_map(self.current_fill_edge_map) self._clean_edge_map(self.current_line_edge_map) self.fill_edge_maps.append(self.current_fill_edge_map) self.line_edge_maps.append(self.current_line_edge_map) self.current_fill_edge_map = {} self.current_line_edge_map = {} self.num_groups += 1 curr_fs_idx0 = 0 curr_fs_idx1 = 0 curr_ls_idx = 0 self._edgeMapsCreated = True def _process_sub_path(self, sub_path, linestyle_idx, fillstyle_idx0, fillstyle_idx1, record_id=-1): path = None if fillstyle_idx0 != 0: if not fillstyle_idx0 in self.current_fill_edge_map: path = self.current_fill_edge_map[fillstyle_idx0] = [] else: path = self.current_fill_edge_map[fillstyle_idx0] for j in range(len(sub_path) - 1, -1, -1): path.append(sub_path[j].reverse_with_new_fillstyle(fillstyle_idx0)) if fillstyle_idx1 != 0: if not fillstyle_idx1 in self.current_fill_edge_map: path = self.current_fill_edge_map[fillstyle_idx1] = [] else: path = self.current_fill_edge_map[fillstyle_idx1] self._append_to(path, sub_path) if linestyle_idx != 0: if not linestyle_idx in self.current_line_edge_map: path = self.current_line_edge_map[linestyle_idx] = [] else: path = self.current_line_edge_map[linestyle_idx] self._append_to(path, sub_path) def _clean_edge_map(self, edge_map): for style_idx in edge_map: sub_path = edge_map[style_idx] if style_idx in edge_map else None if sub_path is not None and len(sub_path) > 0: tmp_path = [] prev_edge = None self._create_coord_map(sub_path) while len(sub_path) > 0: idx = 0 while idx < len(sub_path): if prev_edge is None or self._equal_point(prev_edge.to, sub_path[idx].start): edge = sub_path[idx] del sub_path[idx] tmp_path.append(edge) self._remove_edge_from_coord_map(edge) prev_edge = edge else: edge = self._find_next_edge_in_coord_map(prev_edge) if not edge is None: idx = sub_path.index(edge) else: idx = 0 prev_edge = None edge_map[style_idx] = tmp_path def _equal_point(self, a, b, tol=0.001): return (a[0] > b[0]-tol and a[0] < b[0]+tol and a[1] > b[1]-tol and a[1] < b[1]+tol) def _find_next_edge_in_coord_map(self, edge): key = "%0.4f_%0.4f" % (edge.to[0], edge.to[1]) if key in self.coord_map and len(self.coord_map[key]) > 0: return self.coord_map[key][0] else: return None def _create_coord_map(self, path): self.coord_map = {} for i in range(0, len(path)): start = path[i].start key = "%0.4f_%0.4f" % (start[0], start[1]) coord_map_array = self.coord_map[key] if key in self.coord_map else None if coord_map_array is None: self.coord_map[key] = [path[i]] else: self.coord_map[key].append(path[i]) def _remove_edge_from_coord_map(self, edge): key = "%0.4f_%0.4f" % (edge.start[0], edge.start[1]) if key in self.coord_map: coord_map_array = self.coord_map[key] if len(coord_map_array) == 1: del self.coord_map[key] else: try: idx = coord_map_array.index(edge) del coord_map_array[idx] except: pass def _create_path_from_edge_map(self, edge_map): new_path = [] style_ids = [] for style_id in edge_map: style_ids.append(int(style_id)) style_ids = sorted(style_ids) for i in range(0, len(style_ids)): self._append_to(new_path, edge_map[style_ids[i]]) return new_path def _export_fill_path(self, handler, group_index): path = self._create_path_from_edge_map(self.fill_edge_maps[group_index]) pos = [100000000, 100000000] u = 1.0 / self.unit_divisor fill_style_idx = 10000000 if len(path) < 1: return handler.begin_fills() for i in range(0, len(path)): e = path[i] if fill_style_idx != e.fill_style_idx: fill_style_idx = e.fill_style_idx pos = [100000000, 100000000] try: fill_style = self._fillStyles[fill_style_idx - 1] if fill_style_idx > 0 else None if fill_style.type == 0x0: # solid fill handler.begin_fill( ColorUtils.rgb(fill_style.rgb), ColorUtils.alpha(fill_style.rgb)) elif fill_style.type in [0x10, 0x12, 0x13]: # gradient fill colors = [] ratios = [] alphas = [] for j in range(0, len(fill_style.gradient.records)): gr = fill_style.gradient.records[j] colors.append(ColorUtils.rgb(gr.color)) ratios.append(gr.ratio) alphas.append(ColorUtils.alpha(gr.color)) handler.begin_gradient_fill( GradientType.LINEAR if fill_style.type == 0x10 else GradientType.RADIAL, colors, alphas, ratios, fill_style.gradient_matrix, fill_style.gradient.spreadmethod, fill_style.gradient.interpolation_mode, fill_style.gradient.focal_point ) elif fill_style.type in [0x40, 0x41, 0x42, 0x43]: # bitmap fill handler.begin_bitmap_fill( fill_style.bitmap_id, fill_style.bitmap_matrix, (fill_style.type == 0x40 or fill_style.type == 0x42), (fill_style.type == 0x40 or fill_style.type == 0x41) ) pass except: # Font shapes define no fillstyles per se, but do reference fillstyle index 1, # which represents the font color. We just report solid black in this case. handler.begin_fill(0) if not self._equal_point(pos, e.start): handler.move_to(e.start[0] * u, e.start[1] * u) if type(e) is SWFCurvedEdge: handler.curve_to(e.control[0] * u, e.control[1] * u, e.to[0] * u, e.to[1] * u) else: handler.line_to(e.to[0] * u, e.to[1] * u) pos = e.to handler.end_fill() handler.end_fills() def _export_line_path(self, handler, group_index): path = self._create_path_from_edge_map(self.line_edge_maps[group_index]) pos = [100000000, 100000000] u = 1.0 / self.unit_divisor line_style_idx = 10000000 line_style = None if len(path) < 1: return handler.begin_lines() for i in range(0, len(path)): e = path[i] if line_style_idx != e.line_style_idx: line_style_idx = e.line_style_idx pos = [100000000, 100000000] try: line_style = self._lineStyles[line_style_idx - 1] except: line_style = None if line_style is not None: scale_mode = LineScaleMode.NORMAL if line_style.no_hscale_flag and line_style.no_vscale_flag: scale_mode = LineScaleMode.NONE elif line_style.no_hscale_flag: scale_mode = LineScaleMode.HORIZONTAL elif line_style.no_hscale_flag: scale_mode = LineScaleMode.VERTICAL if not line_style.has_fill_flag: handler.line_style( line_style.width / 20.0, ColorUtils.rgb(line_style.color), ColorUtils.alpha(line_style.color), line_style.pixelhinting_flag, scale_mode, line_style.start_caps_style, line_style.end_caps_style, line_style.joint_style, line_style.miter_limit_factor) else: fill_style = line_style.fill_type if fill_style.type in [0x10, 0x12, 0x13]: # gradient fill colors = [] ratios = [] alphas = [] for j in range(0, len(fill_style.gradient.records)): gr = fill_style.gradient.records[j] colors.append(ColorUtils.rgb(gr.color)) ratios.append(gr.ratio) alphas.append(ColorUtils.alpha(gr.color)) handler.line_gradient_style( line_style.width / 20.0, line_style.pixelhinting_flag, scale_mode, line_style.start_caps_style, line_style.end_caps_style, line_style.joint_style, line_style.miter_limit_factor, GradientType.LINEAR if fill_style.type == 0x10 else GradientType.RADIAL, colors, alphas, ratios, fill_style.gradient_matrix, fill_style.gradient.spreadmethod, fill_style.gradient.interpolation_mode, fill_style.gradient.focal_point ) elif fill_style.type in [0x40, 0x41, 0x42]: handler.line_bitmap_style( line_style.width / 20.0, line_style.pixelhinting_flag, scale_mode, line_style.start_caps_style, line_style.end_caps_style, line_style.joint_style, line_style.miter_limit_factor, fill_style.bitmap_id, fill_style.bitmap_matrix, (fill_style.type == 0x40 or fill_style.type == 0x42), (fill_style.type == 0x40 or fill_style.type == 0x41) ) else: # we should never get here handler.line_style(0) if not self._equal_point(pos, e.start): handler.move_to(e.start[0] * u, e.start[1] * u) if type(e) is SWFCurvedEdge: handler.curve_to(e.control[0] * u, e.control[1] * u, e.to[0] * u, e.to[1] * u) else: handler.line_to(e.to[0] * u, e.to[1] * u) pos = e.to handler.end_lines() def _append_to(self, v1, v2): for i in range(0, len(v2)): v1.append(v2[i]) def __str__(self): return "[SWFShape]" class SWFShapeWithStyle(SWFShape): def __init__(self, data, level, unit_divisor): self._initialFillStyles = [] self._initialLineStyles = [] super(SWFShapeWithStyle, self).__init__(data, level, unit_divisor) def export(self, handler=None): self._fillStyles.extend(self._initialFillStyles) self._lineStyles.extend(self._initialLineStyles) return super(SWFShapeWithStyle, self).export(handler) def get_dependencies(self): s = set() for x in self._fillStyles + self._initialFillStyles: s.update(x.get_dependencies()) for x in self._lineStyles + self._initialLineStyles: s.update(x.get_dependencies()) return s def parse(self, data, level=1): data.reset_bits_pending() num_fillstyles = self.readstyle_array_length(data, level) for i in range(0, num_fillstyles): self._initialFillStyles.append(data.readFILLSTYLE(level)) num_linestyles = self.readstyle_array_length(data, level) for i in range(0, num_linestyles): if level <= 3: self._initialLineStyles.append(data.readLINESTYLE(level)) else: self._initialLineStyles.append(data.readLINESTYLE2(level)) num_fillbits = data.readUB(4) num_linebits = data.readUB(4) data.reset_bits_pending() self.read_shape_records(data, num_fillbits, num_linebits, level) def readstyle_array_length(self, data, level=1): length = data.readUI8() if level >= 2 and length == 0xff: length = data.readUI16() return length def __str__(self): s = " FillStyles:\n" if len(self._fillStyles) > 0 else "" for i in range(0, len(self._initialFillStyles)): s += " %d:%s\n" % (i+1, self._initialFillStyles[i].__str__()) if len(self._initialLineStyles) > 0: s += " LineStyles:\n" for i in range(0, len(self._initialLineStyles)): s += " %d:%s\n" % (i+1, self._initialLineStyles[i].__str__()) for record in self._records: s += record.__str__() + '\n' return s.rstrip() + super(SWFShapeWithStyle, self).__str__() class SWFShapeRecord(_dumb_repr): TYPE_UNKNOWN = 0 TYPE_END = 1 TYPE_STYLECHANGE = 2 TYPE_STRAIGHTEDGE = 3 TYPE_CURVEDEDGE = 4 record_id = -1 def __init__(self, data=None, level=1): if not data is None: self.parse(data, level) @property def is_edge_record(self): return (self.type == SWFShapeRecord.TYPE_STRAIGHTEDGE or self.type == SWFShapeRecord.TYPE_CURVEDEDGE) def parse(self, data, level=1): pass @property def type(self): return SWFShapeRecord.TYPE_UNKNOWN def __str__(self): return " [SWFShapeRecord]" class SWFShapeRecordStraightEdge(SWFShapeRecord): def __init__(self, data, num_bits=0, level=1): self.num_bits = num_bits super(SWFShapeRecordStraightEdge, self).__init__(data, level) def parse(self, data, level=1): self.general_line_flag = (data.readUB(1) == 1) self.vert_line_flag = False if self.general_line_flag else (data.readUB(1) == 1) self.deltaX = data.readSB(self.num_bits) \ if self.general_line_flag or not self.vert_line_flag \ else 0.0 self.deltaY = data.readSB(self.num_bits) \ if self.general_line_flag or self.vert_line_flag \ else 0.0 @property def type(self): return SWFShapeRecord.TYPE_STRAIGHTEDGE def __str__(self): s = " [SWFShapeRecordStraightEdge]" if self.general_line_flag: s += " General: %d %d" % (self.deltaX, self.deltaY) else: if self.vert_line_flag: s += " Vertical: %d" % self.deltaY else: s += " Horizontal: %d" % self.deltaX return s class SWFShapeRecordCurvedEdge(SWFShapeRecord): def __init__(self, data, num_bits=0, level=1): self.num_bits = num_bits super(SWFShapeRecordCurvedEdge, self).__init__(data, level) def parse(self, data, level=1): self.control_deltaX = data.readSB(self.num_bits) self.control_deltaY = data.readSB(self.num_bits) self.anchor_deltaX = data.readSB(self.num_bits) self.anchor_deltaY = data.readSB(self.num_bits) @property def type(self): return SWFShapeRecord.TYPE_CURVEDEDGE def __str__(self): return " [SWFShapeRecordCurvedEdge]" + \ " ControlDelta: %d, %d" % (self.control_deltaX, self.control_deltaY) + \ " AnchorDelta: %d, %d" % (self.anchor_deltaX, self.anchor_deltaY) class SWFShapeRecordStyleChange(SWFShapeRecord): def __init__(self, data, states=0, fill_bits=0, line_bits=0, level=1): self.fill_styles = [] self.line_styles = [] self.state_new_styles = ((states & 0x10) != 0) self.state_line_style = ((states & 0x08) != 0) self.state_fill_style1 = ((states & 0x4) != 0) self.state_fill_style0 = ((states & 0x2) != 0) self.state_moveto = ((states & 0x1) != 0) self.num_fillbits = fill_bits self.num_linebits = line_bits self.move_deltaX = 0.0 self.move_deltaY = 0.0 self.fill_style0 = 0 self.fill_style1 = 0 self.line_style = 0 super(SWFShapeRecordStyleChange, self).__init__(data, level) def parse(self, data, level=1): if self.state_moveto: movebits = data.readUB(5) self.move_deltaX = data.readSB(movebits) self.move_deltaY = data.readSB(movebits) self.fill_style0 = data.readUB(self.num_fillbits) if self.state_fill_style0 else 0 self.fill_style1 = data.readUB(self.num_fillbits) if self.state_fill_style1 else 0 self.line_style = data.readUB(self.num_linebits) if self.state_line_style else 0 if self.state_new_styles: data.reset_bits_pending(); num_fillstyles = self.readstyle_array_length(data, level) for i in range(0, num_fillstyles): self.fill_styles.append(data.readFILLSTYLE(level)) num_linestyles = self.readstyle_array_length(data, level) for i in range(0, num_linestyles): if level <= 3: self.line_styles.append(data.readLINESTYLE(level)) else: self.line_styles.append(data.readLINESTYLE2(level)) self.num_fillbits = data.readUB(4) self.num_linebits = data.readUB(4) @property def type(self): return SWFShapeRecord.TYPE_STYLECHANGE def readstyle_array_length(self, data, level=1): length = data.readUI8() if level >= 2 and length == 0xff: length = data.readUI16() return length def __str__(self): return " [SWFShapeRecordStyleChange]" + \ " moveTo: %d %d" % (self.move_deltaX, self.move_deltaY) + \ " fs0: %d" % self.fill_style0 + \ " fs1: %d" % self.fill_style1 + \ " linestyle: %d" % self.line_style + \ " flags: %d %d %d" % (self.state_fill_style0, self.state_fill_style1, self.state_line_style) class SWFShapeRecordEnd(SWFShapeRecord): def __init__(self): super(SWFShapeRecordEnd, self).__init__(None) def parse(self, data, level=1): pass @property def type(self): return SWFShapeRecord.TYPE_END def __str__(self): return " [SWFShapeRecordEnd]" class SWFMatrix(_dumb_repr): def __init__(self, data): self.scaleX = 1.0 self.scaleY = 1.0 self.rotateSkew0 = 0.0 self.rotateSkew1 = 0.0 self.translateX = 0.0 self.translateY = 0.0 if not data is None: self.parse(data) def parse(self, data): data.reset_bits_pending(); self.scaleX = 1.0 self.scaleY = 1.0 if data.readUB(1) == 1: scaleBits = data.readUB(5) self.scaleX = data.readFB(scaleBits) self.scaleY = data.readFB(scaleBits) self.rotateSkew0 = 0.0 self.rotateSkew1 = 0.0 if data.readUB(1) == 1: rotateBits = data.readUB(5) self.rotateSkew0 = data.readFB(rotateBits) self.rotateSkew1 = data.readFB(rotateBits) translateBits = data.readUB(5) self.translateX = data.readSB(translateBits) self.translateY = data.readSB(translateBits) def to_array(self): return [ self.scaleX, self.rotateSkew0, self.rotateSkew1, self.scaleY, self.translateX, self.translateY ] def __str__(self): def fmt(s): return "%0.2f" % s return "[%s]" % ",".join(map(fmt, self.to_array())) class SWFGradientRecord(_dumb_repr): def __init__(self, data=None, level=1): self._records = [] if not data is None: self.parse(data, level) def parse(self, data, level=1): self.ratio = data.readUI8() self.color = data.readRGB() if level <= 2 else data.readRGBA() def __str__(self): return "[SWFGradientRecord] Color: %s, Ratio: %d" % (ColorUtils.to_rgb_string(self.color), self.ratio) class SWFGradient(_dumb_repr): def __init__(self, data=None, level=1): self._records = [] self.focal_point = 0.0 if not data is None: self.parse(data, level) @property def records(self): return self._records def parse(self, data, level=1): data.reset_bits_pending(); self.spreadmethod = data.readUB(2) self.interpolation_mode = data.readUB(2) num_gradients = data.readUB(4) for i in range(0, num_gradients): self._records.append(data.readGRADIENTRECORD(level)) def __str__(self): s = "[SWFGadient]" for record in self._records: s += "\n " + record.__str__() return s class SWFFocalGradient(SWFGradient): def __init__(self, data=None, level=1): super(SWFFocalGradient, self).__init__(data, level) def parse(self, data, level=1): super(SWFFocalGradient, self).parse(data, level) self.focal_point = data.readFIXED8() def __str__(self): return "[SWFFocalGradient] Color: %s, Ratio: %d, Focal: %0.2f" % \ (ColorUtils.to_rgb_string(self.color), self.ratio, self.focal_point) class SWFFillStyle(_dumb_repr): def __init__(self, data=None, level=1): if not data is None: self.parse(data, level) COLOR = [0x0] GRADIENT = [0x10, 0x12, 0x13] BITMAP = [0x40, 0x41, 0x42, 0x43] def parse(self, data, level=1): self.type = data.readUI8() if self.type in SWFFillStyle.COLOR: self.rgb = data.readRGB() if level <= 2 else data.readRGBA() elif self.type in SWFFillStyle.GRADIENT: self.gradient_matrix = data.readMATRIX() self.gradient = data.readFOCALGRADIENT(level) if self.type == 0x13 else data.readGRADIENT(level) elif self.type in SWFFillStyle.BITMAP: self.bitmap_id = data.readUI16() self.bitmap_matrix = data.readMATRIX() else: raise Exception("Unknown fill style type: 0x%x" % self.type, level) def get_dependencies(self): return set([self.bitmap_id]) if self.type in SWFFillStyle.BITMAP else set() def __str__(self): s = "[SWFFillStyle] " if self.type in SWFFillStyle.COLOR: s += "Color: %s" % ColorUtils.to_rgb_string(self.rgb) elif self.type in SWFFillStyle.GRADIENT: s += "Gradient: %s" % self.gradient_matrix elif self.type in SWFFillStyle.BITMAP: s += "BitmapID: %d" % (self.bitmap_id) return s class SWFLineStyle(_dumb_repr): def __init__(self, data=None, level=1): # forward declarations for SWFLineStyle2 self.start_caps_style = LineCapsStyle.ROUND self.end_caps_style = LineCapsStyle.ROUND self.joint_style = LineJointStyle.ROUND self.has_fill_flag = False self.no_hscale_flag = False self.no_vscale_flag = False self.pixelhinting_flag = False self.no_close = False self.miter_limit_factor = 3.0 self.fill_type = None self.width = 1 self.color = 0 if not data is None: self.parse(data, level) def get_dependencies(self): return set() def parse(self, data, level=1): self.width = data.readUI16() self.color = data.readRGB() if level <= 2 else data.readRGBA() def __str__(self): s = "[SWFLineStyle] " s += "Color: %s, Width: %d" % (ColorUtils.to_rgb_string(self.color), self.width) return s class SWFLineStyle2(SWFLineStyle): def __init__(self, data=None, level=1): super(SWFLineStyle2, self).__init__(data, level) def parse(self, data, level=1): self.width = data.readUI16() self.start_caps_style = data.readUB(2) self.joint_style = data.readUB(2) self.has_fill_flag = (data.readUB(1) == 1) self.no_hscale_flag = (data.readUB(1) == 1) self.no_vscale_flag = (data.readUB(1) == 1) self.pixelhinting_flag = (data.readUB(1) == 1) data.readUB(5) self.no_close = (data.readUB(1) == 1) self.end_caps_style = data.readUB(2) if self.joint_style == LineJointStyle.MITER: self.miter_limit_factor = data.readFIXED8() if self.has_fill_flag: self.fill_type = data.readFILLSTYLE(level) else: self.color = data.readRGBA() def __str__(self): s = "[SWFLineStyle2] " s += "Width: %d, " % self.width s += "StartCapsStyle: %d, " % self.start_caps_style s += "JointStyle: %d, " % self.joint_style s += "HasFillFlag: %d, " % self.has_fill_flag s += "NoHscaleFlag: %d, " % self.no_hscale_flag s += "NoVscaleFlag: %d, " % self.no_vscale_flag s += "PixelhintingFlag: %d, " % self.pixelhinting_flag s += "NoClose: %d, " % self.no_close if self.joint_style: s += "MiterLimitFactor: %d" % self.miter_limit_factor if self.has_fill_flag: s += "FillType: %s, " % self.fill_type else: s += "Color: %s" % ColorUtils.to_rgb_string(self.color) return s class SWFMorphGradientRecord(_dumb_repr): def __init__(self, data): if not data is None: self.parse(data) def parse(self, data): self.startRatio = data.readUI8() self.startColor = data.readRGBA() self.endRatio = data.readUI8() self.endColor = data.readRGBA() class SWFMorphGradient(_dumb_repr): def __init__(self, data, level=1): self.records = [] if not data is None: self.parse(data, level) def parse(self, data, level=1): self.records = [] numGradients = data.readUI8() for i in range(0, numGradients): self.records.append(data.readMORPHGRADIENTRECORD()) class SWFMorphFillStyle(_dumb_repr): def __init__(self, data, level=1): if not data is None: self.parse(data, level) def get_dependencies(self): return set([self.bitmapId]) if hasattr(self, 'bitmapId') else set() def parse(self, data, level=1): type = data.readUI8() if type == 0x0: self.startColor = data.readRGBA() self.endColor = data.readRGBA() elif type in [0x10, 0x12]: self.startGradientMatrix = data.readMATRIX() self.endGradientMatrix = data.readMATRIX() self.gradient = data.readMORPHGRADIENT(level) elif type in [0x40, 0x41, 0x42, 0x43]: self.bitmapId = data.readUI16() self.startBitmapMatrix = data.readMATRIX() self.endBitmapMatrix = data.readMATRIX() class SWFMorphLineStyle(_dumb_repr): def __init__(self, data, level=1): # Forward declaration of SWFMorphLineStyle2 properties self.startCapsStyle = LineCapsStyle.ROUND self.endCapsStyle = LineCapsStyle.ROUND self.jointStyle = LineJointStyle.ROUND self.hasFillFlag = False self.noHScaleFlag = False self.noVScaleFlag = False self.pixelHintingFlag = False self.noClose = False self.miterLimitFactor = 3 self.fillType = None if not data is None: self.parse(data, level) def parse(self, data, level=1): self.startWidth = data.readUI16() self.endWidth = data.readUI16() self.startColor = data.readRGBA() self.endColor = data.readRGBA() class SWFMorphLineStyle2(SWFMorphLineStyle): def __init__(self, data, level=1): super(SWFMorphLineStyle2, self).__init__(data, level) def parse(self, data, level=1): self.startWidth = data.readUI16() self.endWidth = data.readUI16() self.startCapsStyle = data.readUB(2) self.jointStyle = data.readUB(2) self.hasFillFlag = (data.readUB(1) == 1) self.noHScaleFlag = (data.readUB(1) == 1) self.noVScaleFlag = (data.readUB(1) == 1) self.pixelHintingFlag = (data.readUB(1) == 1) reserved = data.readUB(5); self.noClose = (data.readUB(1) == 1) self.endCapsStyle = data.readUB(2) if self.jointStyle == LineJointStyle.MITER: self.miterLimitFactor = data.readFIXED8() if self.hasFillFlag: self.fillType = data.readMORPHFILLSTYLE(level) else: self.startColor = data.readRGBA() self.endColor = data.readRGBA() class SWFRecordHeader(_dumb_repr): def __init__(self, type, content_length, header_length): self.type = type self.content_length = content_length self.header_length = header_length @property def tag_length(self): return self.header_length + self.content_length class SWFRectangle(_dumb_repr): def __init__(self): self.xmin = self.xmax = self.ymin = self.ymax = 0 def parse(self, s): s.reset_bits_pending() bits = s.readUB(5) self.xmin = s.readSB(bits) self.xmax = s.readSB(bits) self.ymin = s.readSB(bits) self.ymax = s.readSB(bits) @property def dimensions(self): """ Returns dimensions as (x, y) tuple. """ return (self.xmax - self.xmin, self.ymax - self.ymin) def __str__(self): return "[xmin: %d xmax: %d ymin: %d ymax: %d]" % (self.xmin/20, self.xmax/20, self.ymin/20, self.ymax/20) class SWFColorTransform(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): data.reset_bits_pending() self.hasAddTerms = (data.readUB(1) == 1) self.hasMultTerms = (data.readUB(1) == 1) bits = data.readUB(4) self.rMult = 1 self.gMult = 1 self.bMult = 1 if self.hasMultTerms: self.rMult = data.readSB(bits) self.gMult = data.readSB(bits) self.bMult = data.readSB(bits) self.rAdd = 0 self.gAdd = 0 self.bAdd = 0 if self.hasAddTerms: self.rAdd = data.readSB(bits) self.gAdd = data.readSB(bits) self.bAdd = data.readSB(bits) @property def matrix(self): return [ self.rMult / 256.0, 0.0, 0.0, 0.0, self.rAdd / 256.0, 0.0, self.gMult / 256.0, 0.0, 0.0, self.gAdd / 256.0, 0.0, 0.0, self.bMult / 256.0, 0.0, self.bAdd / 256.0, 0.0, 0.0, 0.0, 1.0, 1.0 ] def __str__(self): return "[%d %d %d %d %d %d]" % \ (self.rMult, self.gMult, self.bMult, self.rAdd, self.gAdd, self.bAdd) class SWFColorTransformWithAlpha(SWFColorTransform): def __init__(self, data=None): super(SWFColorTransformWithAlpha, self).__init__(data) def parse(self, data): data.reset_bits_pending() self.hasAddTerms = (data.readUB(1) == 1) self.hasMultTerms = (data.readUB(1) == 1) bits = data.readUB(4) self.rMult = 1 self.gMult = 1 self.bMult = 1 self.aMult = 1 if self.hasMultTerms: self.rMult = data.readSB(bits) self.gMult = data.readSB(bits) self.bMult = data.readSB(bits) self.aMult = data.readSB(bits) self.rAdd = 0 self.gAdd = 0 self.bAdd = 0 self.aAdd = 0 if self.hasAddTerms: self.rAdd = data.readSB(bits) self.gAdd = data.readSB(bits) self.bAdd = data.readSB(bits) self.aAdd = data.readSB(bits) @property def matrix(self): ''' Gets the matrix as a 20 item list ''' return [ self.rMult / 256.0, 0.0, 0.0, 0.0, self.rAdd / 256.0, 0.0, self.gMult / 256.0, 0.0, 0.0, self.gAdd / 256.0, 0.0, 0.0, self.bMult / 256.0, 0.0, self.bAdd / 256.0, 0.0, 0.0, 0.0, self.aMult / 256.0, self.aAdd / 256.0 ] def __str__(self): return "[%d %d %d %d %d %d %d %d]" % \ (self.rMult, self.gMult, self.bMult, self.aMult, self.rAdd, self.gAdd, self.bAdd, self.aAdd) class SWFFrameLabel(_dumb_repr): def __init__(self, frameNumber, name): self.frameNumber = frameNumber self.name = name def __str__(self): return "Frame: %d, Name: %s" % (self.frameNumber, self.name) class SWFScene(_dumb_repr): def __init__(self, offset, name): self.offset = offset self.name = name def __str__(self): return "Scene: %d, Name: '%s'" % (self.offset, self.name) class SWFSymbol(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): self.tagId = data.readUI16() self.name = data.readString() def __str__(self): return "ID %d, Name: %s" % (self.tagId, self.name) class SWFGlyphEntry(_dumb_repr): def __init__(self, data=None, glyphBits=0, advanceBits=0): if not data is None: self.parse(data, glyphBits, advanceBits) def parse(self, data, glyphBits, advanceBits): # GLYPHENTRYs are not byte aligned self.index = data.readUB(glyphBits) self.advance = data.readSB(advanceBits) def __str__(self): return "Index: %d, Advance: %d" % (self.index, self.advance) class SWFKerningRecord(_dumb_repr): def __init__(self, data=None, wideCodes=False): if not data is None: self.parse(data, wideCodes) def parse(self, data, wideCodes): self.code1 = data.readUI16() if wideCodes else data.readUI8() self.code2 = data.readUI16() if wideCodes else data.readUI8() self.adjustment = data.readSI16() def __str__(self): return "Code1: %d, Code2: %d, Adjustment: %d" % (self.code1, self.code2, self.adjustment) class SWFTextRecord(_dumb_repr): def __init__(self, data=None, glyphBits=0, advanceBits=0, previousRecord=None, level=1): self.hasFont = False self.hasColor = False self.hasYOffset = False self.hasXOffset = False self.fontId = -1 self.textColor = 0 self.xOffset = 0 self.yOffset = 0 self.textHeight = 12 self.glyphEntries = [] if not data is None: self.parse(data, glyphBits, advanceBits, previousRecord, level) def get_dependencies(self): return set([self.fontId]) if self.hasFont else set() def parse(self, data, glyphBits, advanceBits, previousRecord=None, level=1): self.glyphEntries = [] styles = data.readUI8() self.type = styles >> 7 self.hasFont = ((styles & 0x08) != 0) self.hasColor = ((styles & 0x04) != 0) self.hasYOffset = ((styles & 0x02) != 0) self.hasXOffset = ((styles & 0x01) != 0) if self.hasFont: self.fontId = data.readUI16() elif not previousRecord is None: self.fontId = previousRecord.fontId if self.hasColor: self.textColor = data.readRGB() if level < 2 else data.readRGBA() elif not previousRecord is None: self.textColor = previousRecord.textColor if self.hasXOffset: self.xOffset = data.readSI16(); elif not previousRecord is None: self.xOffset = previousRecord.xOffset if self.hasYOffset: self.yOffset = data.readSI16(); elif not previousRecord is None: self.yOffset = previousRecord.yOffset if self.hasFont: self.textHeight = data.readUI16() elif not previousRecord is None: self.textHeight = previousRecord.textHeight glyphCount = data.readUI8() for i in range(0, glyphCount): self.glyphEntries.append(data.readGLYPHENTRY(glyphBits, advanceBits)) def __str__(self): return "[SWFTextRecord]" class SWFClipActions(_dumb_repr): def __init__(self, data=None, version=0): self.eventFlags = None self.records = [] if not data is None: self.parse(data, version) def parse(self, data, version): data.readUI16() # reserved, always 0 self.eventFlags = data.readCLIPEVENTFLAGS(version) self.records = [] record = data.readCLIPACTIONRECORD(version) while not record is None: self.records.append(record) record = data.readCLIPACTIONRECORD(version) def __str__(self): return "[SWFClipActions]" class SWFClipActionRecord(_dumb_repr): def __init__(self, data=None, version=0): self.eventFlags = None self.keyCode = 0 self.actions = [] if not data is None: self.parse(data, version) def parse(self, data, version): self.actions = [] self.eventFlags = data.readCLIPEVENTFLAGS(version) data.readUI32() # actionRecordSize, not needed here if self.eventFlags.keyPressEvent: self.keyCode = data.readUI8() action = data.readACTIONRECORD() while not action is None: self.actions.append(action) action = data.readACTIONRECORD() def __str__(self): return "[SWFClipActionRecord]" class SWFClipEventFlags(_dumb_repr): keyUpEvent = False keyDownEvent = False mouseUpEvent = False mouseDownEvent = False mouseMoveEvent = False unloadEvent = False enterFrameEvent = False loadEvent = False dragOverEvent = False # SWF6 rollOutEvent = False # SWF6 rollOverEvent = False # SWF6 releaseOutsideEvent = False # SWF6 releaseEvent = False # SWF6 pressEvent = False # SWF6 initializeEvent = False # SWF6 dataEvent = False constructEvent = False # SWF7 keyPressEvent = False # SWF6 dragOutEvent = False # SWF6 def __init__(self, data=None, version=0): if not data is None: self.parse(data, version) def parse(self, data, version): flags1 = data.readUI8(); self.keyUpEvent = ((flags1 & 0x80) != 0) self.keyDownEvent = ((flags1 & 0x40) != 0) self.mouseUpEvent = ((flags1 & 0x20) != 0) self.mouseDownEvent = ((flags1 & 0x10) != 0) self.mouseMoveEvent = ((flags1 & 0x08) != 0) self.unloadEvent = ((flags1 & 0x04) != 0) self.enterFrameEvent = ((flags1 & 0x02) != 0) self.loadEvent = ((flags1 & 0x01) != 0) flags2 = data.readUI8() self.dragOverEvent = ((flags2 & 0x80) != 0) self.rollOutEvent = ((flags2 & 0x40) != 0) self.rollOverEvent = ((flags2 & 0x20) != 0) self.releaseOutsideEvent = ((flags2 & 0x10) != 0) self.releaseEvent = ((flags2 & 0x08) != 0) self.pressEvent = ((flags2 & 0x04) != 0) self.initializeEvent = ((flags2 & 0x02) != 0) self.dataEvent = ((flags2 & 0x01) != 0) if version >= 6: flags3 = data.readUI8() self.constructEvent = ((flags3 & 0x04) != 0) self.keyPressEvent = ((flags3 & 0x02) != 0) self.dragOutEvent = ((flags3 & 0x01) != 0) data.readUI8() # reserved, always 0 def __str__(self): return "[SWFClipEventFlags]" class SWFZoneData(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): self.alignmentCoordinate = data.readFLOAT16() self.zoneRange = data.readFLOAT16() def __str__(self): return "[SWFZoneData]" class SWFZoneRecord(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): self.zoneData = [] numZoneData = data.readUI8() for i in range(0, numZoneData): self.zoneData.append(data.readZONEDATA()) mask = data.readUI8() self.maskX = ((mask & 0x01) != 0) self.maskY = ((mask & 0x02) != 0) def __str__(self): return "[SWFZoneRecord]" class SWFSoundInfo(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): reserved = data.readUB(2) assert reserved == 0 self.syncStop = data.readUB(1) == 1 self.syncNoMultiple = data.readUB(1) == 1 self.hasEnvelope = data.readUB(1) == 1 self.hasLoops = data.readUB(1) == 1 self.hasOutPoint = data.readUB(1) == 1 self.hasInPoint = data.readUB(1) == 1 self.inPoint = data.readUI32() if self.hasInPoint else None self.outPoint = data.readUI32() if self.hasOutPoint else None self.loopCount = data.readUI16() if self.hasLoops else None self.envPointCount = data.readUI8() if self.hasEnvelope else None self.envelopePoints = [data.readSOUNDENVELOPE() for x in xrange(self.envPointCount)] if self.hasEnvelope else None def __str__(self): return "[SWFSoundInfo]" class SWFSoundEnvelope(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): self.position = data.readUI32() self.leftLevel = data.readUI16() self.rightLevel = data.readUI16() def __str__(self): return "[SWFSoundEnvelope]" class SWFButtonRecord(_dumb_repr): def __init__(self, version, data=None): # version is 1 for DefineButton, 2 for DefineButton2, etc if not data is None: self.parse(data, version) def get_dependencies(self): return set([self.characterId]) if self.valid else set() def parse(self, data, version): reserved0 = data.readUB(2) self.hasBlendMode = data.readUB(1) == 1 self.hasFilterList = data.readUB(1) == 1 self.stateHitTest = data.readUB(1) == 1 self.stateDown = data.readUB(1) == 1 self.stateOver = data.readUB(1) == 1 self.stateUp = data.readUB(1) == 1 self.valid = reserved0 or self.hasBlendMode or \ self.hasFilterList or self.stateHitTest or \ self.stateDown or self.stateOver or self.stateUp if not self.valid: return self.characterId = data.readUI16() self.placeDepth = data.readUI16() self.placeMatrix = data.readMATRIX() if version == 2: self.colorTransform = data.readCXFORMWITHALPHA() self.filterList = data.readFILTERLIST() if self.hasFilterList else None self.blendMode = data.readUI8() if self.hasBlendMode else 0 def __str__(self): return "[SWFButtonRecord]" def __repr__(self): return "[SWFButtonRecord %r]" % self.__dict__ class SWFButtonCondAction(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def parse(self, data): self.idleToOverDown = data.readUB(1) == 1 self.outDownToIdle = data.readUB(1) == 1 self.outDownToOverDown = data.readUB(1) == 1 self.overDownToOutDown = data.readUB(1) == 1 self.overDownToOverUp = data.readUB(1) == 1 self.overUpToOverDown = data.readUB(1) == 1 self.overUpToIdle = data.readUB(1) == 1 self.idleToOverUp = data.readUB(1) == 1 self.keyPress = data.readUB(7) self.overDownToIdle = data.readUB(1) == 1 self.actions = data.readACTIONRECORDs() def __str__(self): return "[SWFButtonCondAction]" class SWFExport(_dumb_repr): def __init__(self, data=None): if not data is None: self.parse(data) def get_dependencies(self): return set([self.characterId]) def parse(self, data): self.characterId = data.readUI16() self.characterName = data.readString() def __str__(self): return "[SWFExport %d as %r]" % (self.characterId, self.characterName)