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 000000000..773bd4e08 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim.swf differ diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/DOMDocument.xml b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/DOMDocument.xml new file mode 100644 index 000000000..d2fe5d6b2 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/DOMDocument.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 000000000..640ea9018 Binary files /dev/null and b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/bin/SymDepend.cache differ diff --git a/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/subsprite_anim.xfl b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/subsprite_anim.xfl new file mode 100644 index 000000000..860a820ec --- /dev/null +++ b/libsrc/ffdec_lib/testdata/subsprite_anim/subsprite_anim/subsprite_anim.xfl @@ -0,0 +1 @@ +PROXY-CS5 \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 490dd29e1..88c602015 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -517,6 +517,7 @@ public class CommandLineArgumentParser { List 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].