diff --git a/CHANGELOG.md b/CHANGELOG.md index aded706bc..e4555b806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. - [#1231] Button transforming - [#1690] Deobfuscation tool dialog for script level (not just current method / all classes) - [#1460] Commandline import of text, images, shapes, symbol-class +- [#1909] Export/import DefineBitsJPEG3/4s alpha channel to/from separate file + ("PNG/GIF/JPEG+alpha" option in GUI, "-format image:png_gif_jpeg_alpha" for commandline) ### Fixed - [#1904] NullPointerException when renaming invalid identifiers in AS1/2 files caused by missing charset @@ -25,6 +27,7 @@ All notable changes to this project will be documented in this file. - Speaker image when sound selected not in the center - [#1908] Slow commandline opening SWF - [#1908] Shape/image import must accept also filenames in the form "CHARID_xxx.ext" instead of just "CHARID.ext" +- Exporting DefineJPEG3/4 with alpha channel to PNG produced JPEG instead ### Changed - Warning before switching deobfuscation is now optional @@ -2772,6 +2775,7 @@ All notable changes to this project will be documented in this file. [#1231]: https://www.free-decompiler.com/flash/issues/1231 [#1690]: https://www.free-decompiler.com/flash/issues/1690 [#1460]: https://www.free-decompiler.com/flash/issues/1460 +[#1909]: https://www.free-decompiler.com/flash/issues/1909 [#1904]: https://www.free-decompiler.com/flash/issues/1904 [#595]: https://www.free-decompiler.com/flash/issues/595 [#1908]: https://www.free-decompiler.com/flash/issues/1908 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java index 6ad6d7a1a..a3b72dffb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java @@ -25,10 +25,14 @@ import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; import com.jpexs.decompiler.flash.helpers.BMPFile; import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -36,6 +40,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import javax.imageio.ImageIO; /** * @@ -48,7 +53,7 @@ public class ImageExporter { if (Thread.currentThread().isInterrupted()) { return ret; } - + if (tags.isEmpty()) { return ret; } @@ -76,7 +81,7 @@ public class ImageExporter { final ImageTag imageTag = (ImageTag) t; - ImageFormat fileFormat = imageTag.getImageFormat(); + ImageFormat fileFormat = imageTag.getOriginalImageFormat(); ImageFormat originalFormat = fileFormat; if (settings.mode == ImageExportMode.PNG) { fileFormat = ImageFormat.PNG; @@ -92,6 +97,7 @@ public class ImageExporter { { final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat))); + final ImageFormat ffileFormat = fileFormat; new RetryTask(() -> { @@ -105,9 +111,37 @@ public class ImageExporter { ImageHelper.write(imageTag.getImageCached().getBufferedImage(), ffileFormat, file); } }, handler).run(); + + final File alphaBinFile = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + ".alpha.bin")); + final File alphaPngFile = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + ".alpha.png")); + + if ((imageTag instanceof HasSeparateAlphaChannel) + && (settings.mode == ImageExportMode.PNG_GIF_JPEG_ALPHA)) { + + HasSeparateAlphaChannel hsac = (HasSeparateAlphaChannel) imageTag; + if (hsac.hasAlphaChannel()) { + new RetryTask(() -> { + byte[] alphaChannel = hsac.getImageAlpha(); + Dimension dim = imageTag.getImageDimension(); + BufferedImage img = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); + int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); + for (int i = 0; i < pixels.length; i++) { + int a = alphaChannel[i] & 0xff; + int v = 0; + int r = v; + int g = v; + int b = v; + + pixels[i] = (a << 24) | (b << 16) | (g << 8) | r; + } + ImageIO.write(img, "PNG", alphaPngFile); + + }, handler).run(); + } + } ret.add(file); } - + if (Thread.currentThread().isInterrupted()) { break; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ImageExportMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ImageExportMode.java index f036fe627..7350205fc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ImageExportMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/modes/ImageExportMode.java @@ -22,5 +22,9 @@ package com.jpexs.decompiler.flash.exporters.modes; */ public enum ImageExportMode { - PNG_GIF_JPEG, PNG, JPEG, BMP + PNG_GIF_JPEG, + PNG, + JPEG, + BMP, + PNG_GIF_JPEG_ALPHA } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java index eaa18a7b9..8852cd86d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ImageImporter.java @@ -25,13 +25,25 @@ import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; import com.jpexs.decompiler.flash.tags.DefineBitsTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.helpers.ByteArrayRange; import com.jpexs.helpers.Helper; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; /** * @@ -159,4 +171,124 @@ public class ImageImporter extends TagImporter { return res; } + + public int bulkImport(File imagesDir, SWF swf, boolean printOut) { + int count = 0; + Map characters = swf.getCharacters(); + List extensions = Arrays.asList("png", "jpg", "jpeg", "gif", "bmp"); + List alphaExtensions = Arrays.asList("png"); + File allFiles[] = imagesDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + String nameLower = name.toLowerCase(); + if (nameLower.endsWith(".alpha.png")) { + return false; + } + + for (String ext : extensions) { + if (nameLower.endsWith("." + ext)) { + return true; + } + } + return false; + } + }); + + File alphaFiles[] = imagesDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + String nameLower = name.toLowerCase(); + for (String ext : alphaExtensions) { + if (nameLower.endsWith(".alpha." + ext)) { + return true; + } + } + return false; + } + }); + for (int characterId : characters.keySet()) { + CharacterTag tag = characters.get(characterId); + if (tag instanceof ImageTag) { + ImageTag imageTag = (ImageTag) tag; + if (!imageTag.importSupported()) { + continue; + } + List existingFilesForImageTag = new ArrayList<>(); + List existingAlphaFilesForImageTag = new ArrayList<>(); + + for (File f : allFiles) { + if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { + existingFilesForImageTag.add(f); + } + } + for (File f : alphaFiles) { + if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { + existingAlphaFilesForImageTag.add(f); + } + } + existingFilesForImageTag.sort(new Comparator() { + @Override + public int compare(File o1, File o2) { + String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); + String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); + int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); + if (ret == 0) { + return o1.getName().compareTo(o2.getName()); + } + return ret; + } + }); + + existingAlphaFilesForImageTag.sort(new Comparator() { + @Override + public int compare(File o1, File o2) { + String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); + String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); + int ret = alphaExtensions.indexOf(ext1) - alphaExtensions.indexOf(ext2); + if (ret == 0) { + return o1.getName().compareTo(o2.getName()); + } + return ret; + } + }); + + if (!existingFilesForImageTag.isEmpty()) { + if (existingFilesForImageTag.size() > 1) { + Logger.getLogger(ImageImporter.class.getName()).log(Level.WARNING, "Multiple matching files for image tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForImageTag.get(0).getName()}); + } + + File sourceFile = existingFilesForImageTag.get(0); + if (printOut) { + System.out.println("Importing character " + characterId + " from file " + sourceFile.getName()); + } + + try { + importImage(imageTag, Helper.readFile(sourceFile.getPath())); + count++; + } catch (IOException ex) { + Logger.getLogger(ImageImporter.class.getName()).log(Level.WARNING, "Cannot import image " + characterId + " from file " + sourceFile.getName(), ex); + } + } + if (!existingAlphaFilesForImageTag.isEmpty()) { + if (existingAlphaFilesForImageTag.size() > 1) { + Logger.getLogger(ImageImporter.class.getName()).log(Level.WARNING, "Multiple matching files for image alpha tag {0} exists, {1} selected", new Object[]{characterId, existingAlphaFilesForImageTag.get(0).getName()}); + } + File sourceFile = existingAlphaFilesForImageTag.get(0); + if (printOut) { + System.out.println("Importing character " + characterId + " alpha from file " + sourceFile.getName()); + } + try { + importImageAlpha(imageTag, Helper.readFile(sourceFile.getPath())); + } catch (IOException ex) { + Logger.getLogger(ImageImporter.class.getName()).log(Level.WARNING, "Cannot import image " + characterId + " alpha from file " + sourceFile.getName(), ex); + } + } + + if (Thread.currentThread().isInterrupted()) { + break; + } + } + } + return count; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java index 073a626db..007bc4158 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/ShapeImporter.java @@ -18,6 +18,7 @@ package com.jpexs.decompiler.flash.importers; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.helpers.ImageHelper; +import com.jpexs.decompiler.flash.importers.svg.SvgImporter; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; @@ -28,15 +29,26 @@ import com.jpexs.decompiler.flash.tags.DefineShape3Tag; import com.jpexs.decompiler.flash.tags.DefineShape4Tag; import com.jpexs.decompiler.flash.tags.DefineShapeTag; import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.helpers.Helper; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -145,4 +157,75 @@ public class ShapeImporter { return res; } + + public int bulkImport(File shapesDir, SWF swf, boolean noFill, boolean printOut) { + SvgImporter svgImporter = new SvgImporter(); + + Map characters = swf.getCharacters(); + int shapeCount = 0; + List extensions = Arrays.asList("svg", "png", "jpg", "jpeg", "gif", "bmp"); + File allFiles[] = shapesDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + String nameLower = name.toLowerCase(); + for (String ext : extensions) { + if (nameLower.endsWith("." + ext)) { + return true; + } + } + return false; + } + }); + for (int characterId : characters.keySet()) { + CharacterTag tag = characters.get(characterId); + if (tag instanceof ShapeTag) { + ShapeTag shapeTag = (ShapeTag) tag; + List existingFilesForShapeTag = new ArrayList<>(); + for (File f : allFiles) { + if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { + existingFilesForShapeTag.add(f); + } + } + existingFilesForShapeTag.sort(new Comparator() { + @Override + public int compare(File o1, File o2) { + String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); + String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); + int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); + if (ret == 0) { + return o1.getName().compareTo(o2.getName()); + } + return ret; + } + }); + + if (existingFilesForShapeTag.isEmpty()) { + continue; + } + + if (existingFilesForShapeTag.size() > 1) { + Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Multiple matching files for shape tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForShapeTag.get(0).getName()}); + } + File sourceFile = existingFilesForShapeTag.get(0); + + try { + if (printOut) { + System.out.println("Importing character " + characterId + " from file " + sourceFile.getName()); + } + if (sourceFile.getAbsolutePath().toLowerCase().endsWith(".svg")) { + svgImporter.importSvg(shapeTag, Helper.readTextFile(sourceFile.getAbsolutePath()), !noFill); + } else { + importImage(shapeTag, Helper.readFile(sourceFile.getAbsolutePath()), 0, !noFill); + } + shapeCount++; + } catch (IOException ex) { + Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import shape " + characterId + " from file " + sourceFile.getName(), ex); + } + if (Thread.currentThread().isInterrupted()) { + break; + } + } + } + return shapeCount; + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java index 5d94987e3..34a764325 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG3Tag.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.BasicType; @@ -46,7 +47,7 @@ import java.util.logging.Logger; * @author JPEXS */ @SWFVersion(from = 3) //Note: GIF and PNG since version -public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { +public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag, HasSeparateAlphaChannel { public static final int ID = 35; @@ -139,10 +140,12 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { setModified(true); } + @Override public byte[] getImageAlpha() throws IOException { return SWFInputStream.uncompressByteArray(bitmapAlphaData.getRangeData()); } + @Override public void setImageAlpha(byte[] data) throws IOException { ImageFormat fmt = ImageTag.getImageFormat(imageData); if (fmt != ImageFormat.JPEG) { @@ -159,6 +162,11 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { setModified(true); } + @Override + public boolean hasAlphaChannel() { + return bitmapAlphaData.getLength() > 0; + } + @Override public ImageFormat getImageFormat() { ImageFormat fmt = getOriginalImageFormat(); @@ -175,18 +183,18 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { @Override public InputStream getOriginalImageData() { - if (bitmapAlphaData.getLength() == 0) { // No alpha - JpegFixer jpegFixer = new JpegFixer(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - jpegFixer.fixJpeg(new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()), baos); - } catch (IOException ex) { - Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, null, ex); - } - return new ByteArrayInputStream(baos.toByteArray()); + //if (bitmapAlphaData.getLength() == 0) { // No alpha + JpegFixer jpegFixer = new JpegFixer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + jpegFixer.fixJpeg(new ByteArrayInputStream(imageData.getArray(), imageData.getPos(), imageData.getLength()), baos); + } catch (IOException ex) { + Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, null, ex); } + return new ByteArrayInputStream(baos.toByteArray()); + //} - return null; + //return null; } @Override @@ -245,7 +253,7 @@ public class DefineBitsJPEG3Tag extends ImageTag implements AloneTag { } catch (IOException ex) { Logger.getLogger(DefineBitsJPEG3Tag.class.getName()).log(Level.SEVERE, "Failed to get image", ex); } - + SerializableImage img = new SerializableImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = img.getGraphics(); g.setColor(SWF.ERROR_COLOR); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java index 04d435efa..1367a3ebc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsJPEG4Tag.java @@ -22,6 +22,7 @@ import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.dumpview.DumpInfoSpecialType; import com.jpexs.decompiler.flash.helpers.ImageHelper; import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.types.BasicType; @@ -46,7 +47,7 @@ import java.util.logging.Logger; * @author JPEXS */ @SWFVersion(from = 10) -public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { +public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag, HasSeparateAlphaChannel { public static final int ID = 90; @@ -144,10 +145,12 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { setModified(true); } + @Override public byte[] getImageAlpha() throws IOException { return SWFInputStream.uncompressByteArray(bitmapAlphaData.getRangeData()); } + @Override public void setImageAlpha(byte[] data) throws IOException { ImageFormat fmt = ImageTag.getImageFormat(imageData); if (fmt != ImageFormat.JPEG) { @@ -163,6 +166,11 @@ public class DefineBitsJPEG4Tag extends ImageTag implements AloneTag { clearCache(); setModified(true); } + + @Override + public boolean hasAlphaChannel() { + return bitmapAlphaData.getLength() > 0; + } @Override public ImageFormat getImageFormat() { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/HasSeparateAlphaChannel.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/HasSeparateAlphaChannel.java new file mode 100644 index 000000000..a074b8f8f --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/HasSeparateAlphaChannel.java @@ -0,0 +1,32 @@ +/* + * 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.tags.base; + +import java.io.IOException; + +/** + * + * @author JPEXS + */ +public interface HasSeparateAlphaChannel { + + public boolean hasAlphaChannel(); + + public byte[] getImageAlpha() throws IOException; + + public void setImageAlpha(byte[] data) throws IOException; +} diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index c88516c9b..3f9530d72 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -218,6 +218,7 @@ import java.util.logging.Logger; import com.jpexs.decompiler.flash.Bundle; import com.jpexs.decompiler.flash.gui.translator.Translator; import com.jpexs.decompiler.flash.importers.SymbolClassImporter; +import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel; import java.awt.Font; import java.util.Comparator; @@ -389,6 +390,7 @@ public class CommandLineArgumentParser { out.println(" image:png - PNG format for Images"); out.println(" image:jpeg - JPEG format for Images"); out.println(" image:bmp - BMP format for Images"); + out.println(" image:png_gif_jpeg_alpha - PNG/GIF/JPEG+ALPHA format for Images"); out.println(" text:plain - Plain text format for Texts"); out.println(" text:formatted - Formatted text format for Texts"); out.println(" text:svg - SVG format for Texts"); @@ -3246,7 +3248,7 @@ public class CommandLineArgumentParser { CharacterTag characterTag = swf.getCharacter(imageId); String repFile = args.pop(); byte[] data = Helper.readFile(repFile); - if (characterTag instanceof DefineBitsJPEG3Tag || characterTag instanceof DefineBitsJPEG4Tag) { + if (characterTag instanceof HasSeparateAlphaChannel) { ImageTag imageTag = (ImageTag) characterTag; new ImageImporter().importImageAlpha(imageTag, data); } else { @@ -3699,68 +3701,8 @@ public class CommandLineArgumentParser { System.exit(1); } ShapeImporter shapeImporter = new ShapeImporter(); - SvgImporter svgImporter = new SvgImporter(); - - Map characters = swf.getCharacters(); - int shapeCount = 0; - List extensions = Arrays.asList("svg", "png", "jpg", "jpeg", "gif", "bmp"); - File allFiles[] = shapesDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - String nameLower = name.toLowerCase(); - for (String ext : extensions) { - if (nameLower.endsWith("." + ext)) { - return true; - } - } - return false; - } - }); - for (int characterId : characters.keySet()) { - CharacterTag tag = characters.get(characterId); - if (tag instanceof ShapeTag) { - ShapeTag shapeTag = (ShapeTag) tag; - List existingFilesForShapeTag = new ArrayList<>(); - for (File f : allFiles) { - if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { - existingFilesForShapeTag.add(f); - } - } - existingFilesForShapeTag.sort(new Comparator() { - @Override - public int compare(File o1, File o2) { - String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); - String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); - int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); - if (ret == 0) { - return o1.getName().compareTo(o2.getName()); - } - return ret; - } - }); - - if (existingFilesForShapeTag.isEmpty()) { - continue; - } - - if (existingFilesForShapeTag.size() > 1) { - logger.log(Level.WARNING, "Multiple matching files for shape tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForShapeTag.get(0).getName()}); - } - File sourceFile = existingFilesForShapeTag.get(0); - - try { - System.out.println("Importing character " + characterId + " from file " + sourceFile.getName()); - if (sourceFile.getAbsolutePath().toLowerCase().endsWith(".svg")) { - svgImporter.importSvg(shapeTag, Helper.readTextFile(sourceFile.getAbsolutePath()), !noFill); - } else { - shapeImporter.importImage(shapeTag, Helper.readFile(sourceFile.getAbsolutePath()), 0, !noFill); - } - shapeCount++; - } catch (IOException ex) { - logger.log(Level.WARNING, "Cannot import shape " + characterId + " from file " + sourceFile.getName(), ex); - } - } - } + int shapeCount = shapeImporter.bulkImport(shapesDir, swf, noFill, true); + System.out.println("Writing outfile"); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { swf.saveTo(fos); @@ -3796,64 +3738,7 @@ public class CommandLineArgumentParser { System.exit(1); } ImageImporter imageImporter = new ImageImporter(); - int imageCount = 0; - Map characters = swf.getCharacters(); - final List extensions = Arrays.asList("png", "jpg", "jpeg", "gif", "bmp"); - File allFiles[] = imagesDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - String nameLower = name.toLowerCase(); - for (String ext : extensions) { - if (nameLower.endsWith("." + ext)) { - return true; - } - } - return false; - } - }); - for (int characterId : characters.keySet()) { - CharacterTag tag = characters.get(characterId); - if (tag instanceof ImageTag) { - ImageTag imageTag = (ImageTag) tag; - if (!imageTag.importSupported()) { - continue; - } - List existingFilesForImageTag = new ArrayList<>(); - for (File f : allFiles) { - if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { - existingFilesForImageTag.add(f); - } - } - existingFilesForImageTag.sort(new Comparator() { - @Override - public int compare(File o1, File o2) { - String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); - String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); - int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); - if (ret == 0) { - return o1.getName().compareTo(o2.getName()); - } - return ret; - } - }); - - if (existingFilesForImageTag.isEmpty()) { - continue; - } - - if (existingFilesForImageTag.size() > 1) { - logger.log(Level.WARNING, "Multiple matching files for image tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForImageTag.get(0).getName()}); - } - File sourceFile = existingFilesForImageTag.get(0); - try { - System.out.println("Importing character " + characterId + " from file " + sourceFile.getName()); - imageImporter.importImage(imageTag, Helper.readFile(sourceFile.getPath())); - imageCount++; - } catch (IOException ex) { - logger.log(Level.WARNING, "Cannot import image " + characterId + " from file " + sourceFile.getName(), ex); - } - } - } + int imageCount = imageImporter.bulkImport(imagesDir, swf, true); System.out.println("Writing outfile"); try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) { swf.saveTo(fos); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 53aefde34..a53ddbb1a 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -233,6 +233,7 @@ import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -262,6 +263,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.imageio.ImageIO; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import javax.swing.Box; @@ -3078,67 +3080,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public Void doInBackground() throws Exception { try { - Map characters = swf.getCharacters(); - List extensions = Arrays.asList("svg", "png", "jpg", "jpeg", "gif", "bmp"); - File allFiles[] = fShapesDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - String nameLower = name.toLowerCase(); - for (String ext : extensions) { - if (nameLower.endsWith("." + ext)) { - return true; - } - } - return false; - } - }); - for (int characterId : characters.keySet()) { - CharacterTag tag = characters.get(characterId); - if (tag instanceof ShapeTag) { - ShapeTag shapeTag = (ShapeTag) tag; - List existingFilesForShapeTag = new ArrayList<>(); - for (File f : allFiles) { - if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { - existingFilesForShapeTag.add(f); - } - } - existingFilesForShapeTag.sort(new Comparator() { - @Override - public int compare(File o1, File o2) { - String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); - String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); - int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); - if (ret == 0) { - return o1.getName().compareTo(o2.getName()); - } - return ret; - } - }); - - if (existingFilesForShapeTag.isEmpty()) { - continue; - } - - if (existingFilesForShapeTag.size() > 1) { - Logger.getLogger(MainPanel.class.getName()).log(Level.WARNING, "Multiple matching files for shape tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForShapeTag.get(0).getName()}); - } - File sourceFile = existingFilesForShapeTag.get(0); - - try { - if (sourceFile.getAbsolutePath().toLowerCase().endsWith(".svg")) { - svgImporter.importSvg(shapeTag, Helper.readTextFile(sourceFile.getAbsolutePath()), !noFill); - } else { - shapeImporter.importImage(shapeTag, Helper.readFile(sourceFile.getAbsolutePath()), 0, !noFill); - } - count++; - } catch (IOException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.WARNING, "Cannot import shape " + characterId + " from file " + sourceFile.getName(), ex); - } - if (Thread.currentThread().isInterrupted()) { - break; - } - } - } + count = shapeImporter.bulkImport(fShapesDir, swf, noFill, false); swf.clearImageCache(); swf.clearShapeCache(); } catch (Exception ex) { @@ -3197,65 +3139,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se @Override public Void doInBackground() throws Exception { try { - Map characters = swf.getCharacters(); - List extensions = Arrays.asList("png", "jpg", "jpeg", "gif", "bmp"); - File allFiles[] = fImagesDir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - String nameLower = name.toLowerCase(); - for (String ext : extensions) { - if (nameLower.endsWith("." + ext)) { - return true; - } - } - return false; - } - }); - for (int characterId : characters.keySet()) { - CharacterTag tag = characters.get(characterId); - if (tag instanceof ImageTag) { - ImageTag imageTag = (ImageTag) tag; - if (!imageTag.importSupported()) { - continue; - } - List existingFilesForImageTag = new ArrayList<>(); - for (File f : allFiles) { - if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) { - existingFilesForImageTag.add(f); - } - } - existingFilesForImageTag.sort(new Comparator() { - @Override - public int compare(File o1, File o2) { - String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1); - String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1); - int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2); - if (ret == 0) { - return o1.getName().compareTo(o2.getName()); - } - return ret; - } - }); - - if (existingFilesForImageTag.isEmpty()) { - continue; - } - - if (existingFilesForImageTag.size() > 1) { - Logger.getLogger(MainPanel.class.getName()).log(Level.WARNING, "Multiple matching files for image tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForImageTag.get(0).getName()}); - } - File sourceFile = existingFilesForImageTag.get(0); - try { - imageImporter.importImage(imageTag, Helper.readFile(sourceFile.getPath())); - count++; - } catch (IOException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.WARNING, "Cannot import image " + characterId + " from file " + sourceFile.getName(), ex); - } - if (Thread.currentThread().isInterrupted()) { - break; - } - } - } + count = imageImporter.bulkImport(fImagesDir, swf, false); swf.clearImageCache(); } catch (Exception ex) { logger.log(Level.SEVERE, "Error during import", ex); @@ -3298,7 +3182,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se chooser.setAcceptAllFileFilterUsed(false); if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - boolean textsFolderExists = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).exists(); + boolean textsFolderExists = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).exists(); File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); if (!textsFolderExists) { textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 3b41b7fb0..76acec3ee 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -98,3 +98,6 @@ zoom.invalid = Invalid zoom value. symbolclass = Symbol-Class mapping symbolclass.csv = CSV + +#after 18.0.0 +images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties index 4fcea21f2..d69681878 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog_cs.properties @@ -97,3 +97,6 @@ zoom.invalid = Neplatn\u00e1 hodnota velikost. symbolclass = Mapov\u00e1n\u00ed symbol\u016f na t\u0159\u00eddy symbolclass.csv = CSV + +#after 18.0.0 +images.png_gif_jpeg_alpha = PNG/GIF/JPEG+alpha \ No newline at end of file