diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6b17377..501309358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - [#1871] Toogle buttons for disabling subsprite animation, display preview of sprites/frames - [#1875] Remove no longer accessed items from cache after certain amount of time - [#1280] AS3 Direct editation of traits with the same name +- [#1743] GFX - Adding DefineExternalImage2 and DefineSubImage tags ### Fixed - [#1869] Replace references now replaces all references, not just PlaceObject @@ -20,6 +21,9 @@ All notable changes to this project will be documented in this file. - [#1692] Resolving use namespace - [#1692] Properly distinguish obfuscated names vs namespace suffixes and attributes +### Changed +- GFX - DefineExternalImage2 no longer handled as character + ## [16.3.1] - 2022-11-14 ### Fixed - [#1867] AS3 - §§hasnext, §§nextvalue, §§nextname in some nonstandard compiled SWFs @@ -2593,6 +2597,8 @@ All notable changes to this project will be documented in this file. [#1870]: https://www.free-decompiler.com/flash/issues/1870 [#1871]: https://www.free-decompiler.com/flash/issues/1871 [#1875]: https://www.free-decompiler.com/flash/issues/1875 +[#1280]: https://www.free-decompiler.com/flash/issues/1280 +[#1743]: https://www.free-decompiler.com/flash/issues/1743 [#1869]: https://www.free-decompiler.com/flash/issues/1869 [#1872]: https://www.free-decompiler.com/flash/issues/1872 [#1692]: https://www.free-decompiler.com/flash/issues/1692 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 0421eff81..b332abee8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -133,6 +133,7 @@ import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.enums.ImageFormat; import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; import com.jpexs.decompiler.flash.timeline.AS2Package; import com.jpexs.decompiler.flash.timeline.Frame; import com.jpexs.decompiler.flash.timeline.FrameScript; @@ -300,6 +301,9 @@ public final class SWF implements SWFContainerItem, Timelined { @Internal private volatile Map characters; + @Internal + private volatile Map externalImages2; + @Internal private volatile Map> characterIdTags; @@ -365,9 +369,9 @@ public final class SWF implements SWFContainerItem, Timelined { private Map asmsCache; private Set cyclicCharacters = null; - + private boolean headerModified = false; - + private String charset = "UTF-8"; private static final DecompilerPool decompilerPool = new DecompilerPool(); @@ -382,7 +386,7 @@ public final class SWF implements SWFContainerItem, Timelined { "CFX", // Compressed ScaleForm GFx "ABC" // Non-standard LZMA compressed Flash ); - + /** * Color to paint when there is an error (missing image, ...) */ @@ -394,19 +398,20 @@ public final class SWF implements SWFContainerItem, Timelined { public void setCharset(String charset) { this.charset = charset; - } - + } + public void setHeaderModified(boolean headerModified) { this.headerModified = headerModified; - } + } public boolean isHeaderModified() { return headerModified; - } - + } + public void updateCharacters() { characters = null; characterIdTags = null; + externalImages2 = null; } public void clearTagSwfs() { @@ -478,9 +483,11 @@ public final class SWF implements SWFContainerItem, Timelined { if (characters == null) { Map chars = new HashMap<>(); Map> charIdtags = new HashMap<>(); - parseCharacters(getTags(), chars, charIdtags); + Map eimages = new HashMap<>(); + parseCharacters(getTags(), eimages, chars, charIdtags); characters = Collections.unmodifiableMap(chars); characterIdTags = Collections.unmodifiableMap(charIdtags); + externalImages2 = Collections.unmodifiableMap(eimages); } } } @@ -488,6 +495,18 @@ public final class SWF implements SWFContainerItem, Timelined { return characters; } + public Map getExternalImages2() { + if (externalImages2 == null) { + getCharacters(); + } + return externalImages2; + } + + public DefineExternalImage2 getExternalImage2(int imageId) { + Map images = getExternalImages2(); + return images.get(imageId); + } + public List getCharacterIdTags(int characterId) { if (characterIdTags == null) { getCharacters(); @@ -511,21 +530,22 @@ public final class SWF implements SWFContainerItem, Timelined { return null; } - private void computeDependentCharacters(Timelined timelined, Map> dep) { for (Tag tag : timelined.getTags()) { if (tag instanceof CharacterTag) { int characterId = ((CharacterTag) tag).getCharacterId(); - Set needed = new HashSet<>(); - tag.getNeededCharacters(needed); - for (Integer needed1 : needed) { - Set s = dep.get(needed1); - if (s == null) { - s = new HashSet<>(); - dep.put(needed1, s); - } + if (characterId != -1) { + Set needed = new HashSet<>(); + tag.getNeededCharacters(needed); + for (Integer needed1 : needed) { + Set s = dep.get(needed1); + if (s == null) { + s = new HashSet<>(); + dep.put(needed1, s); + } - s.add(characterId); + s.add(characterId); + } } } if (tag instanceof DefineSpriteTag) { @@ -533,6 +553,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } } + public void computeDependentCharacters() { Map> dep = new HashMap<>(); computeDependentCharacters(this, dep); @@ -829,7 +850,7 @@ public final class SWF implements SWFContainerItem, Timelined { } return null; - } + } public void resetTimelines(Timelined timelined) { timelined.resetTimeline(); @@ -842,28 +863,36 @@ public final class SWF implements SWFContainerItem, Timelined { } } - private void parseCharacters(Iterable list, Map characters, Map> characterIdTags) { + private void parseCharacters(Iterable list, Map externalImages2, Map characters, Map> characterIdTags) { Iterator iterator = list.iterator(); while (iterator.hasNext()) { Tag t = iterator.next(); + + if (t instanceof DefineExternalImage2) { + DefineExternalImage2 ei2 = (DefineExternalImage2) t; + externalImages2.put(ei2.imageID, ei2); + } + if (t instanceof CharacterIdTag) { int characterId = ((CharacterIdTag) t).getCharacterId(); - if (t instanceof CharacterTag) { - if (characters.containsKey(characterId)) { - logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); - } + if (characterId != -1) { + if (t instanceof CharacterTag) { + if (characters.containsKey(characterId)) { + logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); + } - if (characterId != 0) { - characters.put(characterId, (CharacterTag) t); - characterIdTags.put(characterId, new ArrayList<>()); + if (characterId != 0) { + characters.put(characterId, (CharacterTag) t); + characterIdTags.put(characterId, new ArrayList<>()); + } + } else if (characterIdTags.containsKey(characterId)) { + characterIdTags.get(characterId).add((CharacterIdTag) t); } - } else if (characterIdTags.containsKey(characterId)) { - characterIdTags.get(characterId).add((CharacterIdTag) t); } } if (t instanceof DefineSpriteTag) { - parseCharacters(((DefineSpriteTag) t).getTags(), characters, characterIdTags); + parseCharacters(((DefineSpriteTag) t).getTags(), externalImages2, characters, characterIdTags); } } } @@ -982,17 +1011,16 @@ public final class SWF implements SWFContainerItem, Timelined { private byte[] saveToByteArray() throws IOException { return saveToByteArray(gfx); } - + private void checkCharset() { if (version > 5) { charset = Utf8Helper.charsetName; } } - private byte[] saveToByteArray(boolean gfx) throws IOException { + private byte[] saveToByteArray(boolean gfx) throws IOException { byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version, charset)) { + try ( ByteArrayOutputStream baos = new ByteArrayOutputStream(); SWFOutputStream sos = new SWFOutputStream(baos, version, charset)) { sos.write(getHeaderBytes(SWFCompression.NONE, gfx)); sos.writeUI8(version); sos.writeUI32(0); // placeholder for file length @@ -1009,8 +1037,7 @@ public final class SWF implements SWFContainerItem, Timelined { } // update file size - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version, charset)) { + try ( ByteArrayOutputStream baos = new ByteArrayOutputStream(); SWFOutputStream sos = new SWFOutputStream(baos, version, charset)) { sos.writeUI32(data.length); byte[] lengthData = baos.toByteArray(); System.arraycopy(lengthData, 0, data, 4, lengthData.length); @@ -1052,7 +1079,7 @@ public final class SWF implements SWFContainerItem, Timelined { boolean gfx = hdr[1] == 'F' && hdr[2] == 'X'; int version = hdr[3]; long fileSize; - try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { + try ( SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { fileSize = sis.readUI32("fileSize"); } @@ -1122,11 +1149,11 @@ public final class SWF implements SWFContainerItem, Timelined { } @Override - public boolean isModified() { + public boolean isModified() { if (isModified) { return true; } - + if (headerModified) { return true; } @@ -1152,7 +1179,6 @@ public final class SWF implements SWFContainerItem, Timelined { } } - headerModified = false; isModified = false; @@ -1169,9 +1195,9 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF() { version = SWF.DEFAULT_VERSION; displayRect = new RECT(0, 1, 0, 1); - dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); + dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); } - + public SWF(String charset) { this(); this.charset = charset; @@ -1188,7 +1214,7 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { this(is, null, null, null, parallelRead, false, true); } - + public SWF(InputStream is, boolean parallelRead, String charset) throws IOException, InterruptedException { this(is, null, null, null, parallelRead, false, true, charset); } @@ -1205,7 +1231,7 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF(InputStream is, boolean parallelRead, boolean lazy) throws IOException, InterruptedException { this(is, null, null, null, parallelRead, false, lazy); } - + public SWF(InputStream is, boolean parallelRead, boolean lazy, String charset) throws IOException, InterruptedException { this(is, null, null, null, parallelRead, false, lazy, charset); } @@ -1223,7 +1249,7 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF(InputStream is, String file, String fileTitle, boolean parallelRead) throws IOException, InterruptedException { this(is, file, fileTitle, null, parallelRead, false, true); } - + public SWF(InputStream is, String file, String fileTitle, boolean parallelRead, String charset) throws IOException, InterruptedException { this(is, file, fileTitle, null, parallelRead, false, true, charset); } @@ -1245,7 +1271,6 @@ public final class SWF implements SWFContainerItem, Timelined { this(is, null, null, listener, parallelRead, false, true, charset); } - /** * Construct SWF from stream * @@ -1260,7 +1285,7 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { this(is, file, fileTitle, listener, parallelRead, false, true); } - + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, String charset) throws IOException, InterruptedException { this(is, file, fileTitle, listener, parallelRead, false, true, charset); } @@ -1278,11 +1303,11 @@ public final class SWF implements SWFContainerItem, Timelined { public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy, String charset) throws IOException, InterruptedException { this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, null, charset); } - + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException { this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, null, Charset.defaultCharset().name()); } - + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy, UrlResolver resolver) throws IOException, InterruptedException { this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, resolver, Charset.defaultCharset().name()); } @@ -1318,11 +1343,11 @@ public final class SWF implements SWFContainerItem, Timelined { sis.dumpInfo = dumpInfo; sis.skipBytesEx(3, "signature"); // skip siganture version = sis.readUI8("version"); - + if (version > 5) { this.charset = Utf8Helper.charsetName; } - + fileSize = sis.readUI32("fileSize"); dumpInfo.lengthBytes = fileSize; if (listener != null) { @@ -1493,21 +1518,23 @@ public final class SWF implements SWFContainerItem, Timelined { public String getTitleOrShortFileName() { if (fileTitle != null) { return fileTitle; - } + } return new File(file).getName(); } - + public String getShortFileName() { return new File(getTitleOrShortFileName()).getName(); } - + /** - * Gets title of this SWF incuding parent nodes like SwfList and DefineBinaryData - * @return + * Gets title of this SWF incuding parent nodes like SwfList and + * DefineBinaryData + * + * @return */ public String getShortPathTitle() { if (binaryData != null) { - return binaryData.getSwf().getShortPathTitle()+ "/DefineBinaryData (" + binaryData.getCharacterId() + ")"; + return binaryData.getSwf().getShortPathTitle() + "/DefineBinaryData (" + binaryData.getCharacterId() + ")"; } if (swfList != null) { if (swfList.isBundle()) { @@ -1516,14 +1543,16 @@ public final class SWF implements SWFContainerItem, Timelined { } return getTitleOrShortFileName(); } - + /** - * Gets full path title of this SWF incuding parent nodes like SwfList and DefineBinaryData - * @return + * Gets full path title of this SWF incuding parent nodes like SwfList and + * DefineBinaryData + * + * @return */ public String getFullPathTitle() { if (binaryData != null) { - return binaryData.getSwf().getFullPathTitle()+ "/DefineBinaryData (" + binaryData.getCharacterId() + ")"; + return binaryData.getSwf().getFullPathTitle() + "/DefineBinaryData (" + binaryData.getCharacterId() + ")"; } if (swfList != null) { if (swfList.isBundle()) { @@ -1583,6 +1612,9 @@ public final class SWF implements SWFContainerItem, Timelined { for (Tag t : getTags()) { if (t instanceof CharacterTag) { CharacterTag ct = (CharacterTag) t; + if (ct.getCharacterId() == -1) { + continue; + } if (exportNames.containsKey(ct.getCharacterId())) { ct.setExportName(exportNames.get(ct.getCharacterId())); } @@ -1605,6 +1637,9 @@ public final class SWF implements SWFContainerItem, Timelined { for (Tag t : getTags()) { if (t instanceof CharacterTag) { CharacterTag ct = (CharacterTag) t; + if (ct.getCharacterId() == -1) { + continue; + } if (classes.containsKey(ct.getCharacterId())) { ct.setClassName(classes.get(ct.getCharacterId())); } @@ -1656,7 +1691,7 @@ public final class SWF implements SWFContainerItem, Timelined { int version = headerData[3]; long fileSize; - try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(headerData, 4, 8), 4, 4)) { + try ( SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(headerData, 4, 8), 4, 4)) { fileSize = sis.readUI32("fileSize"); } @@ -1679,7 +1714,7 @@ public final class SWF implements SWFContainerItem, Timelined { SWFHeader header = decodeHeader(hdr); long fileSize = header.fileSize; - try (SWFOutputStream sos = new SWFOutputStream(os, header.version, Utf8Helper.charsetName)) { + try ( SWFOutputStream sos = new SWFOutputStream(os, header.version, Utf8Helper.charsetName)) { sos.write(getHeaderBytes(SWFCompression.NONE, header.gfx)); sos.writeUI8(header.version); sos.writeUI32(fileSize); @@ -1693,7 +1728,7 @@ public final class SWF implements SWFContainerItem, Timelined { case 'Z': { // ZWS byte[] lzmaprop = new byte[9]; is.read(lzmaprop); - try (SWFInputStream sis = new SWFInputStream(null, lzmaprop)) { + try ( SWFInputStream sis = new SWFInputStream(null, lzmaprop)) { sis.readUI32("LZMAsize"); // compressed LZMA data size = compressed SWF - 17 byte, // where 17 = 8 byte header + this 4 byte + 5 bytes decoder properties @@ -2748,7 +2783,7 @@ public final class SWF implements SWFContainerItem, Timelined { } } } - + public void clearShapeCache() { shapeExportDataCache.clear(); } @@ -3059,25 +3094,27 @@ public final class SWF implements SWFContainerItem, Timelined { } private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { - Set dependingChars = new HashSet<>(); + Set dependingChars = new HashSet<>(); if (toRemove instanceof CharacterTag) { int characterId = ((CharacterTag) toRemove).getCharacterId(); - dependingChars = getDependentCharacters(characterId); - dependingChars.add(characterId); + if (characterId != -1) { + dependingChars = getDependentCharacters(characterId); + dependingChars.add(characterId); + } } removeTagWithDependenciesFromTimeline(toRemove, timeline, dependingChars); } - + public boolean removeCharacterFromTimeline(int characterId, Timeline timeline) { Set chars = new HashSet<>(); chars.add(characterId); return removeTagWithDependenciesFromTimeline(null, timeline, chars); - } - + } + private boolean removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline, Set dependingChars) { Map stage = new HashMap<>(); Timelined timelined = timeline.timelined; - ReadOnlyTagList tags = timelined.getTags(); + ReadOnlyTagList tags = timelined.getTags(); boolean modified = false; for (int i = 0; i < tags.size(); i++) { Tag t = tags.get(i); @@ -3143,7 +3180,9 @@ public final class SWF implements SWFContainerItem, Timelined { int characterId = -1; if (toRemove instanceof CharacterTag) { characterId = ((CharacterTag) toRemove).getCharacterId(); - modified = removeCharacterFromTimeline(characterId, timeline); + if (characterId != -1) { + modified = removeCharacterFromTimeline(characterId, timeline); + } } Timelined timelined = timeline.timelined; ReadOnlyTagList tags = timelined.getTags(); @@ -3293,12 +3332,12 @@ public final class SWF implements SWFContainerItem, Timelined { updateCharacters(); } } - + public int indexOfTag(Tag tag) { return tags.indexOf(tag); - } - - public static void addTagBefore(Tag newTag, Tag targetTag) { + } + + public static void addTagBefore(Tag newTag, Tag targetTag) { Timelined tim = targetTag.getTimelined(); int index = tim.indexOfTag(targetTag); if (index < 0) { @@ -3352,7 +3391,9 @@ public final class SWF implements SWFContainerItem, Timelined { for (Tag tag : getTags()) { if (tag instanceof CharacterTag) { CharacterTag characterTag = (CharacterTag) tag; - replaceCharacter(characterTag.getCharacterId(), id++); + if (characterTag.getCharacterId() != -1) { + replaceCharacter(characterTag.getCharacterId(), id++); + } } } // then set them to 1,2,3... @@ -3360,7 +3401,9 @@ public final class SWF implements SWFContainerItem, Timelined { for (Tag tag : getTags()) { if (tag instanceof CharacterTag) { CharacterTag characterTag = (CharacterTag) tag; - replaceCharacter(characterTag.getCharacterId(), id++); + if (characterTag.getCharacterId() != -1) { + replaceCharacter(characterTag.getCharacterId(), id++); + } } } } @@ -3387,6 +3430,9 @@ public final class SWF implements SWFContainerItem, Timelined { public void replaceCharacterTags(CharacterTag characterTag, int newCharacterId) { int characterId = characterTag.getCharacterId(); + if (characterId == -1) { + return; + } CharacterTag newCharacter = getCharacter(newCharacterId); newCharacter.setCharacterId(characterId); characterTag.setCharacterId(newCharacterId); @@ -3674,7 +3720,7 @@ public final class SWF implements SWFContainerItem, Timelined { } SWD swd = new SWD(7, items); - try (FileOutputStream fis = new FileOutputStream(file)) { + try ( FileOutputStream fis = new FileOutputStream(file)) { swd.saveTo(fis); } return true; @@ -3807,7 +3853,7 @@ public final class SWF implements SWFContainerItem, Timelined { return false; } SWD swd = new SWD(7, items); - try (FileOutputStream fis = new FileOutputStream(file)) { + try ( FileOutputStream fis = new FileOutputStream(file)) { swd.saveTo(fis); } return true; @@ -3993,9 +4039,11 @@ public final class SWF implements SWFContainerItem, Timelined { for (Tag t : getTags()) { if (t instanceof CharacterTag) { CharacterTag cht = (CharacterTag) t; - Set needed = new HashSet<>(); - cht.getNeededCharacters(needed); - characterToNeeded.put(cht.getCharacterId(), needed); + if (cht.getCharacterId() != -1) { + Set needed = new HashSet<>(); + cht.getNeededCharacters(needed); + characterToNeeded.put(cht.getCharacterId(), needed); + } } } @@ -4036,7 +4084,7 @@ public final class SWF implements SWFContainerItem, Timelined { public void setFileTitle(String fileTitle) { this.fileTitle = fileTitle; } - + @Override public int getFrameCount() { return frameCount; 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 index 9e0302544..f4ac3a03a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/PreviewExporter.java @@ -367,8 +367,10 @@ public class PreviewExporter { //} if (t instanceof CharacterTag) { int characterId = ((CharacterTag) t).getCharacterId(); - doneCharacters.add(characterId); - writeTag(t, sos2); + if (characterId != -1) { + doneCharacters.add(characterId); + writeTag(t, sos2); + } } } 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 5641c9ddd..9785d41c8 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 @@ -679,12 +679,14 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { private void getDependentCharactersOnTimelined(Timelined timelined, Set dependent) { for (Tag tag : timelined.getTags()) { if (tag instanceof CharacterTag) { - Set needed = new HashSet<>(); - tag.getNeededCharactersDeep(needed); - for (int dep : dependent) { - if (needed.contains(dep)) { - dependent.add(((CharacterTag) tag).getCharacterId()); - break; + if (((CharacterTag) tag).getCharacterId() != -1) { + Set needed = new HashSet<>(); + tag.getNeededCharactersDeep(needed); + for (int dep : dependent) { + if (needed.contains(dep)) { + dependent.add(((CharacterTag) tag).getCharacterId()); + break; + } } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage.java index ab27639b4..8264a1448 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage.java @@ -103,6 +103,7 @@ public class DefineExternalImage extends ImageTag { fileName = ""; targetWidth = 1; targetHeight = 1; + bitmapFormat = BITMAP_FORMAT_DDS; createFailedImage(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage2.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage2.java index 058f4103d..e854242d2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage2.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineExternalImage2.java @@ -47,6 +47,8 @@ public class DefineExternalImage2 extends ImageTag { public static final String NAME = "DefineExternalImage2"; + public int imageID; + public int unknownID; public int bitmapFormat; @@ -81,7 +83,7 @@ public class DefineExternalImage2 extends ImageTag { */ @Override public void getData(SWFOutputStream sos) throws IOException { - sos.writeUI16(characterID - 0x8000); + sos.writeUI16(imageID); sos.writeUI16(unknownID); sos.writeUI16(bitmapFormat); sos.writeUI16(targetWidth); @@ -103,6 +105,7 @@ public class DefineExternalImage2 extends ImageTag { public DefineExternalImage2(SWFInputStream sis, ByteArrayRange data) throws IOException { super(sis.getSwf(), ID, NAME, data); readData(sis, data, 0, false, false, false); + characterID = -1; } public DefineExternalImage2(SWF swf) { @@ -111,12 +114,15 @@ public class DefineExternalImage2 extends ImageTag { fileName = ""; targetWidth = 1; targetHeight = 1; + unknownID = 9; //? + bitmapFormat = BITMAP_FORMAT_DDS; + characterID = -1; createFailedImage(); } @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID") | 0x8000; + imageID = sis.readUI16("imageID"); unknownID = sis.readUI16("unknownID"); // always 9 bitmapFormat = sis.readUI16("bitmapFormat"); targetWidth = sis.readUI16("targetWidth"); @@ -214,4 +220,14 @@ public class DefineExternalImage2 extends ImageTag { public boolean importSupported() { return false; } + + @Override + public String toString() { + return tagName + " (i" + imageID + ")"; + } + + @Override + public void setCharacterId(int characterId) { + + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineSubImage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineSubImage.java index c1ac30870..b3363eba9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineSubImage.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/gfx/DefineSubImage.java @@ -47,7 +47,7 @@ public class DefineSubImage extends ImageTag { public static final String NAME = "DefineSubImage"; - public int imageCharacterId; + public int imageId; public int x1; @@ -81,7 +81,7 @@ public class DefineSubImage extends ImageTag { @Override public void getData(SWFOutputStream sos) throws IOException { sos.writeUI16(characterID); - sos.writeUI16(imageCharacterId); + sos.writeUI16(imageId); sos.writeUI16(x1); sos.writeUI16(y1); sos.writeUI16(x2); @@ -113,7 +113,7 @@ public class DefineSubImage extends ImageTag { @Override public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { characterID = sis.readUI16("characterID"); - imageCharacterId = sis.readUI16("imageCharacterId"); + imageId = sis.readUI16("imageId"); x1 = sis.readUI16("x1"); y1 = sis.readUI16("y1"); x2 = sis.readUI16("x2"); @@ -172,14 +172,9 @@ public class DefineSubImage extends ImageTag { public Dimension getImageDimension() { return new Dimension(x2 - x1, y2 - y1); } - - @Override - public void getNeededCharacters(Set needed) { - needed.add(imageCharacterId | 0x8000); - } - + private void initImage() { - DefineExternalImage2 image = (DefineExternalImage2) swf.getImage(imageCharacterId | 0x8000); + DefineExternalImage2 image = swf.getExternalImage2(imageId); if (image == null) { createFailedImage(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/HasCharacterId.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/HasCharacterId.java index 02dc83df5..2d0518777 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/HasCharacterId.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/HasCharacterId.java @@ -7,5 +7,5 @@ package com.jpexs.decompiler.flash.types; public interface HasCharacterId { public int getCharacterId(); - public void setCharacterId(int characterId); + public void setCharacterId(int characterId); } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 895c433a4..002618f51 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -158,6 +158,8 @@ import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; import com.jpexs.decompiler.flash.tags.text.TextParseException; import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.Frame; @@ -4737,9 +4739,20 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se List neededCopies = new ArrayList<>(); for (int n : needed) { - CharacterTag ct = (CharacterTag) imageTag.getSwf().getCharacter(n).cloneTag(); - ct.setSwf(swf); - neededCopies.add(ct); + CharacterTag ct = imageTag.getSwf().getCharacter(n); + if (ct != null) { + ct = (CharacterTag) ct.cloneTag(); + ct.setSwf(swf); + neededCopies.add(ct); + } + } + if (imageTag instanceof DefineSubImage) { + DefineExternalImage2 dei2 = (DefineExternalImage2) imageTag.getSwf().getExternalImage2(((DefineSubImage) imageTag).imageId); + if (dei2 != null) { + dei2 = (DefineExternalImage2) dei2.cloneTag(); + dei2.setSwf(swf); + neededCopies.add(dei2); + } } ImageTag imageTagCopy = (ImageTag) imageTag.cloneTag(); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index 84132bef6..8055f5ea4 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -83,6 +83,9 @@ import com.jpexs.decompiler.flash.tags.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.VideoFrameTag; import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; import com.jpexs.decompiler.flash.treeitems.FolderItem; import com.jpexs.decompiler.flash.treeitems.SWFList; @@ -261,7 +264,13 @@ public class TagTree extends AbstractTagTree { ret = Arrays.asList(DefineTextTag.ID, DefineText2Tag.ID, DefineEditTextTag.ID); break; case TagTreeModel.FOLDER_IMAGES: - ret = Arrays.asList(DefineBitsTag.ID, DefineBitsJPEG2Tag.ID, DefineBitsJPEG3Tag.ID, DefineBitsJPEG4Tag.ID, DefineBitsLosslessTag.ID, DefineBitsLossless2Tag.ID); + if (gfx) { + ret = Arrays.asList(DefineBitsTag.ID, DefineBitsJPEG2Tag.ID, DefineBitsJPEG3Tag.ID, DefineBitsJPEG4Tag.ID, DefineBitsLosslessTag.ID, DefineBitsLossless2Tag.ID, + DefineExternalImage.ID, DefineExternalImage2.ID, DefineSubImage.ID + ); + } else { + ret = Arrays.asList(DefineBitsTag.ID, DefineBitsJPEG2Tag.ID, DefineBitsJPEG3Tag.ID, DefineBitsJPEG4Tag.ID, DefineBitsLosslessTag.ID, DefineBitsLossless2Tag.ID); + } break; case TagTreeModel.FOLDER_MOVIES: ret = Arrays.asList(DefineVideoStreamTag.ID); diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index 31cdc34be..47918386e 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -803,8 +803,11 @@ public class TagTreeContextMenu extends JPopupMenu { } if (firstItem instanceof CharacterTag) { - replaceWithTagMenuItem.setVisible(true); - replaceRefsWithTagMenuItem.setVisible(true); + CharacterTag ct = (CharacterTag) firstItem; + if (ct.getCharacterId() != -1) { + replaceWithTagMenuItem.setVisible(true); + replaceRefsWithTagMenuItem.setVisible(true); + } } TreeItem parent = (TreeItem) tree.getModel().getTreePath(firstItem).getParentPath().getLastPathComponent();