From 840ae0376eb9327f8f84eae21de82e07deee8f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 9 Nov 2025 12:20:25 +0100 Subject: [PATCH] Added: #2556 CLI subsprite animation export (`-sublength ` option for export) Added: #2556 Option to select arbitrary frames of sprites export via `-select`. Check `--help -select` for details. --- CHANGELOG.md | 4 + .../subsprite_anim/subsprite_anim.html | 49 +++++ .../subsprite_anim/subsprite_anim.swf | Bin 0 -> 930 bytes .../subsprite_anim/DOMDocument.xml | 33 +++ .../subsprite_anim/LIBRARY/Circle.xml | 36 +++ .../subsprite_anim/LIBRARY/Square.xml | 35 +++ .../subsprite_anim/LIBRARY/Symbol 1.xml | 29 +++ .../subsprite_anim/LIBRARY/Tween 1.xml | 38 ++++ .../subsprite_anim/LIBRARY/Tween 2.xml | 39 ++++ .../subsprite_anim/META-INF/metadata.xml | 55 +++++ .../subsprite_anim/MobileSettings.xml | 0 .../subsprite_anim/PublishSettings.xml | 206 ++++++++++++++++++ .../subsprite_anim/bin/SymDepend.cache | Bin 0 -> 118 bytes .../subsprite_anim/subsprite_anim.xfl | 1 + .../console/CommandLineArgumentParser.java | 138 ++++++++++-- .../jpexs/decompiler/flash/console/help.txt | 32 ++- 16 files changed, 669 insertions(+), 26 deletions(-) create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.html create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.swf create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/DOMDocument.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Circle.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Square.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Symbol 1.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 1.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 2.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/META-INF/metadata.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/MobileSettings.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/PublishSettings.xml create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/bin/SymDepend.cache create mode 100644 libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/subsprite_anim.xfl diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c5584e83..63df70923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Added - AS3 Document class is underlined and has different icon +- [#2556] CLI subsprite animation export (`-sublength ` option for export) +- [#2556] Option to select arbitrary frames of sprites export via `-select`. + Check `--help -select` for details. ### Fixed - [#2536] AS3 switches detection incorrectly replaces basic ifs with strict equals @@ -4023,6 +4026,7 @@ Major version of SWF to XML export changed to 2. [alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 [alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 +[#2556]: https://www.free-decompiler.com/flash/issues/2556 [#2536]: https://www.free-decompiler.com/flash/issues/2536 [#2537]: https://www.free-decompiler.com/flash/issues/2537 [#2540]: https://www.free-decompiler.com/flash/issues/2540 diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.html b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.html new file mode 100644 index 000000000..e54269feb --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.html @@ -0,0 +1,49 @@ + + + + subsprite_anim + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + Get Adobe Flash player + + + + + +
+ + diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.swf b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.swf new file mode 100644 index 0000000000000000000000000000000000000000..773bd4e08de6192c00452d311eb21bda59a02974 GIT binary patch literal 930 zcmV;T16}+>S5pu52LJ$goW+!Hh}&cw$A9Uql|!bRo=2_j2%X?m(j?8*wV_wrOIO&7 zdV0Hpe1eC6J%69(star#O`!9N2L^^M5+ zYC?UI_33q6o#%XE+oYf8m*4lB8$s|WQIJa$jsKuG??s$$39E#Xg9o_bH z%VhmEnztI>R6O3ZZPo3*t?4F^X3gV-YSsA`vUxEf@V*aek`)Id&vNE0%g7b{F1BPK zx5hr^SXJ!0#2y)}a^PW>W9J*Xp|O)`G0R`89Pq+LvgMLOGR27^CuHV?q&&G-mIO{n z%YrbVz0!F_TE-sIkcWndQ-UmI2E|wP_T-kPSKC_>Z+5a-{+cAWmaV4tqqQ535zI?d zd2HZ1Ha(ZbD+cjus$8|4I`VSJwhdiHz9HY@+?u5xZlWWYtNKxA`OU_gy(DsHjqsU)9^={@4WF!LuaB2OK;@ZZ=MJ2uQ;RnC9b(oc#|F#}h187)&oh@=U$iIkp%3zK2Cc0|+do!dtdfuHT7G=_hwsCcJX4uS zH*R@#;*75veS(~18%)6S{s~85eErGK@~|8UGcbPc8=K + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Circle.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Circle.xml new file mode 100644 index 000000000..708860b21 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Circle.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Square.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Square.xml new file mode 100644 index 000000000..d2ed06788 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Square.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Symbol 1.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Symbol 1.xml new file mode 100644 index 000000000..e2b3b5422 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Symbol 1.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 1.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 1.xml new file mode 100644 index 000000000..0727b42a2 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 1.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 2.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 2.xml new file mode 100644 index 000000000..6e742a9af --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/LIBRARY/Tween 2.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/META-INF/metadata.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/META-INF/metadata.xml new file mode 100644 index 000000000..7fc6334f7 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/META-INF/metadata.xml @@ -0,0 +1,55 @@ + + + + + Adobe Flash Professional CS6 - build 481 + 2025-11-09T02:47:30-08:00 + 2025-11-09T02:50:37-08:00 + 2025-11-09T02:50:37-08:00 + + + application/vnd.adobe.fla + + + xmp.iid:5B3CCAEC59BDF0118295F1B0A0E387A5 + xmp.did:5B3CCAEC59BDF0118295F1B0A0E387A5 + xmp.did:5B3CCAEC59BDF0118295F1B0A0E387A5 + + + + created + xmp.iid:5B3CCAEC59BDF0118295F1B0A0E387A5 + 2025-11-09T02:47:30-08:00 + Adobe Flash Professional CS6 - build 481 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/MobileSettings.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/MobileSettings.xml new file mode 100644 index 000000000..e69de29bb diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/PublishSettings.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/PublishSettings.xml new file mode 100644 index 000000000..3a90ffc27 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/PublishSettings.xml @@ -0,0 +1,206 @@ + + + + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + Untitled-1.swf + Untitled-1.exe + Untitled-1.app + Untitled-1.html + Untitled-1.gif + Untitled-1.jpg + Untitled-1.png + Untitled-1.mov + Untitled-1.smil + Untitled-1.swc + + + 0 + 12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1; + 1 + 1 + Untitled-1_content.html + Untitled-1_alternate.html + 0 + + 550 + 400 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 4 + 0 + 0 + 1 + 0 + C:\Users\MyUser\AppData\Local\Adobe\Flash CS6\en_US\Configuration\HTML\Default.html + 1 + + + + + 0 + 0 + 0 + 80 + 0 + 0 + 7 + 0 + 7 + 0 + 15 + FlashPlayer11.2 + 3 + 1 + + . + CONFIG::FLASH_AUTHORING="true"; + 0 + + 1 + 0 + 1 + 0 + 0 + 0 + 0 + + 2 + 4 + 4096 + AS3 + 1 + 1 + 0 + 15 + 1 + 0 + 4102 + rsl + wrap + $(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf + + + $(AppConfig)/ActionScript 3.0/libs + merge + + + $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc + rsl + http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz + http://fpdownload.adobe.com/pub/swz/crossdomain.xml + textLayout_2.0.0.232.swz + + + + + $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc + + http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz + http://fpdownload.adobe.com/pub/swz/crossdomain.xml + textLayout_2.0.0.232.swz + + + + + 550 + 400 + 0 + 4718592 + 0 + 80 + 1 + + + 1 + 0 + 1 + 0 + 0 + 100000 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + + 550 + 400 + 0 + 1 + 1 + + 1 + 0 + 1 + 0 + 0 + + 128 + + + 255 + + + + 550 + 400 + 1 + 0 + 0 + 1 + 0 + 0 + 1 + + + + 24-bit with Alpha + 255 + + + + 550 + 400 + 1 + 0 + + + 00000000 + 0 + 0 + 0 + 0 + 1 + + + \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/bin/SymDepend.cache b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/bin/SymDepend.cache new file mode 100644 index 0000000000000000000000000000000000000000..640ea90187da86e455c9fd86821acae08578d175 GIT binary patch literal 118 zcmYdiU|@L3&J83v8JHN17*v5QRv_kJ2rev5EJ|fC selectionClasses = null; String nextParam = null; String nextParamOriginal = null; + int subLength = 1; OUTER: while (true) { nextParamOriginal = args.pop(); @@ -534,14 +535,17 @@ public class CommandLineArgumentParser { cliMode = true; break; case "-selectid": - selectionIds = parseSelect(args); + selectionIds = parseSelect(args, false, "selectid"); break; case "-select": - selection = parseSelect(args); + selection = parseSelect(args, true, "select"); break; case "-selectclass": selectionClasses = parseSelectClass(args); break; + case "-sublength": + subLength = parseSubLength(args); + break; case "-exportembed": exportEmbed = true; break; @@ -677,7 +681,7 @@ public class CommandLineArgumentParser { } else if (command.equals("proxy")) { parseProxy(args); } else if (command.equals("export")) { - parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed, resampleWav, transparentBackground, urlResolver); + parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed, resampleWav, transparentBackground, urlResolver, subLength); System.exit(0); } else if (command.equals("compress")) { parseCompress(args); @@ -1584,16 +1588,29 @@ public class CommandLineArgumentParser { private static class Range { + public Integer prefix; + public Integer min; public Integer max; - public Range(Integer min, Integer max) { + public Range(Integer prefix, Integer min, Integer max) { + this.prefix = prefix; this.min = min; this.max = max; } public boolean contains(int index) { + return contains(0, index); + } + + public boolean contains(int prefix, int index) { + if (this.prefix == null && prefix == 0) { + return false; + } + if (this.prefix != null && this.prefix != prefix) { + return false; + } int minimum = min == null ? Integer.MIN_VALUE : min; int maximum = max == null ? Integer.MAX_VALUE : max; @@ -1607,7 +1624,7 @@ public class CommandLineArgumentParser { public Selection() { this.ranges = new ArrayList<>(); - this.ranges.add(new Range(null, null)); + this.ranges.add(new Range(0, null, null)); } public Selection(List ranges) { @@ -1615,36 +1632,76 @@ public class CommandLineArgumentParser { } public boolean contains(int index) { + return contains(0, index); + } + + public boolean contains(int prefix, int index) { + boolean prefixFound = false; for (Range r : ranges) { - if (r.contains(index)) { + if (r.prefix == prefix || (prefix != 0 && r.prefix == null)) { + prefixFound = true; + } + if (r.contains(prefix, index)) { return true; } } + if (!prefixFound) { + return true; + } return false; } } - private static Selection parseSelect(Stack args) { + private static Selection parseSelect(Stack args, boolean allowPrefix, String command) { List ret = new ArrayList<>(); if (args.isEmpty()) { System.err.println("range parameter expected"); - badArguments("select"); + badArguments(command); } String range = args.pop(); String[] ranges; if (range.contains(",")) { - ranges = range.split(","); + ranges = range.split(",", -1); } else { ranges = new String[]{range}; } + + Integer prefix = 0; + for (String r : ranges) { Integer min = null; Integer max = null; - if (r.contains("-")) { - String[] ps = r.split("\\-"); + if (r.contains(":")) { + if (!allowPrefix) { + System.err.println("invalid range"); + badArguments(command); + } + String[] ps = r.split(":", -1); if (ps.length != 2) { System.err.println("invalid range"); - badArguments("select"); + badArguments(command); + } + if ("all".equals(ps[0])) { + prefix = null; + } else { + try { + prefix = Integer.parseInt(ps[0]); + } catch (NumberFormatException nfe) { + System.err.println("invalid range"); + badArguments(command); + } + if (prefix < 0) { + System.err.println("invalid range"); + badArguments(command); + } + } + r = ps[1]; + } + if (r.contains("-")) { + String[] ps = r.split("\\-", -1); + if (ps.length != 2) { + System.err.println("invalid range"); + badArguments(command); } try { if (!"".equals(ps[0])) { @@ -1655,7 +1712,7 @@ public class CommandLineArgumentParser { } } catch (NumberFormatException nfe) { System.err.println("invalid range"); - badArguments("select"); + badArguments(command); } } else { try { @@ -1663,13 +1720,33 @@ public class CommandLineArgumentParser { max = min; } catch (NumberFormatException nfe) { System.err.println("invalid range"); - badArguments("select"); + badArguments(command); } } - ret.add(new Range(min, max)); + ret.add(new Range(prefix, min, max)); } return new Selection(ret); } + + private static int parseSubLength(Stack args) { + if (args.isEmpty()) { + System.err.println("sub length parameter expected"); + badArguments("sublength"); + } + try { + int subLen = Integer.parseInt(args.pop()); + if (subLen < 1) { + System.err.println("Minimum for sub length is 1"); + badArguments("sublength"); + subLen = 1; + } + return subLen; + } catch (NumberFormatException nre) { + System.err.println("Invalid sub length"); + badArguments("sublength"); + } + return 1; + } private static double parseZoom(Stack args) { if (args.isEmpty()) { @@ -2033,7 +2110,21 @@ public class CommandLineArgumentParser { } - private static void parseExport(List selectionClasses, Selection selection, Selection selectionIds, Stack args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats, double zoom, String charset, boolean exportEmbed, boolean resampleWav, boolean transparentBackground, UrlResolver urlResolver) { + private static void parseExport( + List selectionClasses, + Selection selection, + Selection selectionIds, + Stack args, + AbortRetryIgnoreHandler handler, + Level traceLevel, + Map formats, + double zoom, + String charset, + boolean exportEmbed, + boolean resampleWav, + boolean transparentBackground, + UrlResolver urlResolver, + int subFrameLength) { if (args.size() < 3) { badArguments("export"); } @@ -2258,20 +2349,27 @@ public class CommandLineArgumentParser { System.out.println("Exporting frames..."); List frames = new ArrayList<>(); for (int i = 0; i < swf.frameCount; i++) { - if (selection.contains(i + 1)) { + if (selection.contains(0, i + 1)) { frames.add(i); } } FrameExportSettings fes = new FrameExportSettings(enumFromStr(formats.get("frame"), FrameExportMode.class), zoom, transparentBackground); - frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + FrameExportSettings.EXPORT_FOLDER_NAME : ""), swf, 0, frames, 1, fes, evl); + frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + FrameExportSettings.EXPORT_FOLDER_NAME : ""), swf, 0, frames, subFrameLength, fes, evl); } if (exportAll || exportFormats.contains("sprite")) { System.out.println("Exporting sprite..."); SpriteExportSettings ses = new SpriteExportSettings(enumFromStr(formats.get("sprite"), SpriteExportMode.class), zoom); for (Tag t : extags) { - if (t instanceof DefineSpriteTag) { - frameExporter.exportSpriteFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, ((DefineSpriteTag) t).getCharacterId(), null, 1, ses, evl); + if (t instanceof DefineSpriteTag) { + List frames = new ArrayList<>(); + int spriteId = ((DefineSpriteTag) t).getCharacterId(); + for (int i = 0; i < ((DefineSpriteTag) t).getFrameCount(); i++) { + if (selection.contains(spriteId, i + 1)) { + frames.add(i); + } + } + frameExporter.exportSpriteFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, spriteId, frames, subFrameLength, ses, evl); } } } diff --git a/src/com/jpexs/decompiler/flash/console/help.txt b/src/com/jpexs/decompiler/flash/console/help.txt index 31721d1bf..2725f8aff 100644 --- a/src/com/jpexs/decompiler/flash/console/help.txt +++ b/src/com/jpexs/decompiler/flash/console/help.txt @@ -378,15 +378,30 @@ Pre-options: -select Applies to: -export Select frames/pages for export. + Comma separated ranges in format - ( is optional) or . + If it has prefix ":" then all following ranges apply + to the character with specific id. + Use "all" as characterId to apply for every character. + Use "0" (default) as characterId to apply for main timeline. Example formats: - 1-5 - 2,3 - 2-5,7,9- - DO NOT PUT space between comma (,) and next ramge. + 1-5 ... exports frames 1,2,3,4,5 + 2,3 ... exports frames 2 and 3 + 2-5,7,9- ... exports frames 2,3,4,5,7,9 and all after + 57:5-10,12,62:5 ... exports frames 5,6,7,8,9,10,12 + of character 57 and frame 5 of character 62 + all:12-29 ... exports frames 12 to 29 of all characters + 12:3,4,0:1-12 ... exports frames 3 and 4 of character 12 + and first 12 frames of main timeline + DO NOT PUT space between comma (,) and next range. --selectid +-selectid + Applies to: -export Select characters for export by character id. - format is same as in -select + Comma separated ranges in format - ( is optional) or . + Example formats: + 1-5 ... exports characters 1,2,3,4,5 + 2,3 ... exports characters 2 and 3 + 2-5,7,9- ... exports characters 2,3,4,5,7,9 and all ids after -selectclass Applies to: -export @@ -398,6 +413,11 @@ Pre-options: subpackages, class net.company.MyClass) DO NOT PUT space between comma (,) and next class. +-sublength + Applies to: -export + Export subsprite animation of length . + is measured in number of subframes. Must be >= 1. + -exportembed Applies to: -export Allow exporting embedded assets via [Embed tag].