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 c4dd73330..38f5246eb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -1071,13 +1071,13 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { @Override public void saveTo(OutputStream os) throws IOException { checkCharset(); - byte[] uncompressedData = saveToByteArray(); + byte[] uncompressedData = saveToByteArray(false); compress(new ByteArrayInputStream(uncompressedData), os, compression, lzmaProperties); } - public void saveTo(OutputStream os, boolean gfx) throws IOException { + public void saveTo(OutputStream os, boolean gfx, boolean includeImported) throws IOException { checkCharset(); - byte[] uncompressedData = saveToByteArray(gfx); + byte[] uncompressedData = saveToByteArray(gfx, includeImported); compress(new ByteArrayInputStream(uncompressedData), os, compression, lzmaProperties); } @@ -1113,8 +1113,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { return ret; } - private byte[] saveToByteArray() throws IOException { - return saveToByteArray(gfx); + private byte[] saveToByteArray(boolean includeImported) throws IOException { + return saveToByteArray(gfx, includeImported); } private void checkCharset() { @@ -1123,7 +1123,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { } } - private byte[] saveToByteArray(boolean gfx) throws IOException { + private byte[] saveToByteArray(boolean gfx, boolean includeImported) throws IOException { byte[] data; try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); SWFOutputStream sos = new SWFOutputStream(baos, version, charset)) { sos.write(getHeaderBytes(SWFCompression.NONE, gfx)); @@ -1133,7 +1133,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { sos.writeFIXED8(frameRate); sos.writeUI16(frameCount); - sos.writeTags(getLocalTags()); + sos.writeTags(includeImported ? getTags() : getLocalTags()); if (hasEndTag) { sos.writeUI16(0); } @@ -1289,7 +1289,7 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { isModified = false; try { - uncompressedData = saveToByteArray(); + uncompressedData = saveToByteArray(false); } catch (IOException ex) { logger.log(Level.SEVERE, "Cannot save SWF", ex); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/gfx/GfxConvertor.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/gfx/GfxConvertor.java new file mode 100644 index 000000000..3c43d387b --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/gfx/GfxConvertor.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2010-2022 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.gfx; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.ImportAssets2Tag; +import com.jpexs.decompiler.flash.tags.ImportAssetsTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; +import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo; +import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; +import com.jpexs.decompiler.flash.types.KERNINGRECORD; +import com.jpexs.decompiler.flash.types.LANGCODE; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.gfx.FontType; +import com.jpexs.helpers.Helper; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Converts GFX SWF to normal SWF. + * @author JPEXS + */ +public class GfxConvertor { + + public DefineFont2Tag convertDefineCompactedFont(DefineCompactedFont compactedFont) { + DefineFont2Tag ret = new DefineFont2Tag(compactedFont.getSwf()); + ret.fontID = compactedFont.getFontId(); + ret.fontFlagsBold = compactedFont.isBold(); + ret.fontFlagsItalic = compactedFont.isItalic(); + ret.fontFlagsWideOffsets = true; + ret.fontFlagsWideCodes = true; + ret.fontFlagsHasLayout = true; + ret.fontAscent = compactedFont.resize(compactedFont.getAscent()); + ret.fontDescent = compactedFont.resize(compactedFont.getDescent()); + ret.fontLeading = compactedFont.resize(compactedFont.getLeading()); + ret.fontAdvanceTable = new ArrayList<>(); + ret.fontBoundsTable = new ArrayList<>(); + ret.codeTable = new ArrayList<>(); + ret.glyphShapeTable = new ArrayList<>(); + List shp = compactedFont.getGlyphShapeTable(); + for (int g = 0; g < shp.size(); g++) { + ret.fontAdvanceTable.add((int) compactedFont.getGlyphAdvance(g)); //already resized + ret.codeTable.add((int) compactedFont.glyphToChar(g)); + + SHAPE shpX = compactedFont.resizeShape(shp.get(g)); + ret.glyphShapeTable.add(shpX); + ret.fontBoundsTable.add(compactedFont.getGlyphBounds(g)); + } + ret.fontName = compactedFont.getFontNameIntag(); + ret.languageCode = new LANGCODE(1); + ret.fontKerningTable = new ArrayList<>(); + + FontType ft = compactedFont.fonts.get(0); + for (int i = 0; i < ft.kerning.size(); i++) { + KERNINGRECORD kr = new KERNINGRECORD(); + kr.fontKerningAdjustment = compactedFont.resize(ft.kerning.get(i).advance); + kr.fontKerningCode1 = ft.kerning.get(i).char1; + kr.fontKerningCode2 = ft.kerning.get(i).char2; + ret.fontKerningTable.add(kr); + } + return ret; + } + + public DefineBitsLossless2Tag convertDefineSubImage(DefineSubImage defineSubImage) { + DefineBitsLossless2Tag ret = new DefineBitsLossless2Tag(defineSubImage.getSwf()); + ret.characterID = defineSubImage.characterID; + ret.bitmapWidth = defineSubImage.getImageDimension().width; + ret.bitmapHeight = defineSubImage.getImageDimension().height; + ret.bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; + try { + ret.setImage(Helper.readStream(defineSubImage.getImageData())); + } catch (IOException ex) { + Logger.getLogger(GfxConvertor.class.getName()).log(Level.SEVERE, null, ex); + } + return ret; + } + + public DefineBitsLossless2Tag convertDefineExternalImage(DefineExternalImage defineExternalImage) { + DefineBitsLossless2Tag ret = new DefineBitsLossless2Tag(defineExternalImage.getSwf()); + ret.characterID = defineExternalImage.characterID; + ret.bitmapWidth = defineExternalImage.getImageDimension().width; + ret.bitmapHeight = defineExternalImage.getImageDimension().height; + ret.bitmapFormat = DefineBitsLossless2Tag.FORMAT_32BIT_ARGB; + try { + ret.setImage(Helper.readStream(defineExternalImage.getImageData())); + } catch (IOException ex) { + Logger.getLogger(GfxConvertor.class.getName()).log(Level.SEVERE, null, ex); + } + return ret; + } + + public Tag convertTag(Tag tag) { + if (tag instanceof DefineCompactedFont) { + return convertDefineCompactedFont((DefineCompactedFont)tag); + } + if (tag instanceof DefineExternalGradient) { + return null; + } + if (tag instanceof DefineExternalImage) { + return convertDefineExternalImage((DefineExternalImage) tag); + } + + if (tag instanceof DefineExternalImage2) { + return null; + } + + if (tag instanceof DefineExternalSound) { + return null; + } + + if (tag instanceof DefineExternalStreamSound) { + return null; + } + + if (tag instanceof DefineGradientMap) { + return null; + } + + if (tag instanceof DefineSubImage) { + return convertDefineSubImage((DefineSubImage) tag); + } + + if (tag instanceof ExporterInfo) { + return null; + } + + if (tag instanceof FontTextureInfo) { + return null; + } + + if (tag instanceof ImportAssetsTag) { + return null; + } + + if (tag instanceof ImportAssets2Tag) { + return null; + } + + return tag; + } + + public SWF convertSwf(SWF gfxSwf) { + if (!gfxSwf.gfx) { + return gfxSwf; + } + SWF ret = new SWF(); + ret.displayRect = gfxSwf.displayRect; + ret.frameRate = gfxSwf.frameRate; + ret.compression = gfxSwf.compression; + ret.frameCount = gfxSwf.frameCount; + ret.gfx = false; + ret.hasEndTag = gfxSwf.hasEndTag; + for (Tag t:gfxSwf.getTags()) { + Tag ct = convertTag(t); + if (ct != null) { + ret.addTag(ct); + } + } + + return ret; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java index 27208ea60..77ab83abc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -430,7 +430,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { * @throws IOException */ public void writeTag(SWFOutputStream sos) throws IOException { - if (Configuration._debugCopy.get() || isModified()) { + if (Configuration._debugCopy.get() || isModified() || isImported()) { byte[] newData = getData(); byte[] newHeaderData = getHeader(newData.length); sos.write(newHeaderData); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java index 8e3c50b8f..a26cc4153 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineCompactedFont.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.tags.gfx; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.gfx.GfxConvertor; import com.jpexs.decompiler.flash.helpers.FontHelper; import com.jpexs.decompiler.flash.tags.DefineFont2Tag; import com.jpexs.decompiler.flash.tags.base.FontTag; @@ -435,50 +436,14 @@ public final class DefineCompactedFont extends FontTag { return ret; } - protected int resize(double val) { + public int resize(double val) { FontType ft = fonts.get(0); return (int) Math.round(val * 1024.0 / ft.nominalSize); } @Override public FontTag toClassicFont() { - DefineFont2Tag ret = new DefineFont2Tag(swf); - ret.fontID = getFontId(); - ret.fontFlagsBold = isBold(); - ret.fontFlagsItalic = isItalic(); - ret.fontFlagsWideOffsets = true; - ret.fontFlagsWideCodes = true; - ret.fontFlagsHasLayout = true; - ret.fontAscent = resize(getAscent()); - ret.fontDescent = resize(getDescent()); - ret.fontLeading = resize(getLeading()); - ret.fontAdvanceTable = new ArrayList<>(); - ret.fontBoundsTable = new ArrayList<>(); - ret.codeTable = new ArrayList<>(); - ret.glyphShapeTable = new ArrayList<>(); - List shp = getGlyphShapeTable(); - for (int g = 0; g < shp.size(); g++) { - ret.fontAdvanceTable.add((int) getGlyphAdvance(g)); //already resized - ret.codeTable.add((int) glyphToChar(g)); - - SHAPE shpX = resizeShape(shp.get(g)); - ret.glyphShapeTable.add(shpX); - ret.fontBoundsTable.add(getGlyphBounds(g)); - } - ret.fontName = getFontNameIntag(); - ret.languageCode = new LANGCODE(1); - ret.fontKerningTable = new ArrayList<>(); - - FontType ft = fonts.get(0); - for (int i = 0; i < ft.kerning.size(); i++) { - KERNINGRECORD kr = new KERNINGRECORD(); - kr.fontKerningAdjustment = resize(ft.kerning.get(i).advance); - kr.fontKerningCode1 = ft.kerning.get(i).char1; - kr.fontKerningCode2 = ft.kerning.get(i).char2; - ret.fontKerningTable.add(kr); - } - - return ret; + return new GfxConvertor().convertDefineCompactedFont(this); } @Override diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 07bc6cdf7..45abc0c2a 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -46,6 +46,7 @@ import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration; import com.jpexs.decompiler.flash.console.CommandLineArgumentParser; import com.jpexs.decompiler.flash.console.ContextMenuTools; import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode; +import com.jpexs.decompiler.flash.gfx.GfxConvertor; import com.jpexs.decompiler.flash.gui.debugger.DebugListener; import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; import com.jpexs.decompiler.flash.gui.pipes.FirstInstance; @@ -573,8 +574,12 @@ public class Main { try { tempFile = File.createTempFile("ffdec_run_", ".swf"); + SWF swfToSave = swf; + if (swf.gfx) { + swfToSave = new GfxConvertor().convertSwf(swf); + } try ( FileOutputStream fos = new FileOutputStream(tempFile)) { - swf.saveTo(fos); + swfToSave.saveTo(fos, false, swf.gfx); } prepareSwf(new SwfRunPrepare(), tempFile, swf.getFile() == null ? null : new File(swf.getFile()), tempFiles); @@ -620,8 +625,12 @@ public class Main { @Override protected Object doInBackground() throws Exception { + SWF swfToSave = swf; + if (swf.gfx) { + swfToSave = new GfxConvertor().convertSwf(swf); + } try ( OutputStream fos = new BufferedOutputStream(new FileOutputStream(fTempFile))) { - swf.saveTo(fos); + swfToSave.saveTo(fos, false, swf.gfx); } prepareSwf(new SwfDebugPrepare(doPCode), fTempFile, swf.getFile() == null ? null : new File(swf.getFile()), tempFiles); return null; diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index cae65dc83..5ce94f915 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -23,6 +23,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.PreviewExporter; import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.gfx.GfxConvertor; import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; import com.jpexs.decompiler.flash.gui.debugger.DebuggerTools; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; @@ -1582,8 +1583,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } try { tempFile = File.createTempFile("ffdec_view_", ".swf"); + SWF savedSWF = swf; + if (swf.gfx) { + savedSWF = new GfxConvertor().convertSwf(swf); + } try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - swf.saveTo(fos, false); + swf.saveTo(fos, false, swf.gfx); } //Inject Loader if (swf.isAS3() && Configuration.autoOpenLoadedSWFs.get() && Configuration.useAdobeFlashPlayerForPreviews.get() && !DebuggerTools.hasDebugger(swf)) {