From b13bd62e5f1b60e7c66fc2afd4920c42b7872df0 Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Sat, 31 Dec 2016 17:06:45 +0100 Subject: [PATCH] #1333 Exporting sprites as swf files --- CHANGELOG.md | 1 + .../src/com/jpexs/decompiler/flash/SWF.java | 45 +- .../com/jpexs/decompiler/flash/SWFHeader.java | 5 + .../decompiler/flash/abc/avm2/AVM2Code.java | 6 +- .../flash/exporters/FrameExporter.java | 64 +- .../flash/exporters/MorphShapeExporter.java | 16 +- .../flash/exporters/PreviewExporter.java | 564 ++++++++++++++++++ .../flash/exporters/ShapeExporter.java | 27 +- .../exporters/modes/ButtonExportMode.java | 4 +- .../exporters/modes/FrameExportMode.java | 4 +- .../exporters/modes/MorphShapeExportMode.java | 15 +- .../exporters/modes/ShapeExportMode.java | 13 +- .../exporters/modes/SpriteExportMode.java | 4 +- .../settings/MorphShapeExportSettings.java | 13 + .../settings/ShapeExportSettings.java | 17 + .../console/CommandLineArgumentParser.java | 14 +- .../jpexs/decompiler/flash/gui/MainPanel.java | 49 +- .../decompiler/flash/gui/PreviewPanel.java | 523 +--------------- .../flash/gui/abc/ASMSourceEditorPane.java | 2 +- .../flash/gui/locales/ExportDialog.properties | 5 + .../decompiler/flash/gui/tagtree/TagTree.java | 10 +- 21 files changed, 784 insertions(+), 617 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e0e04bdd9..5473974b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Added - [#1240] AS search using multiple threads when parallel speedup is enabled - [#1308] Search by all P-code files in AS3 +- [#1333] Exporting sprites as swf files ### Fixed - [#1327] P-code editing: error message and syntax highlighting fixed when instruction name contains upper cased letter diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 700085ec8..50c53bce2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -1490,7 +1490,27 @@ public final class SWF implements SWFContainerItem, Timelined { } } + public static SWFHeader decodeHeader(byte[] headerData) throws IOException { + String signature = new String(headerData, 0, 3, Utf8Helper.charset); + if (!swfSignatures.contains(signature)) { + throw new SwfOpenException("Invalid SWF file, wrong signature."); + } + + int version = headerData[3]; + long fileSize; + try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(headerData, 4, 8), 4, 4)) { + fileSize = sis.readUI32("fileSize"); + } + + SWFHeader header = new SWFHeader(); + header.version = version; + header.fileSize = fileSize; + header.gfx = headerData[1] == 'F' && headerData[2] == 'X'; + return header; + } + private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException { + byte[] hdr = new byte[8]; // SWFheader: signature, version and fileSize @@ -1498,25 +1518,12 @@ public final class SWF implements SWFContainerItem, Timelined { throw new SwfOpenException("SWF header is too short"); } - String signature = new String(hdr, 0, 3, Utf8Helper.charset); - if (!swfSignatures.contains(signature)) { - throw new SwfOpenException("Invalid SWF file, wrong signature."); - } + SWFHeader header = decodeHeader(hdr); + long fileSize = header.fileSize; - int version = hdr[3]; - long fileSize; - try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { - fileSize = sis.readUI32("fileSize"); - } - - SWFHeader header = new SWFHeader(); - header.version = version; - header.fileSize = fileSize; - header.gfx = hdr[1] == 'F' && hdr[2] == 'X'; - - try (SWFOutputStream sos = new SWFOutputStream(os, version)) { + try (SWFOutputStream sos = new SWFOutputStream(os, header.version)) { sos.write(getHeaderBytes(SWFCompression.NONE, header.gfx)); - sos.writeUI8(version); + sos.writeUI8(header.version); sos.writeUI32(fileSize); switch (hdr[0]) { @@ -2980,8 +2987,7 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.setModified(true); timelined.resetTimeline(); } else // timeline should be always the swf here - { - if (removeDependencies) { + if (removeDependencies) { removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); timelined.setModified(true); } else { @@ -2990,7 +2996,6 @@ public final class SWF implements SWFContainerItem, Timelined { timelined.setModified(true); } } - } } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFHeader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFHeader.java index f80296c8a..15526847e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFHeader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFHeader.java @@ -46,4 +46,9 @@ public class SWFHeader { * ScaleForm GFx */ public boolean gfx = false; + + /** + * Frame rate + */ + public float frameRate; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 88aad0683..4abbf75bd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -2173,7 +2173,8 @@ public class AVM2Code implements Cloneable { ins.operands[j] = updater.updateOperandOffset(target, ins.operands[j]); } }*/ //Faster, but not so universal - if (ins.definition instanceof IfTypeIns) { + { + if (ins.definition instanceof IfTypeIns) { long target = ins.getTargetAddress(); try { ins.operands[0] = updater.updateOperandOffset(ins.getAddress(), target, ins.operands[0]); @@ -2181,6 +2182,7 @@ public class AVM2Code implements Cloneable { throw new ConvertException("Invalid offset (" + ins + ")", i); } } + } ins.setAddress(updater.updateInstructionOffset(ins.getAddress())); //Note: changing operands here does not change instruction byte length as offsets are always S24 (not variable length) } @@ -2605,7 +2607,7 @@ public class AVM2Code implements Cloneable { return stats; } - // simplified varions of getStats. This method calculates only the maxlocal value + // simplified version of getStats. This method calculates only the maxlocal value public CodeStats getMaxLocal() { CodeStats stats = new CodeStats(); for (AVM2Instruction ins : code) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java index 5810a690c..68d3fae11 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; @@ -91,7 +92,7 @@ public class FrameExporter { private static final Logger logger = Logger.getLogger(FrameExporter.class.getName()); - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, ButtonExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportButtonFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, ButtonExportSettings settings, EventListener evl) throws IOException, InterruptedException { FrameExportMode fem; switch (settings.mode) { case BMP: @@ -103,15 +104,23 @@ public class FrameExporter { case SVG: fem = FrameExportMode.SVG; break; + case SWF: + fem = FrameExportMode.SWF; + break; default: - throw new Error("Unsupported button export mode"); + throw new Error("Unsupported button export mode: " + settings.mode); + } + + if (frames == null) { + frames = new ArrayList<>(); + frames.add(0); // todo: export all frames } FrameExportSettings fes = new FrameExportSettings(fem, settings.zoom); return exportFrames(handler, outdir, swf, containerId, frames, fes, evl); } - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException { + public List exportSpriteFrames(AbortRetryIgnoreHandler handler, String outdir, SWF swf, int containerId, List frames, SpriteExportSettings settings, EventListener evl) throws IOException, InterruptedException { FrameExportMode fem; switch (settings.mode) { case PNG: @@ -135,6 +144,9 @@ public class FrameExporter { case BMP: fem = FrameExportMode.BMP; break; + case SWF: + fem = FrameExportMode.SWF; + break; default: throw new Error("Unsupported sprite export mode"); } @@ -159,11 +171,11 @@ public class FrameExporter { final Timeline tim = tim0; + boolean exportAll = frames == null; if (frames == null) { - int frameCnt = tim.getFrameCount(); frames = new ArrayList<>(); - for (int i = 0; i < frameCnt; i++) { - frames.add(i); + for (Frame frame : tim.getFrames()) { + frames.add(frame.frame); } } @@ -330,10 +342,44 @@ public class FrameExporter { return ret; } - final Timeline ftim = tim; + if (settings.mode == FrameExportMode.SWF) { + Color fBackgroundColor = backgroundColor; + if (exportAll) { + new RetryTask(() -> { + File f = new File(foutdir + File.separator + "frames.swf"); + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { + try { + new PreviewExporter().exportSwf(fos, swf.getCharacter(containerId), fBackgroundColor, 0); + } catch (ActionParseException ex) { + Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + ret.add(f); + }, handler).run(); + } else { + for (Integer frame : fframes) { + new RetryTask(() -> { + File f = new File(foutdir + File.separator + frame + ".swf"); + Frame fn = (Frame) tim.getFrame(frame); + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(f))) { + try { + new PreviewExporter().exportSwf(fos, fn, fBackgroundColor, 0); + } catch (ActionParseException ex) { + Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + ret.add(f); + }, handler).run(); + } + } + } + final Color fbackgroundColor = backgroundColor; final Iterator frameImages = new Iterator() { - private int pos = 0; @Override @@ -360,7 +406,7 @@ public class FrameExporter { } int fframe = fframes.get(pos++); - BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage(); + BufferedImage result = SWF.frameToImageGet(tim, fframe, fframe, null, 0, tim.displayRect, new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage(); if (evl != null) { evl.handleExportedEvent("frame", pos, fframes.size(), tagName); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index 0d961850f..a2b7f4b42 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; @@ -44,6 +45,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -83,9 +86,8 @@ public class MorphShapeExporter { if (t instanceof CharacterTag) { characterID = ((CharacterTag) t).getCharacterId(); } - String ext = settings.mode == MorphShapeExportMode.CANVAS ? "html" : "svg"; - final File file = new File(outdir + File.separator + characterID + "." + ext); + final File file = new File(outdir + File.separator + characterID + settings.getFileExtension()); new RetryTask(() -> { MorphShapeTag mst = (MorphShapeTag) t; switch (settings.mode) { @@ -115,6 +117,16 @@ public class MorphShapeExporter { SWF.writeLibrary(ct.getSwf(), needed, baos); fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), Utf8Helper.charset), SWF.getTypePrefix(mst) + mst.getCharacterId(), mst.getRect()))); } + break; + case SWF: + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { + try { + new PreviewExporter().exportSwf(fos, mst, null, 0); + } catch (ActionParseException ex) { + Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + break; } }, handler).run(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java new file mode 100644 index 000000000..a06978760 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.exporters; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFHeader; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import java.awt.Color; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class PreviewExporter { + + // play morph shape in 2 second(s) + public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; + + public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; + + public SWFHeader exportSwf(OutputStream os, TreeItem treeItem, Color backgroundColor, int fontPageNum) throws IOException, ActionParseException { + SWF swf = treeItem.getSwf(); + + int frameCount = 1; + float frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (treeItem instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem; + SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (treeItem instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { + frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = (int) (MORPH_SHAPE_ANIMATION_LENGTH * frameRate); + } + + if (treeItem instanceof DefineSoundTag) { + frameCount = 1; + } + + if (treeItem instanceof DefineSpriteTag) { + frameCount = ((DefineSpriteTag) treeItem).frameCount; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); + RECT outrect = new RECT(swf.displayRect); + + RECT treeItemBounds = null; + if (treeItem instanceof FontTag) { + outrect.Xmin = 0; + outrect.Ymin = 0; + outrect.Xmax = FontTag.PREVIEWSIZE * 20; + outrect.Ymax = FontTag.PREVIEWSIZE * 20; + } else if (treeItem instanceof BoundedTag) { + treeItemBounds = ((BoundedTag) treeItem).getRect(); + } else if (treeItem instanceof Frame) { + treeItemBounds = ((Frame) treeItem).timeline.timelined.getRect(); + } + + if (treeItemBounds != null) { + if (outrect.getWidth() < treeItemBounds.getWidth()) { + outrect.Xmax += treeItemBounds.getWidth() - outrect.getWidth(); + } + + if (outrect.getHeight() < treeItemBounds.getHeight()) { + outrect.Ymax += treeItemBounds.getHeight() - outrect.getHeight(); + } + } + + int width = outrect.getWidth(); + int height = outrect.getHeight(); + + sos2.writeRECT(outrect); + sos2.writeFIXED8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + FileAttributesTag fa = swf.getFileAttributes(); + if (fa != null) { + fa.writeTag(sos2); + } + + SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + if (setBgColorTag == null && backgroundColor != null) { + setBgColorTag = new SetBackgroundColorTag(swf, new RGB(backgroundColor)); + } + + if (setBgColorTag != null) { + setBgColorTag.writeTag(sos2); + } + + if (treeItem instanceof Frame) { + Frame fn = (Frame) treeItem; + Timelined parent = fn.timeline.timelined; + + Set doneCharacters = new HashSet<>(); + for (Tag t : parent.getTags()) { + if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { + continue; + } + + if (t instanceof DoActionTag || t instanceof DoInitActionTag) { + // todo: Maybe DoABC tags should be removed, too + continue; + } + + Set needed = new HashSet<>(); + t.getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + writeTag(swf.getCharacter(n), sos2); + doneCharacters.add(n); + } + } + + //if (t instanceof ShowFrameTag || t instanceof PlaceObjectTypeTag || t instanceof RemoveTag) { + // continue; + //} + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + doneCharacters.add(characterId); + writeTag(t, sos2); + } + } + + RECT r = parent.getRect(); + for (Map.Entry value : fn.layers.entrySet()) { + PlaceObjectTypeTag pot = value.getValue().toPlaceObjectTag(value.getKey()); + MATRIX mat = new MATRIX(pot.getMatrix()); + mat.translateX += width / 2 - r.getWidth() / 2; + mat.translateY += height / 2 - r.getHeight() / 2; + pot.setMatrix(mat); + pot.writeTag(sos2); + } + + new ShowFrameTag(swf).writeTag(sos2); + } else { + boolean isSprite = false; + if (treeItem instanceof DefineSpriteTag) { + isSprite = true; + } + int chtId = -1; + if (treeItem instanceof CharacterTag) { + chtId = ((CharacterTag) treeItem).getCharacterId(); + } + + if (treeItem instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.getJtt(); + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (treeItem instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) treeItem).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); + } + } + + writeTag(characterTag, sos2); + } + } + + writeTag((Tag) treeItem, sos2); + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (treeItem instanceof BoundedTag) { + RECT r = ((BoundedTag) treeItem).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 (treeItem instanceof FontTag) { + FontTag ft = (FontTag) classicTag((Tag) treeItem); + + 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 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 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, 1 + f, 999 + f, tmat, null, 0, null, -1, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, true, 1, chtId, mat, null, ratio, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (treeItem instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (treeItem instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) treeItem; + ea.tags.add(ds.soundId); + ea.names.add("my_define_sound"); + ea.writeTag(sos2); + List 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 (treeItem instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, -1, null, -1, null).writeTag(sos2); + List frs = new ArrayList<>(videoFrames.values()); + Collections.sort(frs, new Comparator() { + @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, true, 1, -1, null, null, ratio, null, -1, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else if (treeItem instanceof DefineSpriteTag) { + DefineSpriteTag s = (DefineSpriteTag) treeItem; + Tag lastTag = null; + for (Tag t : s.getTags()) { + 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.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { + new ShowFrameTag(swf).writeTag(sos2); + } + } else { + new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + + } // not showframe + + new EndTag(swf).writeTag(sos2); + data = baos.toByteArray(); + } + + SWFHeader result = new SWFHeader(); + result.version = Math.max(10, swf.version); + result.frameRate = frameRate; + + SWFOutputStream sos = new SWFOutputStream(os, result.version); + sos.write("FWS".getBytes()); + sos.write(swf.version); + result.fileSize = sos.getPos() + data.length + 4; + sos.writeUI32(result.fileSize); + sos.write(data); + os.flush(); + + return result; + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + private static void writeTag(Tag t, SWFOutputStream sos) throws IOException { + t = classicTag(t); + + t.writeTag(sos); + if (t instanceof CharacterIdTag) { + List chIdTags = t.getSwf().getCharacterIdTags(((CharacterIdTag) t).getCharacterId()); + if (chIdTags != null) { + for (CharacterIdTag chIdTag : chIdTags) { + if (!(chIdTag instanceof PlaceObjectTypeTag || chIdTag instanceof RemoveTag)) { + ((Tag) chIdTag).writeTag(sos); + } + } + } + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index 277ff7f0d..7408a6652 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.RetryTask; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; @@ -50,6 +51,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -85,19 +88,7 @@ public class ShapeExporter { evl.handleExportingEvent("shape", currentIndex, count, t.getName()); } - int characterID = st.getCharacterId(); - String ext = ".svg"; - if (settings.mode == ShapeExportMode.PNG) { - ext = ".png"; - } - if (settings.mode == ShapeExportMode.BMP) { - ext = ".bmp"; - } - if (settings.mode == ShapeExportMode.CANVAS) { - ext = ".html"; - } - - final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + ext)); + final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName() + settings.getFileExtension())); new RetryTask(() -> { switch (settings.mode) { case SVG: @@ -142,6 +133,16 @@ public class ShapeExporter { SWF.writeLibrary(st.getSwf(), needed, baos); fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), Utf8Helper.charset), SWF.getTypePrefix(st) + st.getCharacterId(), st.getRect()))); } + break; + case SWF: + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(file))) { + try { + new PreviewExporter().exportSwf(fos, st, null, 0); + } catch (ActionParseException ex) { + Logger.getLogger(MorphShapeExporter.class.getName()).log(Level.SEVERE, null, ex); + } + } + break; } }, handler).run(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java index 2529db29c..de49df0e2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ButtonExportMode.java @@ -21,8 +21,8 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum ButtonExportMode { - PNG, SVG, - BMP + BMP, + SWF, } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java index 0ec83a415..5e183fbef 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/FrameExportMode.java @@ -21,12 +21,12 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum FrameExportMode { - PNG, GIF, AVI, SVG, CANVAS, PDF, - BMP + BMP, + SWF, } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java index a7680c7f9..4e35744a2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/MorphShapeExportMode.java @@ -1,18 +1,19 @@ /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.exporters.modes; /** @@ -20,11 +21,11 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum MorphShapeExportMode { - //TODO: implement other morphshape export modes SVG, - CANVAS + CANVAS, //PNG, //GIF, - //AVI + //AVI, + SWF, } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java index af8c767c7..7995d0b73 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java @@ -1,18 +1,19 @@ /* * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.exporters.modes; /** @@ -20,9 +21,9 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum ShapeExportMode { - SVG, PNG, CANVAS, - BMP + BMP, + SWF, } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java index 583d8979d..d342f4bc5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/SpriteExportMode.java @@ -21,12 +21,12 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum SpriteExportMode { - PNG, GIF, AVI, SVG, CANVAS, PDF, - BMP + BMP, + SWF, } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java index bdad9f8cd..b08cb4c50 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/MorphShapeExportSettings.java @@ -34,4 +34,17 @@ public class MorphShapeExportSettings { this.mode = mode; this.zoom = zoom; } + + public String getFileExtension() { + switch (mode) { + case SVG: + return ".svg"; + case CANVAS: + return ".html"; + case SWF: + return ".swf"; + default: + throw new Error("Unsupported morphshape export mode: " + mode); + } + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ShapeExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ShapeExportSettings.java index bc8ae7d25..f4cd798a3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ShapeExportSettings.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/ShapeExportSettings.java @@ -34,4 +34,21 @@ public class ShapeExportSettings { this.mode = mode; this.zoom = zoom; } + + public String getFileExtension() { + switch (mode) { + case SVG: + return ".svg"; + case PNG: + return ".png"; + case BMP: + return ".bmp"; + case CANVAS: + return ".html"; + case SWF: + return ".swf"; + default: + throw new Error("Unsupported morphshape export mode: " + mode); + } + } } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 629d0cb4c..547adff73 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -218,6 +218,7 @@ public class CommandLineArgumentParser { private static String stdErr = null; private static final String METADATA_FORMAT_JSLIKE = "jslike"; + private static final String METADATA_FORMAT_RAW = "raw"; @SuppressWarnings("unchecked") @@ -1238,7 +1239,6 @@ public class CommandLineArgumentParser { } return false; } - }); System.exit(0); } @@ -1449,7 +1449,6 @@ public class CommandLineArgumentParser { } return false; } - }); System.exit(0); } @@ -1579,7 +1578,6 @@ public class CommandLineArgumentParser { } return false; } - }); System.exit(0); @@ -2221,7 +2219,7 @@ public class CommandLineArgumentParser { SpriteExportSettings ses = new SpriteExportSettings(enumFromStr(formats.get("sprite"), SpriteExportMode.class), zoom); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, c.getCharacterId(), null, ses, evl); + frameExporter.exportSpriteFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, c.getCharacterId(), null, ses, evl); } } } @@ -2231,9 +2229,7 @@ public class CommandLineArgumentParser { ButtonExportSettings bes = new ButtonExportSettings(enumFromStr(formats.get("button"), ButtonExportMode.class), zoom); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + ButtonExportSettings.EXPORT_FOLDER_NAME : ""), swf, c.getCharacterId(), frameNums, bes, evl); + frameExporter.exportButtonFrames(handler, outDir + (multipleExportTypes ? File.separator + ButtonExportSettings.EXPORT_FOLDER_NAME : ""), swf, c.getCharacterId(), null, bes, evl); } } } @@ -2660,7 +2656,6 @@ public class CommandLineArgumentParser { try { new SearchInMemory(new SearchInMemoryListener() { - @Override public void publish(Object... chunks) { for (Object s : chunks) { @@ -2872,7 +2867,6 @@ public class CommandLineArgumentParser { } else if (characterTag instanceof TextTag) { TextTag textTag = (TextTag) characterTag; new TextImporter(new MissingCharacterHandler(), new TextImportErrorHandler() { - @Override public boolean handle(TextTag textTag) { String msg = "Error during text import."; @@ -3540,7 +3534,6 @@ public class CommandLineArgumentParser { public boolean missingFloat4(Float4 value) { return true; } - }, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info)); //acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes()); abc.bodies.get(bodyIndex).setCode(acode); @@ -4033,5 +4026,4 @@ public class CommandLineArgumentParser { } } } - } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index fdddb5bf5..9cab00508 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -42,6 +42,7 @@ import com.jpexs.decompiler.flash.exporters.FrameExporter; import com.jpexs.decompiler.flash.exporters.ImageExporter; import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; import com.jpexs.decompiler.flash.exporters.MovieExporter; +import com.jpexs.decompiler.flash.exporters.PreviewExporter; import com.jpexs.decompiler.flash.exporters.ShapeExporter; import com.jpexs.decompiler.flash.exporters.SoundExporter; import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; @@ -328,11 +329,6 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public TreeItem oldItem; - // play morph shape in 2 second(s) - public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; - - public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; - private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); public void setPercent(int percent) { @@ -1121,7 +1117,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { List ret = new ArrayList<>(); - List sel = folderPreviewPanel.selectedItems.isEmpty() ? tagTree.getAllSelected() : new ArrayList<>(folderPreviewPanel.selectedItems.values()); + List sel = tagTree.getSelection(getCurrentSwf()); Set usedSwfs = new HashSet<>(); for (TreeItem d : sel) { @@ -1137,6 +1133,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se List images = new ArrayList<>(); List shapes = new ArrayList<>(); List morphshapes = new ArrayList<>(); + List sprites = new ArrayList<>(); List buttons = new ArrayList<>(); List movies = new ArrayList<>(); List sounds = new ArrayList<>(); @@ -1175,6 +1172,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (nodeType == TreeNodeType.MORPH_SHAPE) { morphshapes.add((Tag) d); } + if (nodeType == TreeNodeType.SPRITE) { + sprites.add((Tag) d); + } if (nodeType == TreeNodeType.AS) { as12scripts.add(d); } @@ -1211,13 +1211,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (!frames.containsKey(parentId)) { frames.put(parentId, new ArrayList<>()); } + frames.get(parentId).add(frame); } + if (d instanceof ScriptPack) { as3scripts.add((ScriptPack) d); } } + for (Tag sprite : sprites) { + frames.put(((DefineSpriteTag) sprite).getCharacterId(), null); + } + String selFile2; if (usedSwfs.size() > 1) { selFile2 = selFile + File.separator + Helper.getNextId(swf.getShortFileName(), usedSwfsIds); @@ -1275,12 +1281,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (export.isOptionEnabled(FrameExportMode.class)) { FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); - for (Entry> entry : frames.entrySet()) { - int containerId = entry.getKey(); - if (containerId == 0) { - String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), fes, evl)); - } + if (frames.containsKey(0)) { + String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; + ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, 0, frames.get(0), fes, evl)); } } @@ -1290,7 +1293,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se int containerId = entry.getKey(); if (containerId != 0) { String subFolder = SpriteExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); + ret.addAll(frameExporter.exportSpriteFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); } } } @@ -1300,9 +1303,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se for (Tag tag : buttons) { ButtonTag button = (ButtonTag) tag; String subFolder = ButtonExportSettings.EXPORT_FOLDER_NAME; - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, button.getCharacterId(), frameNums, bes, evl)); + ret.addAll(frameExporter.exportButtonFrames(handler, selFile2 + File.separator + subFolder, swf, button.getCharacterId(), null, bes, evl)); } } @@ -1399,7 +1400,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); + frameExporter.exportSpriteFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); } } } @@ -1408,9 +1409,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), frameNums, bes, evl); + frameExporter.exportButtonFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, bes, evl); } } } @@ -1513,7 +1512,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se SpriteExportSettings ses = new SpriteExportSettings(exportMode, export.getZoom()); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); + frameExporter.exportSpriteFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); } } } @@ -1524,9 +1523,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); for (CharacterTag c : swf.getCharacters().values()) { if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); + frameExporter.exportButtonFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, bes, evl); } } } @@ -3618,8 +3615,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se private void initTimeline(Timeline timeline) { if (tag instanceof MorphShapeTag) { - timeline.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; - int framesCnt = (int) (timeline.frameRate * MORPH_SHAPE_ANIMATION_LENGTH); + timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE; + int framesCnt = (int) (timeline.frameRate * PreviewExporter.MORPH_SHAPE_ANIMATION_LENGTH); for (int i = 0; i < framesCnt; i++) { Frame f = new Frame(timeline, i); DepthState ds = new DepthState(tag.getSwf(), f); diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index b87786298..d6d06fe34 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -17,12 +17,10 @@ package com.jpexs.decompiler.flash.gui; import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.SWFHeader; import com.jpexs.decompiler.flash.action.parser.ActionParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.PreviewExporter; import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; @@ -30,47 +28,15 @@ import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; import com.jpexs.decompiler.flash.gui.player.MediaDisplay; import com.jpexs.decompiler.flash.gui.player.PlayerControls; import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.TagScript; import com.jpexs.decompiler.flash.timeline.Timelined; import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; import com.jpexs.helpers.SerializableImage; import java.awt.BorderLayout; @@ -81,7 +47,6 @@ import java.awt.Insets; import java.awt.event.ActionEvent; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -90,14 +55,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JButton; @@ -620,31 +577,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel nextFontsButton.setVisible(false); } - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - private static void writeTag(Tag t, SWFOutputStream sos) throws IOException { - t = classicTag(t); - - t.writeTag(sos); - if (t instanceof CharacterIdTag) { - List chIdTags = t.getSwf().getCharacterIdTags(((CharacterIdTag) t).getCharacterId()); - if (chIdTags != null) { - for (CharacterIdTag chIdTag : chIdTags) { - if (!(chIdTag instanceof PlaceObjectTypeTag || chIdTag instanceof RemoveTag)) { - ((Tag) chIdTag).writeTag(sos); - } - } - } - } - } - public void createAndShowTempSwf(TreeItem treeItem) { - SWF swf = null; try { if (tempFile != null) { tempFile.delete(); @@ -657,465 +590,29 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel if (treeItem instanceof Tag) { Tag tag = (Tag) treeItem; - swf = tag.getSwf(); if (tag instanceof FontTag) { //Fonts are always black on white backgroundColor = View.getDefaultBackgroundColor(); } } else if (treeItem instanceof Frame) { Frame fn = (Frame) treeItem; - swf = fn.getSwf(); - if (fn.timeline.timelined == swf) { - SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); + SWF sourceSwf = fn.getSwf(); + if (fn.timeline.timelined == sourceSwf) { + SetBackgroundColorTag setBgColorTag = sourceSwf.getBackgroundColor(); if (setBgColorTag != null) { backgroundColor = setBgColorTag.backgroundColor.toColor(); } } } - int frameCount = 1; - float frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (treeItem instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) treeItem; - SWF.populateVideoFrames(vs.getCharacterId(), swf.getTags(), videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (treeItem instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) treeItem).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = (int) (MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate); - } - - if (treeItem instanceof DefineSoundTag) { - frameCount = 1; - } - - if (treeItem instanceof DefineSpriteTag) { - frameCount = ((DefineSpriteTag) treeItem).frameCount; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, SWF.DEFAULT_VERSION); - RECT outrect = new RECT(swf.displayRect); - - RECT treeItemBounds = null; - if (treeItem instanceof FontTag) { - outrect.Xmin = 0; - outrect.Ymin = 0; - outrect.Xmax = FontTag.PREVIEWSIZE * 20; - outrect.Ymax = FontTag.PREVIEWSIZE * 20; - } else if (treeItem instanceof BoundedTag) { - treeItemBounds = ((BoundedTag) treeItem).getRect(); - } else if (treeItem instanceof Frame) { - treeItemBounds = ((Frame) treeItem).timeline.timelined.getRect(); - } - - if (treeItemBounds != null) { - if (outrect.getWidth() < treeItemBounds.getWidth()) { - outrect.Xmax += treeItemBounds.getWidth() - outrect.getWidth(); - } - - if (outrect.getHeight() < treeItemBounds.getHeight()) { - outrect.Ymax += treeItemBounds.getHeight() - outrect.getHeight(); - } - } - - int width = outrect.getWidth(); - int height = outrect.getHeight(); - - sos2.writeRECT(outrect); - sos2.writeFIXED8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - FileAttributesTag fa = swf.getFileAttributes(); - if (fa != null) { - fa.writeTag(sos2); - } - - SetBackgroundColorTag setBgColorTag = swf.getBackgroundColor(); - if (setBgColorTag == null) { - setBgColorTag = new SetBackgroundColorTag(swf, new RGB(backgroundColor)); - } - - setBgColorTag.writeTag(sos2); - - if (treeItem instanceof Frame) { - Frame fn = (Frame) treeItem; - Timelined parent = fn.timeline.timelined; - - Set doneCharacters = new HashSet<>(); - for (Tag t : parent.getTags()) { - if (t instanceof FileAttributesTag || t instanceof SetBackgroundColorTag) { - continue; - } - - if (t instanceof DoActionTag || t instanceof DoInitActionTag) { - // todo: Maybe DoABC tags should be removed, too - continue; - } - - Set needed = new HashSet<>(); - t.getNeededCharactersDeep(needed); - for (int n : needed) { - if (!doneCharacters.contains(n)) { - writeTag(swf.getCharacter(n), sos2); - doneCharacters.add(n); - } - } - - //if (t instanceof ShowFrameTag || t instanceof PlaceObjectTypeTag || t instanceof RemoveTag) { - // continue; - //} - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - doneCharacters.add(characterId); - writeTag(t, sos2); - } - } - - RECT r = parent.getRect(); - for (Map.Entry value : fn.layers.entrySet()) { - PlaceObjectTypeTag pot = value.getValue().toPlaceObjectTag(value.getKey()); - MATRIX mat = new MATRIX(pot.getMatrix()); - mat.translateX += width / 2 - r.getWidth() / 2; - mat.translateY += height / 2 - r.getHeight() / 2; - pot.setMatrix(mat); - pot.writeTag(sos2); - } - - new ShowFrameTag(swf).writeTag(sos2); - } else { - boolean isSprite = false; - if (treeItem instanceof DefineSpriteTag) { - isSprite = true; - } - int chtId = -1; - if (treeItem instanceof CharacterTag) { - chtId = ((CharacterTag) treeItem).getCharacterId(); - } - - if (treeItem instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.getJtt(); - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (treeItem instanceof AloneTag) { - } else { - Set needed = new HashSet<>(); - ((Tag) treeItem).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); - } - } - - writeTag(characterTag, sos2); - } - } - - writeTag((Tag) treeItem, sos2); - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (treeItem instanceof BoundedTag) { - RECT r = ((BoundedTag) treeItem).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 (treeItem instanceof FontTag) { - FontTag ft = (FontTag) classicTag((Tag) treeItem); - - 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 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 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, 1 + f, 999 + f, tmat, null, 0, null, -1, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((treeItem instanceof DefineMorphShapeTag) || (treeItem instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, true, 1, chtId, mat, null, ratio, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (treeItem instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (treeItem instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) treeItem; - ea.tags.add(ds.soundId); - ea.names.add("my_define_sound"); - ea.writeTag(sos2); - List 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 (treeItem instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, -1, null, -1, null).writeTag(sos2); - List frs = new ArrayList<>(videoFrames.values()); - Collections.sort(frs, new Comparator() { - @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, true, 1, -1, null, null, ratio, null, -1, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else if (treeItem instanceof DefineSpriteTag) { - DefineSpriteTag s = (DefineSpriteTag) treeItem; - Tag lastTag = null; - for (Tag t : s.getTags()) { - 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.getTags().isEmpty() && (lastTag != null) && (!(lastTag instanceof ShowFrameTag))) { - new ShowFrameTag(swf).writeTag(sos2); - } - } else { - new PlaceObject2Tag(swf, false, 1, chtId, mat, null, 0, null, -1, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - - } // not showframe - - new EndTag(swf).writeTag(sos2); - data = baos.toByteArray(); - } - + SWFHeader header; 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(); + header = new PreviewExporter().exportSwf(fos, treeItem, backgroundColor, fontPageNum); } + if (flashPanel != null) { - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, header.frameRate); } + showFlashViewerPanel(); } catch (IOException | ActionParseException ex) { Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, null, ex); diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java index 1a21c08e4..ca8c81011 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java @@ -429,7 +429,7 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi int caretPos = getCaretPosition(); Flasm3Lexer lexer = new Flasm3Lexer(new StringReader(getText().replace("\r\n", "\n"))); ParsedSymbol symb; - String lastLevel = null; + String lastLevel; final Integer singleUse[] = new Integer[]{ ParsedSymbol.TYPE_KEYWORD_FINAL, ParsedSymbol.TYPE_KEYWORD_OVERRIDE, diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 61e11cdec..cda0f3caa 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -17,6 +17,7 @@ shapes.svg = SVG shapes.png = PNG shapes.bmp = BMP shapes.canvas = HTML5 Canvas +shapes.swf = SWF texts = Texts texts.plain = Plain text @@ -58,6 +59,7 @@ morphshapes = Morphshapes morphshapes.gif = GIF morphshapes.svg = SVG morphshapes.canvas = HTML5 Canvas +morphshapes.swf = SWF frames = Frames frames.png = PNG @@ -67,6 +69,7 @@ frames.svg = SVG frames.canvas = HTML5 Canvas frames.pdf = PDF frames.bmp = BMP +frames.swf = SWF sprites = Sprites sprites.png = PNG @@ -76,11 +79,13 @@ sprites.svg = SVG sprites.canvas = HTML5 Canvas sprites.pdf = PDF sprites.bmp = BMP +sprites.swf = SWF buttons = Buttons buttons.png = PNG buttons.svg = SVG buttons.bmp = BMP +buttons.swf = SWF fonts = Fonts fonts.ttf = TTF diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index 9ba56f74f..ce982d077 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -537,7 +537,12 @@ public class TagTree extends JTree { if (mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { sel = getAllSelected(); } else { - sel = new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); + sel = new ArrayList<>(); + + for (TreeItem treeItem : mainPanel.folderPreviewPanel.selectedItems.values()) { + sel.add(treeItem); + getAllSubs(treeItem, sel); + } } return getSelection(swf, sel); } @@ -570,6 +575,9 @@ public class TagTree extends JTree { if (nodeType == TreeNodeType.MORPH_SHAPE) { ret.add(d); } + if (nodeType == TreeNodeType.SPRITE) { + ret.add(d); + } if (nodeType == TreeNodeType.BUTTON) { ret.add(d); }