diff --git a/CHANGELOG.md b/CHANGELOG.md index 1309cd5c4..e5e1f768b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. - AS1/2 callmethod action arguments - Fixed §§push after continue - should be before (usually on obfuscated code) - AS1/2 Delete operator with nonIdentifier parameters (e.g. spaces or obfuscated) +- DefineBits (with JPEGTables) tag export ## [11.1.0] - 2018-05-24 ### Added diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java index add2fda48..936deb72a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBitsTag.java @@ -12,7 +12,8 @@ * 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.tags; import com.jpexs.decompiler.flash.SWF; @@ -25,6 +26,7 @@ import com.jpexs.decompiler.flash.types.BasicType; import com.jpexs.decompiler.flash.types.annotations.SWFType; import com.jpexs.decompiler.flash.types.annotations.SWFVersion; import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; import com.jpexs.helpers.SerializableImage; import java.awt.Dimension; import java.awt.image.BufferedImage; @@ -32,6 +34,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -112,19 +117,71 @@ public class DefineBitsTag extends ImageTag implements TagChangedListener { return ImageFormat.JPEG; } + private static List parseJpegChunks(byte[] data) { + //A little inspired by shum way :-) + List ret = new ArrayList<>(); + int pos = 0; + int n = data.length; + // Finding first marker, and skipping the data before this marker. + // (FF 00 - code is escaped FF; FF FF ... (FF xx) - fill bytes before marker). + while (pos < n && ((data[pos] & 0xFF) != 0xFF + || (pos + 1 < n && ((data[pos + 1] & 0xFF) == 0x00 || (data[pos + 1] & 0xFF) == 0xFF)))) { + pos++; + } + + while (pos < n) { + int start = pos++; + int code = data[pos++] & 0xFF; + + // Some tags have length field -- using it + if ((code >= 0xC0 && code <= 0xC7) + || (code >= 0xC9 && code <= 0xCF) + || (code >= 0xDA && code <= 0xEF) + || code == 0xFE) { + int length = (data[pos] & 0xFF) << 8 + (data[pos + 1] & 0xFF); + pos += length; + } + + // Finding next marker. + while (pos < n && ((data[pos] & 0xFF) != 0xFF + || (pos + 1 < n && ((data[pos + 1] & 0xff) == 0x00 || (data[pos + 1] & 0xFF) == 0xFF)))) { + pos++; + } + + if (code == 0xD8 || code == 0xD9) { + // Removing SOI and EOI to avoid wrong EOI-SOI pairs in the middle. + continue; + } + ret.add(Arrays.copyOfRange(data, start, pos)); + } + return ret; + } + @Override public InputStream getOriginalImageData() { if (swf.getJtt() != null) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + int errorLength = hasErrorHeader(jpegData) ? 4 : 0; + + List jpegChunks = parseJpegChunks(jpegData.getRangeData(errorLength, jpegData.getLength() - errorLength)); ByteArrayRange jttdata = swf.getJtt().jpegData; if (jttdata.getLength() != 0) { int jttErrorLength = hasErrorHeader(jttdata) ? 4 : 0; - baos.write(jttdata.getArray(), jttdata.getPos() + jttErrorLength, jttdata.getLength() - jttErrorLength); + List chunksJtt = parseJpegChunks(jttdata.getRangeData(jttErrorLength, jttdata.getLength() - jttErrorLength)); + for (int c = 0; c < jpegChunks.size(); c++) { + int chunkType = (jpegChunks.get(c)[1] & 0xFF); + if (chunkType >= 0xC0 && chunkType <= 0xCF) { + jpegChunks.addAll(c, chunksJtt); + break; + } + } } + jpegChunks.add(0, new byte[]{(byte) 0xFF, (byte) 0xD8}); //SOI to beginning + jpegChunks.add(new byte[]{(byte) 0xFF, (byte) 0xD9}); //EOI to the end - int errorLength = hasErrorHeader(jpegData) ? 4 : 0; - baos.write(jpegData.getArray(), jpegData.getPos() + errorLength, jpegData.getLength() - errorLength); - + for (byte[] chunk : jpegChunks) { + baos.write(chunk); + } return new ByteArrayInputStream(baos.toByteArray()); } catch (IOException ex) { // this should never happen, since IOException comes from OutputStream, but ByteArrayOutputStream should never throw it