From cbcc2dc88a49788fadc2d5952e4f1dd3d8db67a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 2 Aug 2024 19:33:53 +0200 Subject: [PATCH] Added: #1644 Save all button - has priority over standard Save button Changed: Save to EXE moved to tools tab Changed: Save (not save as) button are now available only when there's anything to save when the selected SWF is modified. Similar for Save all button. --- CHANGELOG.md | 5 ++ .../src/com/jpexs/decompiler/flash/SWF.java | 25 +++++++ .../flash/tags/DefineBinaryDataTag.java | 2 +- .../decompiler/flash/gui/MainFrameMenu.java | 64 +++++++++++++++--- .../jpexs/decompiler/flash/gui/MainPanel.java | 10 +-- .../flash/gui/action/ActionPanel.java | 5 +- .../flash/gui/graphics/saveall16.png | Bin 0 -> 644 bytes .../flash/gui/graphics/saveall32.png | Bin 0 -> 1755 bytes .../flash/gui/locales/MainFrame.properties | 4 +- 9 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/saveall16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/saveall32.png diff --git a/CHANGELOG.md b/CHANGELOG.md index e22f765f6..e257efecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ All notable changes to this project will be documented in this file. - Export FLA context menu on SWFs - Window icons for various dialogs including save/open/export/import - [#873] Context menu items are organized with separators and the order is more intuitive +- [#1644] Save all button - has priority over standard Save button ### Fixed - Debugger - getting children of top level variables @@ -99,6 +100,9 @@ All notable changes to this project will be documented in this file. - Run/Debug command - executed SWF temp files (`~ffdec_run...swf` etc.) are now generated in the directory where original SWF resides to allow loading relative assets - [#2228] AS1/2/3 bitwise operations use hexadecimal operands +- Save to EXE moved to tools tab +- Save (not save as) button are now available only when there's anything to save + when the selected SWF is modified. Similar for Save all button. ### Removed - Proxy feature. It was not working since today almost every page uses HTTPS. Also Flash is limited in browsers. @@ -3467,6 +3471,7 @@ Major version of SWF to XML export changed to 2. [#2260]: https://www.free-decompiler.com/flash/issues/2260 [#1290]: https://www.free-decompiler.com/flash/issues/1290 [#873]: https://www.free-decompiler.com/flash/issues/873 +[#1644]: https://www.free-decompiler.com/flash/issues/1644 [#2149]: https://www.free-decompiler.com/flash/issues/2149 [#2172]: https://www.free-decompiler.com/flash/issues/2172 [#2174]: https://www.free-decompiler.com/flash/issues/2174 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 ab6a279d4..b2f33d5d3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -1244,6 +1244,31 @@ public final class SWF implements SWFContainerItem, Timelined, Openable { os.write(newCompressedData); } } + + public void saveNestedDefineBinaryData() { + Map chtags = getCharacters(false); + for (CharacterTag t : chtags.values()) { + if (t instanceof DefineBinaryDataTag) { + DefineBinaryDataTag dbd = (DefineBinaryDataTag) t; + if (dbd.innerSwf != null && dbd.innerSwf.isModified()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + SWF swf = (SWF) dbd.innerSwf; + swf.saveNestedDefineBinaryData(); + swf.saveTo(baos); + byte[] data = baos.toByteArray(); + swf.binaryData.setDataBytes(new ByteArrayRange(data)); + swf.binaryData.setModified(true); + swf.binaryData.getTopLevelBinaryData().pack(); + dbd.innerSwf.clearModified(); + } catch (IOException ex) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); + } + dbd.setModified(true); + } + } + } + } public byte[] getHeaderBytes() { return getHeaderBytes(compression, gfx, encrypted); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBinaryDataTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBinaryDataTag.java index 6e922c6e9..dccde2baa 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBinaryDataTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineBinaryDataTag.java @@ -280,5 +280,5 @@ public class DefineBinaryDataTag extends CharacterTag implements BinaryDataInter @Override public String getClassExportFileName(String className) { return className; - } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java index 05e159839..6ddf3c986 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameMenu.java @@ -145,6 +145,12 @@ public abstract class MainFrameMenu implements MenuBuilder { Main.startSaving(savedFile); Bundle bundle = openable.getOpenableList().bundle; if (!bundle.isReadOnly()) { + + if (openable instanceof SWF) { + SWF swf = (SWF) openable; + swf.saveNestedDefineBinaryData(); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { openable.saveTo(baos); @@ -159,8 +165,9 @@ public abstract class MainFrameMenu implements MenuBuilder { } else if ((openable instanceof SWF) && ((SWF) openable).binaryData != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - openable.saveTo(baos); SWF swf = (SWF) openable; + swf.saveNestedDefineBinaryData(); + swf.saveTo(baos); byte[] data = baos.toByteArray(); swf.binaryData.setDataBytes(new ByteArrayRange(data)); swf.binaryData.setModified(true); @@ -172,6 +179,10 @@ public abstract class MainFrameMenu implements MenuBuilder { } else if (openable.getFile() == null) { saved = saveAs(openable, SaveFileMode.SAVEAS); } else { + if (openable instanceof SWF) { + SWF swf = (SWF) openable; + swf.saveNestedDefineBinaryData(); + } try { Main.saveFile(openable, openable.getFile()); saved = true; @@ -203,6 +214,30 @@ public abstract class MainFrameMenu implements MenuBuilder { } return saveOpenable(openable); } + + protected boolean saveAllActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return false; + } + if (mainFrame.getPanel().checkEdited()) { + return false; + } + List openableLists = mainFrame.getPanel().getSwfs(); + List allOpenables = new ArrayList<>(); + for (OpenableList list : openableLists) { + for (Openable openable : list.items) { + allOpenables.add(openable); + } + } + for (Openable openable : allOpenables) { + if (openable.isModified()) { + if (!saveOpenable(openable)) { + return false; + } + } + } + return true; + } protected boolean saveAsActionPerformed(ActionEvent evt) { if (Main.isWorking()) { @@ -984,6 +1019,8 @@ public abstract class MainFrameMenu implements MenuBuilder { MainPanel mainPanel = mainFrame.getPanel(); boolean swfLoaded = mainPanel != null ? !mainPanel.getSwfs().isEmpty() : false; boolean swfIsNew = openableSelected && openable.getOpenableList() != null && openable.getOpenableList().sourceInfo.isEmpty(); + boolean anythingModified = mainPanel != null ? mainPanel.isModified() : false; + boolean swfModified = swf == null ? false : swf.isModified(); boolean allSameSwf = true; boolean allSameOpenable = true; @@ -1026,11 +1063,12 @@ public abstract class MainFrameMenu implements MenuBuilder { setMenuEnabled("_/open", !isWorking); setMenuEnabled("/file/open", !isWorking); - setMenuEnabled("_/save", openableSelected && !isWorking); - setMenuEnabled("/file/save", openableSelected && !isWorking); + setMenuEnabled("_/save", openableSelected && !isWorking && swfModified); + setMenuEnabled("/file/save", openableSelected && !isWorking && swfModified); setMenuEnabled("_/saveAs", openableSelected && !isWorking); setMenuEnabled("/file/saveAs", openableSelected && !isWorking); - setMenuEnabled("/file/saveAsExe", swfSelected && !isWorking); + setMenuEnabled("_/saveAll", !isWorking && anythingModified); + setMenuEnabled("/file/saveAll", !isWorking && anythingModified); setMenuEnabled("_/close", openableOrListSelected && !isWorking); setMenuEnabled("/file/close", openableOrListSelected && !isWorking); setMenuEnabled("_/closeAll", swfLoaded && !isWorking); @@ -1074,6 +1112,8 @@ public abstract class MainFrameMenu implements MenuBuilder { setMenuEnabled("/tools/abcExplorer", isAs3); setMenuEnabled("/tools/gotoDocumentClass", hasAbc); + setMenuEnabled("/tools/saveAsExe", swfSelected && !isWorking); + /*setMenuEnabled("/tools/debugger/debuggerSwitch", hasAbc); setMenuChecked("/tools/debugger/debuggerSwitch", hasDebugger); setMenuEnabled("/tools/debugger/debuggerReplaceTrace", hasAbc && hasDebugger);*/ @@ -1139,6 +1179,7 @@ public abstract class MainFrameMenu implements MenuBuilder { addMenuItem("_", null, null, null, 0, null, false, null, false); addMenuItem("_/open", translate("menu.file.open"), "open32", this::openActionPerformed, PRIORITY_TOP, this::loadRecent, false, null, false); addMenuItem("_/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("_/saveAll", translate("menu.file.saveAll"), "saveall32", this::saveAllActionPerformed, PRIORITY_TOP, null, true, null, false); addMenuItem("_/saveAs", translate("menu.file.saveas"), "saveas32", this::saveAsActionPerformed, PRIORITY_TOP, null, true, null, false); addSeparator("_"); addMenuItem("_/exportFla", translate("menu.file.export.fla"), "exportfla32", this::exportFlaActionPerformed, PRIORITY_TOP, null, true, null, false); @@ -1165,13 +1206,15 @@ public abstract class MainFrameMenu implements MenuBuilder { finishMenu("/file/open"); } - addMenuItem("/file/save", translate("menu.file.save"), "save32", this::saveActionPerformed, PRIORITY_TOP, null, true, new HotKey("CTRL+SHIFT+S"), false); + addMenuItem("/file/new", translate("menu.file.new"), "newswf32", this::newActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/saveAll", translate("menu.file.saveAll"), "saveall32", this::saveAllActionPerformed, PRIORITY_TOP, null, true, null, false); + addMenuItem("/file/save", translate("menu.file.save"), "save16", this::saveActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+S"), false); addMenuItem("/file/saveAs", translate("menu.file.saveas"), "saveas16", this::saveAsActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+A"), false); - addMenuItem("/file/saveAsExe", translate("menu.file.saveasexe"), "saveasexe16", this::saveAsExeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); addMenuItem("/file/reload", translate("menu.file.reload"), "reload16", this::reloadActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+R"), false); addMenuItem("/file/reloadAll", translate("menu.file.reloadAll"), "reload16", this::reloadAllActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/new", translate("menu.file.new"), "newswf32", this::newActionPerformed, PRIORITY_TOP, null, true, null, false); - + addMenuItem("/file/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); + addMenuItem("/file/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+X"), false); + addSeparator("/file"); addMenuItem("/file/export", translate("menu.export"), null, null, 0, null, false, null, false); @@ -1212,9 +1255,7 @@ public abstract class MainFrameMenu implements MenuBuilder { finishMenu("/file/view"); addSeparator("/file"); - addMenuItem("/file/close", translate("menu.file.close"), "close32", this::closeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); - addMenuItem("/file/closeAll", translate("menu.file.closeAll"), "closeall32", this::closeAllActionPerformed, PRIORITY_MEDIUM, null, true, new HotKey("CTRL+SHIFT+X"), false); - + if (!supportsAppMenu()) { addSeparator("/file"); addMenuItem("/file/exit", translate("menu.file.exit"), "exit32", this::exitActionPerformed, PRIORITY_TOP, null, true, null, false); @@ -1268,6 +1309,7 @@ public abstract class MainFrameMenu implements MenuBuilder { if (Platform.isWindows()) { addMenuItem("/tools/searchMemory", translate("menu.tools.searchMemory"), "loadmemory16", this::searchMemoryActionPerformed, PRIORITY_MEDIUM, null, true, null, false); } + addMenuItem("/tools/saveAsExe", translate("menu.file.saveasexe"), "saveasexe16", this::saveAsExeActionPerformed, PRIORITY_MEDIUM, null, true, null, false); //addMenuItem("/tools/searchCache", translate("menu.tools.searchCache"), "loadcache16", this::searchCacheActionPerformed, PRIORITY_MEDIUM, null, true, null); addMenuItem("/tools/deobfuscation", translate("menu.tools.deobfuscation"), "deobfuscate16", null, 0, null, false, null, false); diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 1c6bafc76..0e5a2969e 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -4624,6 +4624,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se reload(true); updateMissingNeededCharacters(); pinsPanel.refresh(); + updateUiWithCurrentOpenable(); } public void refreshDecompiled() { @@ -4921,8 +4922,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se try { Tag newTag = new ImageImporter().importImage(it, data, create ? -1 : 0); SWF swf = it.getSwf(); - if (newTag != null) { - refreshTree(swf); + refreshTree(swf); + if (newTag != null) { setTagTreeSelectedNode(getCurrentTree(), newTag); } swf.clearImageCache(); @@ -4963,8 +4964,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } } SWF swf = st.getSwf(); - if (newTag != null) { - refreshTree(swf); + refreshTree(swf); + if (newTag != null) { setTagTreeSelectedNode(getCurrentTree(), newTag); } @@ -6190,6 +6191,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se tagTree.repaint(); tagListTree.repaint(); reload(true); + updateUiWithCurrentOpenable(); } public void showGenericTag(Tag tag) { diff --git a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java index a815ff2b3..acc8d0737 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/action/ActionPanel.java @@ -1300,7 +1300,7 @@ public class ActionPanel extends JPanel implements SearchListenerJL9*r8@s}q4`$|@{pLO1`(~D93ooZ!VUD0nB)s-hVHoG*YAjuRtzKLn#*uV9 z_|qzI=HV=nILu%KU<@o$7yhI?7tQ863Ll$zGXE`>$l(d40dD(-;Kn$M64Y%ELnbu% z*@Y7r-p^x6KGcA-ld}ab-Eqbu1nVt{ndNr)!9%t28@^C(1gHF2P%hA;_nwph?R`6% zV8c_GTJm(UL1aTF!|l)iw?j==ZeV9x6WETzKr)c+t9kXSt-;SP=8?}Bu_W(n3g;&0 zMl=Cousf+!Y?7MkPz`y-%FhirO1D2iDuo17kZ#Xb55PFm$!$8d-7PhBTVjyXw{Pai zbEt&;pUmfV<23^xp_@L-l|uWgBXJY4e0b52MQYT1F zg_a668g|&+Ov=KZUK9KJQ`o!90#^>QDWPln2!Uen*)D&*r?R=xZht9nR!Xc(Jwqdl e?y>qufB^u8JlMJ>T_%tK0000-2*3W%u)CFOP zKmxfCp#NbV20Nf1Q;-=3Y!SBp(^xl`Zs&&6-y%!!;0KpJ3I>oR1T$_oF_NSGOaO-v zFo4-xzpi7SWS52uzzaeN*9b9v*9ioE2)VKx$4>M>iXnPSiRn|zFmu;&n6`nV&rb#W zVxPkWz!4Pmhm153v~5E&VW|g+go~nNlG+{;9$nL!ElJnh=KE}X6eVl~-);v*lDz@( z?=T~vj&wFa$)qYcoKQOUZr=#ovQU{S$0POgv18LasI00&NNK96U!dESDcrrV0mZHb zYw|Q?f)S0KM|$hHwFl1bTT+W92fC2WNpvl}6N~o#jy=b^A!JB{O8u(TVOK`^20ddl zDgTBc=shMPVAHeV64Q!N^bz%4At4Mk3O?=l8$Z15wjDAUa!BY#Ox1%Qy_#! zPe8K1CZ@$&#rKGqMP`6|ciuc=8u2^iPYuQhJyYLKhfwt*7 zax|7THWPqo>7YnTsUysm&4v9_z+tyc3ndB72Of|Y7Zm}n0w$-Sy~m0f=@quDU4c|Z zCF-AQz?LjiqdCe-~Llq&)VbS6S6U zh?ty$LSk}Mv;&Ei>zb*Ar3(L>>((p2C_Yg8t)}&n-fO=Jt}yDG)g_n*knL)$s~JQvk$eClJSZHy_2N(44Gfh;#I$` z^D2^#sr<0A7INSU*{R;P-Ty|56Za}Dp0_+tObs9sEU6WfHMOO^msImV@Ps7f4K^IQ zeA!QNw5AkLKmqW{E|B#W0Pyp~q>))2T_a9`UbF->KQh$z2|;)RIW!chMO5P2`Sk$e zSOt-(s*H(Q>gk%kl(rPX0;!$FD`>+cfrZr*uGijvtSZr+BjKe>-i70 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 cd39c31b3..dc910d8f3 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1293,4 +1293,6 @@ menu.file.export.idea = Export IDEA project contextmenu.exportFla = Export to FLA -export.project.select.directory = Select location of the new project directory \ No newline at end of file +export.project.select.directory = Select location of the new project directory + +menu.file.saveAll = Save all \ No newline at end of file