From 6ae253ef7e375ae4f0ab1937bde703c04ca41db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 3 Jun 2025 21:59:25 +0200 Subject: [PATCH] Added: Open in the Flash Player context menu on graphic/sound tags and frames --- CHANGELOG.md | 1 + .../flash/exporters/PreviewExporter.java | 123 ++++++------------ .../flash/tags/DefineButton2Tag.java | 15 +++ .../flash/tags/DefineButtonTag.java | 6 + .../flash/tags/PlaceObject2Tag.java | 33 +++++ .../flash/tags/PlaceObject3Tag.java | 62 +++++++++ .../flash/tags/PlaceObject4Tag.java | 62 +++++++++ .../com/jpexs/decompiler/flash/tags/Tag.java | 38 +++++- src/com/jpexs/decompiler/flash/gui/Main.java | 6 +- .../flash/gui/graphics/playflash16.png | Bin 0 -> 4633 bytes .../flash/gui/locales/MainFrame.properties | 4 +- .../flash/gui/locales/MainFrame_cs.properties | 4 +- .../flash/gui/player/ZoomPanel.java | 26 ++-- .../flash/gui/tagtree/TagTreeContextMenu.java | 82 +++++++++++- 14 files changed, 356 insertions(+), 106 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/playflash16.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c12358ff..a6f1e1b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file. - Disable AS1/2/3 direct editation when editing P-code - AS3 - navigation to definition in other SWF file and also player/airglobal - [#2463] Export subsprites animation context menu on frames +- Open in the Flash Player context menu on graphic/sound tags and frames ### Changed - AS1/2 - Single DoAction tag inside frame is now displayed directly as frame node 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 c53c32ff8..ca2dbf41e 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 @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.action.parser.ActionParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser; import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.DefineBitsTag; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; @@ -43,6 +44,7 @@ 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.SymbolClassTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.tags.VideoFrameTag; import com.jpexs.decompiler.flash.tags.base.AloneTag; @@ -58,6 +60,7 @@ 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.SoundStreamFrameRange; +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.BUTTONCONDACTION; @@ -252,6 +255,10 @@ public class PreviewExporter { public SWFHeader exportSwf(OutputStream os, TreeItem treeItem, Color backgroundColor, int fontPageNum, boolean showControls) throws IOException, ActionParseException { SWF swf = (SWF) treeItem.getOpenable(); + if (treeItem instanceof TagScript) { + treeItem = ((TagScript) treeItem).getTag(); + } + int frameCount = 1; float frameRate = swf.frameRate; HashMap videoFrames = new HashMap<>(); @@ -347,18 +354,21 @@ public class PreviewExporter { setBgColorTag.writeTag(sos2); } + Set doneCharacters = new LinkedHashSet<>(); if (treeItem instanceof Frame) { Frame fn = (Frame) treeItem; Timelined parent = fn.timeline.timelined; - Set doneCharacters = new LinkedHashSet<>(); 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 + if (t instanceof DoActionTag + || t instanceof DoInitActionTag + || t instanceof ABCContainerTag + || t instanceof SymbolClassTag + || t instanceof ExportAssetsTag) { continue; } @@ -366,8 +376,7 @@ public class PreviewExporter { t.getNeededCharactersDeep(needed); for (int n : needed) { if (!doneCharacters.contains(n)) { - writeTag(swf.getCharacter(n), sos2); - doneCharacters.add(n); + writeTag(swf.getCharacter(n), sos2, doneCharacters); } } @@ -377,8 +386,7 @@ public class PreviewExporter { if (t instanceof CharacterTag) { int characterId = ((CharacterTag) t).getCharacterId(); if (characterId != -1) { - doneCharacters.add(characterId); - writeTag(t, sos2); + writeTag(t, sos2, doneCharacters); } } } @@ -427,11 +435,11 @@ public class PreviewExporter { } } - writeTag(characterTag, sos2); + writeTag(characterTag, sos2, doneCharacters); } } - writeTag((Tag) treeItem, sos2); + writeTag((Tag) treeItem, sos2, doneCharacters); MATRIX mat = new MATRIX(); mat.hasRotate = false; @@ -569,25 +577,6 @@ public class PreviewExporter { 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, swf.getCharset()); - 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" @@ -614,59 +603,6 @@ public class PreviewExporter { 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, swf.getCharset()); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, false, - "StopSounds\n" - + "Stop", swf.version, false, swf.getCharset()); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - new ShowFrameTag(swf).writeTag(sos2); } else if (treeItem instanceof DefineVideoStreamTag) { List frs = new ArrayList<>(videoFrames.values()); Collections.sort(frs, new Comparator() { @@ -705,8 +641,10 @@ public class PreviewExporter { MATRIX m2 = new Matrix(m).preConcatenate(new Matrix(mat)).toMATRIX(); pt.writeTagWithMatrix(sos2, m2); lastTag = t; + } else if (t instanceof DoActionTag) { + //ignore } else { - t.writeTag(sos2); + t.writeTagNoScripts(sos2); lastTag = t; } } @@ -746,16 +684,33 @@ public class PreviewExporter { return t; } - private static void writeTag(Tag t, SWFOutputStream sos) throws IOException { + private static void writeTag(Tag t, SWFOutputStream sos, Set doneCharacters) throws IOException { t = classicTag(t); + if (t instanceof CharacterIdTag) { + int chId = ((CharacterIdTag) t).getCharacterId(); + if (doneCharacters.contains(chId)) { + return; + } + doneCharacters.add(chId); + } + 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); + + Set needed = new LinkedHashSet<>(); + ((Tag) chIdTag).getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + writeTag(((Tag) chIdTag).getSwf().getCharacter(n), sos, doneCharacters); + } + } + + ((Tag) chIdTag).writeTagNoScripts(sos); } } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java index 86f950901..a85e568a1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java @@ -141,6 +141,21 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer { sos.writeBUTTONCONDACTIONList(actions); } + @Override + public void getDataNoScript(SWFOutputStream sos) throws IOException { + sos.writeUI16(buttonId); + sos.writeUB(7, reserved); + sos.writeUB(1, trackAsMenu ? 1 : 0); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + try (SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion(), getCharset())) { + sos2.writeBUTTONRECORDList(characters, true); + } + byte[] brdata = baos2.toByteArray(); + sos.writeUI16(0); + sos.write(brdata); + } + @Override public int getCharacterId() { return buttonId; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index c228163c4..a26eac98d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -118,6 +118,12 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer { sos.write(getActionBytes()); } + @Override + public void getDataNoScript(SWFOutputStream sos) throws IOException { + sos.writeUI16(buttonId); + sos.writeBUTTONRECORDList(characters, false); + } + @Override public int getCharacterId() { return buttonId; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java index 6b4bce900..37eea0e2f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java @@ -259,6 +259,39 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont } } + @Override + public void getDataNoScript(SWFOutputStream sos) throws IOException { + sos.writeUB(1, 0); //no clip actions + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUI16(depth); + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + } + + + @Override public int getPlaceObjectNum() { return 2; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index c413d958b..6cd02ded8 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -442,6 +442,68 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont } } + @Override + public void getDataNoScript(SWFOutputStream sos) throws IOException { + sos.writeUB(1, 0); //no clip actions + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUB(1, reserved ? 1 : 0); + sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasImage ? 1 : 0); + sos.writeUB(1, placeFlagHasClassName ? 1 : 0); + sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); + sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); + sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); + sos.writeUI16(depth); + + if (placeFlagHasClassName) { + sos.writeString(className); + } + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + if (placeFlagHasFilterList) { + sos.writeFILTERLIST(surfaceFilterList); + } + if (placeFlagHasBlendMode) { + sos.writeUI8(blendMode); + } + if (placeFlagHasCacheAsBitmap) { + if (!bitmapCacheBug) { + sos.writeUI8(bitmapCache); + } + } + if (placeFlagHasVisible) { + sos.writeUI8(visible); + } + if (placeFlagOpaqueBackground) { + sos.writeRGBA(backgroundColor); + } + } + + + @Override public int getPlaceObjectNum() { return 3; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index 77ab319bd..d51690907 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -463,6 +463,68 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont } } + @Override + public void getDataNoScript(SWFOutputStream sos) throws IOException { + sos.writeUB(1, 0); //No clip actions + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUB(1, reserved ? 1 : 0); + sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasImage ? 1 : 0); + sos.writeUB(1, placeFlagHasClassName ? 1 : 0); + sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); + sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); + sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); + sos.writeUI16(depth); + + if (placeFlagHasClassName) { + sos.writeString(className); + } + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + if (placeFlagHasFilterList) { + sos.writeFILTERLIST(surfaceFilterList); + } + if (placeFlagHasBlendMode) { + sos.writeUI8(blendMode); + } + if (placeFlagHasCacheAsBitmap) { + if (!bitmapCacheBug) { + sos.writeUI8(bitmapCache); + } + } + if (placeFlagHasVisible) { + sos.writeUI8(visible); + } + if (placeFlagOpaqueBackground) { + sos.writeRGBA(backgroundColor); + } + } + + + @Override public int getPlaceObjectNum() { return 4; 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 7c09b223e..ea6be08a6 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 @@ -561,6 +561,18 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { sos.write(originalRange.getArray(), originalRange.getPos(), originalRange.getLength()); } } + + /** + * Writes Tag value to the stream, ignoring all scripts + * @param sos + * @throws IOException + */ + public void writeTagNoScripts(SWFOutputStream sos) throws IOException { + byte[] newData = getDataNoScript(); + byte[] newHeaderData = getHeader(newData.length); + sos.write(newHeaderData); + sos.write(newData); + } /** * Clones the tag. @@ -625,7 +637,7 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { * @throws IOException On I/O error */ public abstract void getData(SWFOutputStream sos) throws IOException; - + /** * Gets data bytes * @@ -652,6 +664,30 @@ public abstract class Tag implements NeedsCharacters, Exportable, Serializable { return baos.toByteArray(); } + + /** + * Gets data bytes ignoring all scripts + * @param sos SWF output stream + * @throws IOException On I/O error + */ + public void getDataNoScript(SWFOutputStream sos) throws IOException { + getData(sos); + } + + public byte[] getDataNoScript() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + try (SWFOutputStream sos = new SWFOutputStream(os, getVersion(), getCharset())) { + getDataNoScript(sos); + if (remainingData != null) { + sos.write(remainingData); + } + } catch (IOException e) { + throw new Error("This should never happen.", e); + } + + return baos.toByteArray(); + } /** * Gets original byte range. diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index 9776c354a..4cd2f6310 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -607,12 +607,12 @@ public class Main { } } - public static void runAsync(File swfFile) { + public static boolean runAsync(File swfFile) { String playerLocation = Configuration.playerLocation.get(); if (playerLocation.isEmpty() || (!new File(playerLocation).exists())) { ViewMessages.showMessageDialog(getDefaultMessagesComponent(), AppStrings.translate("message.playerpath.notset"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); advancedSettings("paths"); - return; + return false; } try { final Process process = Runtime.getRuntime().exec(new String[]{playerLocation, swfFile.getAbsolutePath()}); @@ -624,8 +624,10 @@ public class Main { } } }); + return true; } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); + return false; } } diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/playflash16.png b/src/com/jpexs/decompiler/flash/gui/graphics/playflash16.png new file mode 100644 index 0000000000000000000000000000000000000000..6eba79d9e99ba1bf1399cb2a64deddbf351c4eed GIT binary patch literal 4633 zcmeHKdsGu=77xV&4N67OqDvtTP%JvhO z?+30$#TFmfhl`Ju3KgwVpOuy43zmXgMXPA-Vg;0b6Ah!$7Mg3i#-_&s#-QITpS?m#yiRyJ? zxGWHf-8kjKlHA>zNd?vZmG5u*s4ehpf>FGnB%?uXhvuBA&M(a8 zZLO}kuq?B}`f4N@;(vAbcW^%3^fy@tjE`}6j*e6n+n z`o=G3Cp31ho46wA?bYp%AJ#WLe3vDNiW45+m9sB(QdCI5p<^?+kCuo0#$ASd7p}jT z|EtbxN7Ma~aVP3yT$`RAQ%tT{Cm!AqkiR=lJf?9_<8{r{JLS`YbrI^3<05wm?&h9a zleYP<*-dFwO2gKXVe7X=`uVRo?YC*{mW=~9x;~a1C>il@-_*@RFMhcF>XcPw53AFI z&d(i99;^wD37#6uddr_TXVmUDeEnw%6XJ>uil2TAo!6nZRo{-vTZ?(jC@Bb5{1)2r zuy#z`v}5XTgDyYU-3)CSKl>d+t*O2J+Tp^;oX+7#uiSVs$8$@2_&4>nCnr}{imdbM zYH!OMY~DfHS0a?QaD`^4mQjD{ZlSI6GJN^SPp!2#&NO=#>^!*1l9Q*Ly({+m{g9T2 z4}N~|FMjEZY(`0zt^8JUZZ)X6OcK;qiY88mSxjscx9AAA%VY&b$6!P&TvimDOHeEw zkwKc}P|NWOh(+RZ=mVh!(O8v)fsDu23T3%WMzdY! zl0$A>8F;3}9Ejysq2|h=6pfapwAcuifGuDnaEy!0$w8Gg&p79(c38n+3oJ$AwxsTsFcnnK->I>{Lu90O@w<4=wCTphY zVMI(OVWz@*L*Q7Cz13key2HUS4q+rrK-CU*<@VWfvRb3#3d*|(ZNC;mw*K#2?k4r z2mHvRKkZ*fgXhggjfhmMG`$MMhKyZ zE5dLL(Yc{;Og714Goc`zqzTO+I979pdx0igHbJYFLp(O}N}@HQlpYv>9w5!Q#c6-F zlth|{L<*((?4TBDR4&5iOWfvkVq_p3KrKpFDgbcH zK{PU@jX)`jEy-dr${`wpMO*ePYd}NcD21v}iU6PpPbfn~GK8PRLu7oeOduJJ@MK6Y zeG5+N=l?Hlx_wyDT}h86?O^};Zc$fHB@*+x-nw3mq`R3|EO%STP^>EiJDN%0Za)E5 z*Aiwx%^3ukAKeA}QcnIsF%Sa1SkL84U@pibEZ|FcuoTk^VIHoN5)ugy7b4=m=yr>q za-ue3LI&Uwa0Lp~%@u1@cdFj+tM4=rbRGa@Fd~9`Q3i3kgXPc@<7Kqbod4n@+O5!Q z!~nZ485mt)5^{P*VK-m&(D@U8-Ff&EcL31+o4gjk{dD!y^;!(PmU4e}_0#oQ47`?d ze|7!e=wiM)Oc7@A1>^)rrK?WOGjPoE(oLPLVytD%V9dZ1J34{nEo*G5ox$)8pf3-` zw(UVc=uN3LG2YEyoi183>8YSn}!$+^;X6>}4(2cA(xw$#;($X6%+BR_EY zu(V$}*3$T)jxnRAL^>b*Q&Ch@STNE72kyN(%yBV!iuWQTd&pOL!-wRr4d*-!`DAnP zrCr(|IB7@I{q8Kd^mM`79bp3+s>}A@tZ$Fuk4;_gGd*x&d1u>&r9qVoDqQz(jd{?} zT)4-3cj(F!Z?<_oAN*Ze=A(f#OP??v$NnevdB7t6w?5qQK8x>Pb<8Xs7R2)M(R+9{ zhVOjz%(w2Bz&hR9y|%5T3!IFz9s`~*N)m4^X-uj)p7>dX=;&_^BM*r!TBE?auXv5$ z*8#<+_IieEk_SGk|MBO0QCEyt(!RJ-Sg8Eujj$v0HWkPGWuae9*$1~ok?9MW zmxq*TQ6nKT-nL;)$Hm-z&_-tYM~h8n$k*4fK{;ks>opNE;N#+1U#w4`^;yN&CyTT> zRaFWzwqeDZ0omav4m+N<4sBT(^b literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index c085ac1f2..c82549a04 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1073,4 +1073,6 @@ message.link.type.otherFile = Link to other SWF file (usually playerglobal.swc) message.link.type.otherFile.sample = String message.link.reallyGo = Do you really want to go to another SWF? -contextmenu.exportSubspriteAnimation = Export subsprite animation \ No newline at end of file +contextmenu.exportSubspriteAnimation = Export subsprite animation + +contextmenu.showInFlashPlayer = Show in the Flash Player \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index cc97b1d1f..883a0d915 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1072,4 +1072,6 @@ message.link.type.otherFile = Odkaz do jin\u00e9ho SWF souboru (obvykle playergl message.link.type.otherFile.sample = String message.link.reallyGo = Opravdu chcete p\u0159ej\u00edt do jin\u00e9ho SWF? -contextmenu.exportSubspriteAnimation = Export animace podsprit\u016f \ No newline at end of file +contextmenu.exportSubspriteAnimation = Export animace podsprit\u016f + +contextmenu.showInFlashPlayer = Zobrazit ve Flash Playeru \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/player/ZoomPanel.java b/src/com/jpexs/decompiler/flash/gui/player/ZoomPanel.java index 5811f491c..15f752bbc 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/ZoomPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/player/ZoomPanel.java @@ -79,7 +79,7 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { rulerButton.addActionListener(this::rulerActionPerformed); rulerButton.setToolTipText(AppStrings.translate("button.ruler.hint")); rulerButton.setSelected(Configuration.showRuler.get()); - + Configuration.showRuler.addListener(new ConfigurationItemChangeListener() { @Override public void configurationItemChanged(Boolean newValue) { @@ -114,7 +114,7 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { guidesOptionsButton.setToolTipText(AppStrings.translate("button.guides_options")); snapOptionsButton = new SnapOptionsButton(); - + PopupButton gridButton = new PopupButton(View.getIcon("grid16")) { @Override protected JPopupMenu getPopupMenu() { @@ -123,15 +123,15 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { showGridCheckBoxMenuItem.addActionListener(ZoomPanel.this::showGridCheckBoxMenuItemActionPerformed); showGridCheckBoxMenuItem.setSelected(Configuration.showGrid.get()); menu.add(showGridCheckBoxMenuItem); - + JMenuItem editGridMenuItem = new JMenuItem(AppStrings.translate("grid_options.edit")); editGridMenuItem.addActionListener(ZoomPanel.this::editGridMenuItemActionPerformed); menu.add(editGridMenuItem); - + return menu; - } + } }; - gridButton.setToolTipText(AppStrings.translate("button.grid_options")); + gridButton.setToolTipText(AppStrings.translate("button.grid_options")); percentLabel.setToolTipText(AppStrings.translate("zoom.hint")); @@ -152,11 +152,11 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { } catch (NumberFormatException nfe) { //ignore } - + } - } + } }); - + setLayout(new FlowLayout()); add(percentLabel); add(zoomInButton); @@ -170,12 +170,12 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { display.addEventListener(this); } - + private void editGridMenuItemActionPerformed(ActionEvent evt) { new GridDialog(Main.getDefaultDialogsOwner()).setVisible(true); } - + private void showGridCheckBoxMenuItemActionPerformed(ActionEvent evt) { JCheckBoxMenuItem source = (JCheckBoxMenuItem) evt.getSource(); Configuration.showGrid.set(source.isSelected()); @@ -194,7 +194,7 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { private void guidesEditActionPerformed(ActionEvent evt) { new GuidesDialog(Main.getDefaultDialogsOwner(), display).setVisible(true); } - + private void guidesClearActionPerformed(ActionEvent evt) { display.clearGuides(); } @@ -295,7 +295,7 @@ public class ZoomPanel extends JPanel implements MediaDisplayListener { zoomToFit = zoom.fit; realZoom = zoom.value; updateZoomDisplay(); - } + } }); } diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java index f6a3076a5..0cb1f0314 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTreeContextMenu.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.flash.gui.tagtree; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.ReadOnlyTagList; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFHeader; import com.jpexs.decompiler.flash.SWFInputStream; import com.jpexs.decompiler.flash.SWFOutputStream; import com.jpexs.decompiler.flash.TagRemoveListener; @@ -34,6 +35,7 @@ import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.CustomConfigurationKeys; import com.jpexs.decompiler.flash.configuration.SwfSpecificCustomConfiguration; +import com.jpexs.decompiler.flash.exporters.PreviewExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfFlashDevelopExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfIntelliJIdeaExporter; import com.jpexs.decompiler.flash.exporters.swf.SwfVsCodeExporter; @@ -99,16 +101,21 @@ import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DepthTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; import com.jpexs.decompiler.flash.tags.base.ImageTag; import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.PackedBinaryData; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.RemoveTag; import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; import com.jpexs.decompiler.flash.tags.base.SoundTag; import com.jpexs.decompiler.flash.tags.base.TextTag; import com.jpexs.decompiler.flash.tags.converters.PlaceObjectTypeConverter; import com.jpexs.decompiler.flash.tags.converters.ShapeTypeConverter; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo; import com.jpexs.decompiler.flash.timeline.AS2Package; import com.jpexs.decompiler.flash.timeline.AS3Package; @@ -140,14 +147,17 @@ import com.jpexs.helpers.Helper; import com.jpexs.helpers.LinkedIdentityHashSet; import com.jpexs.helpers.Reference; import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; @@ -202,7 +212,7 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem undoTagMenuItem; private JMenuItem exportSelectionMenuItem; - + private JMenuItem exportSubspriteAnimationMenuItem; private JMenuItem exportABCMenuItem; @@ -325,6 +335,8 @@ public class TagTreeContextMenu extends JPopupMenu { private JMenuItem showInEasyViewTagMenuItem; + private JMenuItem showInFlashPlayerMenuItem; + private JMenuItem showInFramesFolderMenuItem; private JMenuItem addFramesMenuItem; @@ -468,6 +480,11 @@ public class TagTreeContextMenu extends JPopupMenu { showInEasyViewTagMenuItem.setIcon(View.getIcon("easy16")); add(showInEasyViewTagMenuItem); + showInFlashPlayerMenuItem = new JMenuItem(mainPanel.translate("contextmenu.showInFlashPlayer")); + showInFlashPlayerMenuItem.addActionListener(this::showInFlashPlayerActionPerformed); + showInFlashPlayerMenuItem.setIcon(View.getIcon("playflash16")); + add(showInFlashPlayerMenuItem); + textSearchMenuItem = new JMenuItem(mainPanel.translate("menu.tools.search")); textSearchMenuItem.addActionListener(this::textSearchActionPerformed); textSearchMenuItem.setIcon(View.getIcon("search16")); @@ -484,7 +501,7 @@ public class TagTreeContextMenu extends JPopupMenu { }); exportSelectionMenuItem.setIcon(View.getIcon("exportsel16")); add(exportSelectionMenuItem); - + exportSubspriteAnimationMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportSubspriteAnimation")); exportSubspriteAnimationMenuItem.addActionListener(new ActionListener() { @Override @@ -494,7 +511,6 @@ public class TagTreeContextMenu extends JPopupMenu { }); exportSubspriteAnimationMenuItem.setIcon(View.getIcon("exportsubsprites16")); add(exportSubspriteAnimationMenuItem); - exportABCMenuItem = new JMenuItem(mainPanel.translate("contextmenu.exportAbc")); exportABCMenuItem.addActionListener(this::exportABCActionPerformed); @@ -1385,6 +1401,7 @@ public class TagTreeContextMenu extends JPopupMenu { showInTagListViewTagMenuItem.setVisible(false); showInHexDumpViewTagMenuItem.setVisible(false); showInEasyViewTagMenuItem.setVisible(false); + showInFlashPlayerMenuItem.setVisible(false); showInFramesFolderMenuItem.setVisible(false); addFramesMenuItem.setVisible(false); addFramesBeforeMenuItem.setVisible(false); @@ -1711,6 +1728,20 @@ public class TagTreeContextMenu extends JPopupMenu { if (firstItem instanceof Timelined) { showInEasyViewTagMenuItem.setVisible(true); } + if (((firstItem instanceof DrawableTag) + || (firstItem instanceof Frame) + || (firstItem instanceof TagScript) + || (firstItem instanceof SoundStreamHeadTypeTag) + || (firstItem instanceof DefineSoundTag) + || (firstItem instanceof DefineExternalSound) + || (firstItem instanceof DefineExternalStreamSound)) + && !(firstItem instanceof ImageTag) + && ((firstItem instanceof SoundStreamHeadTypeTag) + || (firstItem instanceof DefineExternalStreamSound) + || !((firstItem instanceof CharacterIdTag) && !(firstItem instanceof CharacterTag))) + && !(firstItem instanceof FontTag)) { + showInFlashPlayerMenuItem.setVisible(true); + } if ((firstItem instanceof Tag) || (firstItem instanceof CLIPACTIONRECORD) @@ -1789,7 +1820,7 @@ public class TagTreeContextMenu extends JPopupMenu { changeCharsetMenu.setVisible(true); } } - + if (firstItem instanceof Frame) { exportSubspriteAnimationMenuItem.setVisible(true); } @@ -4623,6 +4654,49 @@ public class TagTreeContextMenu extends JPopupMenu { mainPanel.updateMenu(); } + private void showInFlashPlayerActionPerformed(ActionEvent evt) { + File tempFile = null; + try { + tempFile = File.createTempFile("ffdec_view_", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.getSwfBackgroundColor(); + TreeItem treeItem = getCurrentItem(); + + if (treeItem instanceof PlaceObjectTypeTag) { + treeItem = Main.getMainFrame().getPanel().getCurrentTree().getFullModel().getParent(treeItem); //should be a frame + } else if (treeItem instanceof Tag) { + Tag tag = (Tag) treeItem; + if (tag instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.getDefaultBackgroundColor(); + } + } else if (treeItem instanceof Frame) { + Frame fn = (Frame) treeItem; + SWF sourceSwf = (SWF) fn.getOpenable(); + if (fn.timeline.timelined == sourceSwf) { + SetBackgroundColorTag setBgColorTag = sourceSwf.getBackgroundColor(); + if (setBgColorTag != null) { + backgroundColor = setBgColorTag.backgroundColor.toColor(); + } + } + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + new PreviewExporter().exportSwf(fos, treeItem, backgroundColor, 1/*fontPageNum ???*/, false); + } catch (ActionParseException ex) { + Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, null, ex); + } + if (!Main.runAsync(tempFile)) { + tempFile.delete(); + } + } catch (IOException ex) { + Logger.getLogger(TagTreeContextMenu.class.getName()).log(Level.SEVERE, null, ex); + if (tempFile != null && tempFile.exists()) { + tempFile.delete(); + } + } + } + private void moveTagActionPerformed(ActionEvent evt) { Tag t = (Tag) getCurrentItem(); TreePath path = getTree().getFullModel().getTreePath(t);