public static int getFontPageCount(FontTag fontTag) { int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; if (pageCount < 1) { pageCount = 1; } return pageCount; }
public void createAndShowTempSwf(TreeItem tagObj) { SWF swf; try { if (tempFile != null) { tempFile.delete(); } tempFile = File.createTempFile("ffdec_view_", ".swf"); tempFile.deleteOnExit(); Color backgroundColor = View.getSwfBackgroundColor(); if (tagObj instanceof FontTag) { // Fonts are always black on white backgroundColor = View.getDefaultBackgroundColor(); } if (tagObj instanceof Frame) { Frame fn = (Frame) tagObj; swf = fn.getSwf(); if (fn.timeline.timelined == swf) { for (Tag t : swf.tags) { if (t instanceof SetBackgroundColorTag) { backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); break; } } } } else { Tag tag = (Tag) tagObj; swf = tag.getSwf(); } int frameCount = 1; float frameRate = swf.frameRate; HashMap<Integer, VideoFrameTag> videoFrames = new HashMap<>(); if (tagObj instanceof DefineVideoStreamTag) { DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); frameCount = videoFrames.size(); } List<SoundStreamBlockTag> soundFrames = new ArrayList<>(); if (tagObj instanceof SoundStreamHeadTypeTag) { soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); frameCount = soundFrames.size(); } if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; frameCount = (int) (MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate); } if (tagObj instanceof DefineSoundTag) { frameCount = 1; } if (tagObj instanceof DefineSpriteTag) { frameCount = ((DefineSpriteTag) tagObj).frameCount; } byte[] data; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); RECT outrect = new RECT(swf.displayRect); if (tagObj instanceof FontTag) { outrect.Xmin = 0; outrect.Ymin = 0; outrect.Xmax = FontTag.PREVIEWSIZE * 20; outrect.Ymax = FontTag.PREVIEWSIZE * 20; } int width = outrect.getWidth(); int height = outrect.getHeight(); sos2.writeRECT(outrect); sos2.writeFIXED8(frameRate); sos2.writeUI16(frameCount); // framecnt /*FileAttributesTag fa = new FileAttributesTag(); sos2.writeTag(fa); */ new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); if (tagObj instanceof Frame) { Frame fn = (Frame) tagObj; Timelined parent = fn.timeline.timelined; List<Tag> subs = fn.timeline.tags; List<Integer> doneCharacters = new ArrayList<>(); int frameCnt = 0; for (Tag t : subs) { if (t instanceof ShowFrameTag) { frameCnt++; continue; } if (frameCnt > fn.frame) { break; } if (t instanceof DoActionTag || t instanceof DoInitActionTag) { // todo: Maybe DoABC tags should be removed, too continue; } Set<Integer> needed = new HashSet<>(); t.getNeededCharactersDeep(needed); for (int n : needed) { if (!doneCharacters.contains(n)) { classicTag(swf.getCharacter(n)).writeTag(sos2); doneCharacters.add(n); } } if (t instanceof CharacterTag) { int characterId = ((CharacterTag) t).getCharacterId(); if (!doneCharacters.contains(characterId)) { doneCharacters.add(((CharacterTag) t).getCharacterId()); } } classicTag(t).writeTag(sos2); if (parent != null) { if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; int chid = pot.getCharacterId(); int depth = pot.getDepth(); MATRIX mat = pot.getMatrix(); if (mat == null) { mat = new MATRIX(); } mat = Helper.deepCopy(mat); if (parent instanceof BoundedTag) { RECT r = ((BoundedTag) parent).getRect(); mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; } else { mat.translateX += width / 2; mat.translateY += height / 2; } new PlaceObject2Tag( swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null) .writeTag(sos2); } } } new ShowFrameTag(swf).writeTag(sos2); } else { boolean isSprite = false; if (tagObj instanceof DefineSpriteTag) { isSprite = true; } int chtId = 0; if (tagObj instanceof CharacterTag) { chtId = ((CharacterTag) tagObj).getCharacterId(); } if (tagObj instanceof DefineBitsTag) { JPEGTablesTag jtt = swf.getJtt(); if (jtt != null) { jtt.writeTag(sos2); } } else if (tagObj instanceof AloneTag) { } else { Set<Integer> needed = new HashSet<>(); ((Tag) tagObj).getNeededCharactersDeep(needed); for (int n : needed) { if (isSprite && chtId == n) { continue; } CharacterTag characterTag = swf.getCharacter(n); if (characterTag instanceof DefineBitsTag) { JPEGTablesTag jtt = swf.getJtt(); if (jtt != null) { jtt.writeTag(sos2); } } classicTag(characterTag).writeTag(sos2); } } classicTag((Tag) tagObj).writeTag(sos2); MATRIX mat = new MATRIX(); mat.hasRotate = false; mat.hasScale = false; mat.translateX = 0; mat.translateY = 0; if (tagObj instanceof BoundedTag) { RECT r = ((BoundedTag) tagObj).getRect(); mat.translateX = -r.Xmin; mat.translateY = -r.Ymin; mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; } else { mat.translateX = width / 4; mat.translateY = height / 4; } if (tagObj instanceof FontTag) { FontTag ft = (FontTag) classicTag((Tag) tagObj); int countGlyphsTotal = ft.getGlyphShapeTable().size(); int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); int fontId = ft.getFontId(); int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); if (rows == 0) { rows = 1; cols = 1; } int x = 0; int y = 0; int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; countGlyphs = Math.min( SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); List<SHAPE> shapes = ft.getGlyphShapeTable(); int maxw = 0; for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { RECT b = shapes.get(f).getBounds(); if (b.Xmin == Integer.MAX_VALUE) { continue; } if (b.Ymin == Integer.MAX_VALUE) { continue; } int w = (int) (b.getWidth() / ft.getDivider()); if (w > maxw) { maxw = w; } x++; } x = 0; int BORDER = 3 * 20; int textHeight = height / rows; while (maxw * textHeight / 1024.0 > width / cols - 2 * BORDER) { textHeight--; } MATRIX tmat = new MATRIX(); for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { if (x >= cols) { x = 0; y++; } List<TEXTRECORD> rec = new ArrayList<>(); TEXTRECORD tr = new TEXTRECORD(); RECT b = shapes.get(f).getBounds(); int xmin = b.Xmin == Integer.MAX_VALUE ? 0 : (int) (b.Xmin / ft.getDivider()); xmin *= textHeight / 1024.0; int ymin = b.Ymin == Integer.MAX_VALUE ? 0 : (int) (b.Ymin / ft.getDivider()); ymin *= textHeight / 1024.0; int w = (int) (b.getWidth() / ft.getDivider()); w *= textHeight / 1024.0; int h = (int) (b.getHeight() / ft.getDivider()); h *= textHeight / 1024.0; tr.fontId = fontId; tr.styleFlagsHasFont = true; tr.textHeight = textHeight; tr.xOffset = -xmin; tr.yOffset = 0; tr.styleFlagsHasXOffset = true; tr.styleFlagsHasYOffset = true; tr.glyphEntries = new ArrayList<>(1); tr.styleFlagsHasColor = true; tr.textColor = new RGB(0, 0, 0); GLYPHENTRY ge = new GLYPHENTRY(); double ga = ft.getGlyphAdvance(f); int cw = ga == -1 ? w : (int) (ga / ft.getDivider() * textHeight / 1024.0); ge.glyphAdvance = 0; ge.glyphIndex = f; tr.glyphEntries.add(ge); rec.add(tr); tmat.translateX = x * width / cols + width / cols / 2 - w / 2; tmat.translateY = y * height / rows + height / rows / 2; new DefineTextTag(swf, 999 + f, new RECT(0, cw, ymin, ymin + h), new MATRIX(), rec) .writeTag(sos2); new PlaceObject2Tag( swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, tmat, null, 0, null, 0, null) .writeTag(sos2); x++; } new ShowFrameTag(swf).writeTag(sos2); } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { new PlaceObject2Tag( swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null) .writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { new PlaceObject2Tag( swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null) .writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); } } else if (tagObj instanceof SoundStreamHeadTypeTag) { for (SoundStreamBlockTag blk : soundFrames) { blk.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); } } else if (tagObj instanceof DefineSoundTag) { ExportAssetsTag ea = new ExportAssetsTag(swf); DefineSoundTag ds = (DefineSoundTag) tagObj; ea.tags.add(ds.soundId); ea.names.add("my_define_sound"); ea.writeTag(sos2); List<Action> actions; DoActionTag doa; doa = new DoActionTag(swf, null); actions = ASMParser.parse( 0, false, "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" + "Push \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\" 0.0 \"Sound\"\n" + "NewObject\n" + "SetMember\n" + "Push \"my_define_sound\" 1 \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"attachSound\"\n" + "CallMethod\n" + "Pop\n" + "Stop", swf.version, false); doa.setActions(actions); doa.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); actions = ASMParser.parse( 0, false, "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" + "StopSounds\n" + "Push \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\" 0.0 \"Sound\"\n" + "NewObject\n" + "SetMember\n" + "Push \"my_define_sound\" 1 \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"attachSound\"\n" + "CallMethod\n" + "Pop\n" + "Push 9999 0.0 2 \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"start\"\n" + "CallMethod\n" + "Pop\n" + "Stop", swf.version, false); doa.setActions(actions); doa.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); actions = ASMParser.parse( 0, false, "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" + "StopSounds\n" + "Push \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\" 0.0 \"Sound\"\n" + "NewObject\n" + "SetMember\n" + "Push \"my_define_sound\" 1 \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"attachSound\"\n" + "CallMethod\n" + "Pop\n" + "Push \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"onSoundComplete\"\n" + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" + "Push 0.0 register1 \"my_sound\"\n" + "GetMember\n" + "Push \"start\"\n" + "CallMethod\n" + "Pop\n" + "}\n" + "SetMember\n" + "Push \"_root\"\n" + "GetVariable\n" + "Push \"execParam\"\n" + "GetMember\n" + "Push 1 \"_root\"\n" + "GetVariable\n" + "Push \"my_sound\"\n" + "GetMember\n" + "Push \"start\"\n" + "CallMethod\n" + "Pop\n" + "Stop", swf.version, false); doa.setActions(actions); doa.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); actions = ASMParser.parse(0, false, "StopSounds\n" + "Stop", swf.version, false); doa.setActions(actions); doa.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); } else if (tagObj instanceof DefineVideoStreamTag) { new PlaceObject2Tag( swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null) .writeTag(sos2); List<VideoFrameTag> frs = new ArrayList<>(videoFrames.values()); Collections.sort( frs, new Comparator<VideoFrameTag>() { @Override public int compare(VideoFrameTag o1, VideoFrameTag o2) { return o1.frameNum - o2.frameNum; } }); boolean first = true; int ratio = 0; for (VideoFrameTag f : frs) { if (!first) { ratio++; new PlaceObject2Tag( swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null) .writeTag(sos2); } f.writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); first = false; } } else if (tagObj instanceof DefineSpriteTag) { DefineSpriteTag s = (DefineSpriteTag) tagObj; Tag lastTag = null; for (Tag t : s.subTags) { if (t instanceof EndTag) { break; } else if (t instanceof PlaceObjectTypeTag) { PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t; MATRIX m = pt.getMatrix(); MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); pt.writeTagWithMatrix(sos2, m2); lastTag = t; } else { t.writeTag(sos2); lastTag = t; } } if (!s.subTags.isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { new ShowFrameTag(swf).writeTag(sos2); } } else { new PlaceObject2Tag( swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null) .writeTag(sos2); new ShowFrameTag(swf).writeTag(sos2); } } // not showframe new EndTag(swf).writeTag(sos2); data = baos.toByteArray(); } try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); sos.write("FWS".getBytes()); sos.write(swf.version); sos.writeUI32(sos.getPos() + data.length + 4); sos.write(data); fos.flush(); } if (flashPanel != null) { flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); } showFlashViewerPanel(); } catch (IOException | ActionParseException ex) { Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); } }