diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90e3e261e..e4b45d5e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,14 +35,14 @@ All notable changes to this project will be documented in this file.
- font normalizer uses 1024em ([#2661])
- null pointer on null caret in variable marker
- push commands in as2 left in code ([#2654])
-- deadlock on getcharacters vs drawframe ([#2492])
+- deadlock on getCharacters vs drawFrame ([#2492])
- nullpointer on as3 deobfuscation
- as3 deobfuscation - null values instead of registers ([#2568], [#2665])
- stackoverflow on circular importassets ([#2666])
- allow add breakpoints on large classes ([#2672])
- unable to reset JNA temp directory ([#2675])
- svg shape export - use proper winding
-- properly normalize fonts in defineedittexts, kerning
+- properly normalize fonts in DefineEditTexts, kerning
- properly draw edittext border, normalize size
- illegal argument exception on creating morphshape from svg ([#2676])
- respect nofill argument in shape CLI SVG export ([#2681])
diff --git a/README.md b/README.md
index f4ec9a9e1..d617f8bdd 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# JPEXS Free Flash Decompiler

+English | [简体中文](README_zh.md)
+
Open Source Flash SWF decompiler and editor. Extract resources, convert SWF to FLA, edit ActionScript, replace images, sounds, texts and fonts. Various output formats available. Works with Java on Windows, Linux and macOS.
## Application description and features
@@ -120,7 +122,7 @@ The application was made in Czech Republic.
### Translators
* **Jaume Badiella Aguilera** - catalan translation
* **Capasha** - swedish translation
-* **王晨旭** (Chenxu Wang), **晓之车**, **安安**, **流水渺渺**, **老biu** - chinese translation
+* **王晨旭** (Chenxu Wang), **晓之车**, **安安**, **Liushui**, **老biu** - chinese translation
* **focus** - russian translation
* **honfika** - hungarian translation
* **kalip** - italian translation
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 000000000..f00431a4c
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,186 @@
+# JPEXS 免费 Flash 反编译器
+
+
+[English](README.md) | 简体中文
+
+这是一款开源的 Flash SWF 反编译器和编辑器。能够提取资源,将 SWF 文件转换为 FLA 文件,编辑 ActionScript 代码,替换图片、声音、文本和字体。能够提供多种输出格式。通过 Java 环境运行于 Windows、Linux 和 macOS 系统上。
+
+## 应用描述与功能
+有关软件使用方法、功能列表等信息,请访问 [FFDec 维基](https://github.com/jindrapetrik/jpexs-decompiler/wiki)。
+
+## Free-Decompiler.com 网站
+过去(2018 年之前),我们使用 *free-decompiler.com* 域名作为主页,源代码则存储在 GitHub 上。现在,我们已将所有信息(问题跟踪器除外)迁移到了 GitHub。
+
+## 下载应用
+如需下载该应用程序,请前往 [latest release](https://github.com/jindrapetrik/jpexs-decompiler/releases/latest) 页面。
+旧版本和每夜构建版本可在 [releases](https://github.com/jindrapetrik/jpexs-decompiler/releases) 页面获取。
+
+### 如何安装
+请参阅 [维基百科](https://github.com/jindrapetrik/jpexs-decompiler/wiki/Installation) 的安装部分。
+
+## 源代码
+### 如何获取源代码
+您可以使用以下命令创建源代码的本地副本:
+```
+git clone https://github.com/jindrapetrik/jpexs-decompiler.git
+```
+此操作假定您的系统中已经安装了 Git 。
+
+### 分支
+Git 版本控制管理器管理着多个代码分支。我们使用其中两个主要分支。
+
+* `master` - 已发布的 “稳定” 版本
+* `dev` - 开发者的最新更改 - “每夜构建”版本从此分支发布
+
+您可以使用以下 git 命令切换到 `dev` 分支:
+```
+git checkout dev
+```
+
+### GIT 建议
+建议安装 [GIT] 命令行可执行文件。构建脚本使用 GIT 将版本号包含在二进制文件中。(对于 Windows 系统,您必须在 GIT 安装过程中配置 Windows 命令行。)
+
+### Netbeans 工程
+
+源代码包含 Netbeans 项目,因此您可以在 [Netbeans IDE] 中打开它。然后,您可以使用 IDE 中诸如 “运行”、“构建”、“调试”、“清理” 和 “构建” 等标准操作。其他特定任务可以通过 build.xml 中的菜单执行(参见 Ant 部分)。
+
+### Ant
+如果没有 Netbeans,您也可以使用 Apache Ant 构建源代码。
+安装 Ant 之后,将最好其添加到您的 PATH 变量中。
+打开命令行并导航到源代码目录,
+要运行应用程序,请输入以下命令执行 “运行” 任务:
+```
+ant run
+```
+若仅需构建应用程序,请执行 “构建” 任务:
+```
+ant build
+```
+
+### 构建库
+
+还有一些库也需要构建。这些库放在 “libsrc” 目录中。
+* **FFDec_lib** - 进行反编译、SWF 解析、导出等核心功能。**该库会随主项目自动构建,但也可以使用其自身的 Ant 脚本单独构建。**
+* **jpacker** - 用于压缩 JavaScript Canvas 的脚本(Netbeans/Ant 工程)
+* **jpproxy** - FFDec 的代理部分(Netbeans/Ant project)
+* **jsyntaxpane** - 代码编辑器(Netbeans/Apache Maven 工程)
+* **LZMA** - 用于 SWF 文件的压缩(Netbeans/Ant 工程)
+* **nellymoser** - 用于 Nelly Moser 音效的解码(Netbeans/Ant 工程)
+* **Swf2Exe** - “保存为 EXE 文件” 功能的占位符(Delphi 7 工程)
+* **ttf** - 用于 TTF 字体的导出(Netbeans/Ant 工程)
+* **gnujpdf** - 用于 PDF 文件的导出(Netbeans/Ant 工程)
+
+## Docker
+我们有用于无头运行的 `Dockerfile`,这样就无需在本地安装 Java 或 FFDec。
+(原始脚本来自:Mahdi Lazraq)
+### 构建
+```
+docker build -t ffdec .
+```
+### 用法
+FFDec 命令行(CLI) 是入口点,因此您可以直接传递参数:
+```
+docker run --rm -v ./input:/work/input -v ./output:/work/output ffdec [args]
+```
+
+## 更新日志
+所有重要变更均列于 [CHANGELOG.md](CHANGELOG.md) 文件中。
+
+## 部署
+
+### 每夜构建
+当提交被 推送/合并 到 `dev` 分支时,GitHub Actions CI 会自动创建一个新的预发布版本。
+这些预发布版本被称为 每夜构建(nightly build)。在发布新的每夜构建版本后,之前的每夜构建版本就会被移除。
+
+### 稳定版本
+当在 master 分支中使用 `versionx.y.z` 格式的标签标记修订版本时,GitHub Actions CI 会自动创建一个新的稳定版本。
+
+## 贡献
+
+请参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 文件,以了解我们的行为准则详情以及提交拉取请求的流程。
+
+## 版本控制
+
+版本号采用 `x.y.z` 格式,例如 `9.1.2`。
+有关可用版本,请参阅 [此存储库上的标签](https://github.com/jindrapetrik/jpexs-decompiler/tags)。
+
+每夜构建版本带有额外的 `_nightlyN` 后缀,其中 `N` 是一个数字,每次(自动)发布每夜构建版本时都会递增,并且与 `x.y.z` 编号无关(这意味着发布稳定版本时,每夜构建版本的编号不会重置为 0)。
+较早的每夜构建版本 *无法* 通过 Git 标签获取。
+
+## 作者
+反编译器最初由 **Jindra Petřík**(又名 **JPEXS**)编写。
+该应用制作于捷克共和国。
+
+### 开发者
+* **JPEXS** - 项目负责人、反编译器开发、网站主管理员、GitHub 帐户管理员、组织管理员
+* **honfika** - 反编译器开发
+* **Paolo Cancedda** - 前开发者
+* ...以及来自 GitHub 和 Google Code 的其他人
+
+### 翻译者
+* **Jaume Badiella Aguilera** - 加泰罗尼亚语翻译
+* **Capasha** - 瑞典语翻译
+* **王晨旭** (Chenxu Wang), **晓之车**, **安安**, **Liushui**, **老biu** - 中文翻译
+* **focus** - 俄语翻译
+* **honfika** - 匈牙利语翻译
+* **kalip** - 意大利语翻译
+* **Krock** - 德语翻译
+* **Laurent LOUVET** - 法语翻译
+* **MaGiC** - 葡萄牙语翻译
+* **martinkoza** - 波兰语翻译
+* **Osman ÖZ** - 土耳其语翻译
+* **pepka** - 乌克兰语、荷兰语翻译
+* **poxyran** - 西班牙语翻译
+* **realmaster42**, **alimsoftware** - 葡萄牙语(巴西)翻译
+* **Rtsjx** - 中文翻译
+* **koiru** - 日语翻译
+* **J. Kramer** - 荷兰语翻译
+* **Andrew Poženel** - 斯洛文尼亚语翻译
+* **GitHub Copilot (Claude AI)** - 德语、斯洛伐克语翻译
+
+## 联系
+如果您想报告问题或提出新功能请求,请使用我们的问题跟踪系统:[https://www.free-decompiler.com/flash/issues](https://www.free-decompiler.com/flash/issues)
+
+在报告之前,您应该已经在维基百科上查看过 [常见问题解答(FAQ)](https://github.com/jindrapetrik/jpexs-decompiler/wiki/FAQ) 部分了。
+另外,也请 [参阅维基百科中的已知问题列表](https://github.com/jindrapetrik/jpexs-decompiler/wiki/Known-problems)。
+
+### 邮件联系
+JPEXS 开发者的紧急联系邮箱是 `jindra.petrik@gmail.com`。
+但我们更倾向于使用问题跟踪系统进行联系。
+
+## 许可协议 + 致谢
+### 应用
+
+FFDec 应用程序采用 GNU GPL v3(GPL-3.0 或更高版本)许可证,详情请参阅 [license.txt](license.txt) 文件。它使用了以下库的修改代码:
+
+* [JSyntaxPane] (代码编辑器) - Apache License 2.0
+
+并且链接了以下的库:
+
+* [Java Native Access - JNA] (注册表关联,进程内存读取) - LGPL
+* [Insubstantial] (Substance Look and Feel,Flamingo Ribbon 组件) - Revised BSD
+* [javactivex] (Flash Player ActiveX 的嵌入) - LGPLv3
+* [flashdebugger library] (调试 ActionScript) - LGPLv3
+* FFDec Library (LGPLv3) - 参阅如下
+
+应用程序还使用了 [Silk icons pack]、[Silk companion 1]、[FatCow icons pack] 和 [Aha-Soft icons pack] 中的一些图标。
+
+对于 EXE 启动器,我们使用 [Launch5j] - MIT。
+
+## 库
+有关 FFDec 库的更多信息,请参阅 [库 README](libsrc/ffdec_lib/README.md) 文件。
+
+[GIT]: http://git-scm.com/downloads
+[Netbeans IDE]: http://www.netbeans.org/
+[Apache Ant]: http://ant.apache.org/
+[Launch5j]: https://github.com/lordmulder/Launch5j
+[NSIS]: http://nsis.sourceforge.net/
+[JSyntaxPane]: https://code.google.com/p/jsyntaxpane/
+[Java Native Access - JNA]: https://github.com/twall/jna
+[Insubstantial]: http://shemnon.com/speling/2011/04/insubstantial-62-release.html
+[javactivex]:https://github.com/jindrapetrik/javactivex
+[flashdebugger library]: https://github.com/jindrapetrik/flashdebugger
+[Silk icons pack]: http://www.famfamfam.com/lab/icons/silk/
+[Silk companion 1]: http://damieng.com/creative/icons/silk-companion-1-icons
+[FatCow icons pack]: http://www.fatcow.com/free-icons
+[Aha-Soft icons pack]: http://www.aha-soft.com
diff --git a/altsigner/pom.xml b/altsigner/pom.xml
index 52c34f8bb..f186fa24c 100644
--- a/altsigner/pom.xml
+++ b/altsigner/pom.xml
@@ -64,7 +64,7 @@
org.bouncycastle
bcprov-jdk18on
- 1.83
+ 1.84
\ No newline at end of file
diff --git a/cicd_scripts/update_changelog.php b/cicd_scripts/update_changelog.php
index 4d8b7d1cd..75bb3acbe 100644
--- a/cicd_scripts/update_changelog.php
+++ b/cicd_scripts/update_changelog.php
@@ -40,7 +40,7 @@ if ($result !== 0) {
exit(1);
}
-
+$output = [];
$result = 0;
exec('git log ' . $version1 . '..' . $version2 . ' --pretty=format:"%s%n%b%n---SPLIT---" --reverse', $output, $result);
if ($result !== 0) {
@@ -99,7 +99,7 @@ foreach ($messages as $message) {
if (array_key_exists($type, $changelog_types)) {
$changelog[$type][] = $desc;
}
- }
+ }
}
$result = "";
diff --git a/lib/flacomdoc.jar b/lib/flacomdoc.jar
index eb4041b73..6218cc825 100644
Binary files a/lib/flacomdoc.jar and b/lib/flacomdoc.jar differ
diff --git a/lib/jsyntaxpane-0.9.5.jar b/lib/jsyntaxpane-0.9.5.jar
index 2d610772d..7098665e9 100644
Binary files a/lib/jsyntaxpane-0.9.5.jar and b/lib/jsyntaxpane-0.9.5.jar differ
diff --git a/libsrc/ffdec_lib/lib/flacomdoc.jar b/libsrc/ffdec_lib/lib/flacomdoc.jar
index eb4041b73..6218cc825 100644
Binary files a/libsrc/ffdec_lib/lib/flacomdoc.jar and b/libsrc/ffdec_lib/lib/flacomdoc.jar differ
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/FontNormalizer.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/FontNormalizer.java
index 6ea995584..80a4a31b9 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/FontNormalizer.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/FontNormalizer.java
@@ -390,7 +390,7 @@ public class FontNormalizer {
private Set getDefineEditTextFonts(DefineEditTextTag text) {
Set ret = new LinkedHashSet<>();
TextStyle style = new TextStyle();
- if (text.fontClass != null) {
+ if (text.hasFontClass) {
style.font = text.getSwf().getFontByClass(text.fontClass);
} else {
style.font = text.getSwf().getFont(text.fontId);
@@ -513,7 +513,7 @@ public class FontNormalizer {
private void scaleDefineEditTextFonts(DefineEditTextTag text, Map fontNewScale, boolean inPlace, Map outTexts) {
String str = "";
TextStyle style = new TextStyle();
- if (text.fontClass != null) {
+ if (text.hasFontClass) {
style.font = text.getSwf().getFontByClass(text.fontClass);
} else {
style.font = text.getSwf().getFont(text.fontId);
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 9ec42418b..c4cb27a26 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java
@@ -3165,25 +3165,22 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
}
/**
- * Makes scriptpacks unique. Unique = no two packs with same classpath
+ * Checks scriptpacks whether they are unique. Unique = no two packs with same classpath
* exist.
*
* @param packs List of ScriptPacks
* @return List of unique ScriptPacks
*/
- private List uniqueAS3Packs(List packs) {
- List ret = new ArrayList<>();
+ private void checkUniqueAS3Packs(List packs) {
Set classPaths = new HashSet<>();
for (ScriptPack item : packs) {
ClassPath key = item.getClassPath();
if (classPaths.contains(key) && item.isSimple) {
- logger.log(Level.SEVERE, "Duplicate pack path found ({0})!", key);
+ logger.log(Level.WARNING, "Duplicate scriptpack path found ({0})!", key);
} else {
- classPaths.add(key);
- ret.add(item);
+ classPaths.add(key);
}
- }
- return ret;
+ }
}
/**
@@ -3205,7 +3202,8 @@ public final class SWF implements SWFContainerItem, Timelined, Openable {
for (ABCContainerTag abcTag : abcList) {
packs.addAll(abcTag.getABC().getScriptPacks(null, allAbcList));
}
- return uniqueAS3Packs(packs);
+ checkUniqueAS3Packs(packs);
+ return packs;
}
/**
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java
index 76c02e764..5adcfa062 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java
@@ -2031,7 +2031,9 @@ public class AVM2Code implements Cloneable {
if (code.get(ip + plus + 3).definition instanceof SetPropertyIns) {
functionName = abc.constants.getMultiname(code.get(ip + plus + 3).operands[0]).getName(usedDeobfuscations, abc, abc.constants, fullyQualifiedNames, true, true);
localScopeStack.pop(); // with
- output.remove(output.size() - 1); // with
+ stack.finishBlock(output);
+ stack.moveToStack(output);
+ output.remove(output.size() - 1); // with
ip = ip + plus + 4; // +1 below
}
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java
index 5f4c8c969..de5336fa7 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AbcIndexing.java
@@ -47,7 +47,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.WeakHashMap;
/**
* Indexing of ABCs for faster access. Indexes ABC classes for faster class and
@@ -1104,6 +1103,9 @@ public final class AbcIndexing {
*/
protected void indexTraits(ABC abc, int name_index, Traits ts, Map map, Map mapNs, Map> mapAmbiguous, int scriptIndex) {
for (Trait t : ts.traits) {
+ if (t.deleted) {
+ continue;
+ }
ValueKind propValue = null;
if (t instanceof TraitSlotConst) {
TraitSlotConst tsc = (TraitSlotConst) t;
@@ -1233,6 +1235,9 @@ public final class AbcIndexing {
indexTraits(abc, 0, abc.script_info.get(i).traits, null, scriptProperties, scriptAmbiguousProperties, i);
for (int t = 0; t < abc.script_info.get(i).traits.traits.size(); t++) {
Trait tr = abc.script_info.get(i).traits.traits.get(t);
+ if (tr.deleted) {
+ continue;
+ }
if (tr instanceof TraitClass) {
TraitClass tc = (TraitClass) tr;
InstanceInfo ii = abc.instance_info.get(tc.class_info);
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java
index 94aa74a13..8d42f22fe 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java
@@ -224,7 +224,7 @@ public class CallAVM2Item extends AVM2Item {
}
if (callable instanceof TypeItem && propIndex != -1 && arguments.size() == 1) {
- AVM2Instruction ins = NameAVM2Item.generateCoerce(localData, generator, callable);
+ AVM2Instruction ins = NameAVM2Item.generateConvert(localData, generator, callable);
if (ins != null) {
return toSourceMerge(localData, generator, arguments, ins);
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java
index c339c63cc..230dfa711 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java
@@ -352,6 +352,45 @@ public class NameAVM2Item extends AssignableAVM2Item {
}
return ins;
}
+
+ /**
+ * Generates convert.
+ * @param localData Local data
+ * @param generator Generator
+ * @param ttype Target type
+ * @return Convert instruction
+ * @throws CompilationException On compilation error
+ */
+ public static AVM2Instruction generateConvert(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem ttype) throws CompilationException {
+ if (ttype instanceof UnresolvedAVM2Item) {
+ ttype = ((UnresolvedAVM2Item) ttype).resolved;
+ }
+ AVM2Instruction ins = null;
+ switch (ttype.toString()) {
+ case "int":
+ ins = ins(AVM2Instructions.ConvertI);
+ break;
+ case "String":
+ ins = ins(AVM2Instructions.ConvertS);
+ break;
+ case "Boolean":
+ ins = ins(AVM2Instructions.ConvertB);
+ break;
+ case "uint":
+ ins = ins(AVM2Instructions.ConvertU);
+ break;
+ case "Number":
+ ins = ins(AVM2Instructions.ConvertD);
+ break;
+ case "float":
+ ins = ins(AVM2Instructions.ConvertF);
+ break;
+ case "float4":
+ ins = ins(AVM2Instructions.ConvertF4);
+ break;
+ }
+ return ins;
+ }
private List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException {
addTraitUsage(localData, localData.callStack);
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java
index e490dc250..baa384a83 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java
@@ -342,8 +342,10 @@ public class PropertyAVM2Item extends AssignableAVM2Item {
boolean found = false;
String nsName = ns.getName(propValueAbc.constants).toRawString();
while (ci != null) {
- DottedChain clsName = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNameWithNamespace(new HashSet<>(), ci.abc, ci.abc.constants, false);
- String clsNsName = clsName.isTopLevel() ? clsName.getLast() : clsName.getWithoutLast().toRawString() + ":" + clsName.getLast();
+ DottedChain clsFullName = ci.abc.instance_info.get(ci.index).getName(ci.abc.constants).getNameWithNamespace(new HashSet<>(), ci.abc, ci.abc.constants, false);
+ DottedChain clsPkg = clsFullName.getWithoutLast();
+ String clsName = clsFullName.getLast();
+ String clsNsName = clsPkg.isTopLevel() ? clsName : clsPkg.toRawString() + ":" + clsName;
if (Objects.equals(nsName, clsNsName)) {
found = true;
break;
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java
index ba6ae4960..96e99cd9e 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java
@@ -433,6 +433,8 @@ public class ActionGraph extends Graph {
targetStartItem = st;
target = new DirectValueActionItem(null, null, 0, st.target, new ArrayList<>());
}
+ } else if (targetStart > -1) {
+ targetEnd = t;
}
}
if (it instanceof SetTarget2ActionItem) {
@@ -445,9 +447,15 @@ public class ActionGraph extends Graph {
targetStartItem = st;
target = st.target;
}
+ } else if (targetStart > -1) {
+ targetEnd = t;
}
}
+ if (it instanceof TellTargetActionItem && targetStart > -1) {
+ targetEnd = t;
+ }
+
if (targetStart > -1 && targetEnd > -1) {
List newlist = new ArrayList<>();
for (int i = 0; i < targetStart; i++) {
@@ -459,7 +467,7 @@ public class ActionGraph extends Graph {
}
newlist.add(new TellTargetActionItem(targetStartItem.getSrc(), targetStartItem.getLineStartItem(), target, tellist));
//TODO: maybe set nested flag
- for (int i = targetEnd + 1; i < list.size(); i++) {
+ for (int i = targetEnd + (it instanceof TellTargetActionItem ? 0 : 1); i < list.size(); i++) {
newlist.add(list.get(i));
}
list.clear();
@@ -467,7 +475,7 @@ public class ActionGraph extends Graph {
targetStart = -1;
targetEnd = -1;
target = null;
- t = 0;
+ t = -1;
}
}
for (int t = 1/*not first*/; t < list.size(); t++) {
@@ -1043,7 +1051,7 @@ public class ActionGraph extends Graph {
ActionSecondPassData spd = new ActionSecondPassData();
Set processedIfs = new HashSet<>();
checkSecondPassSwitches(localData, loops, throwStates, spd.switchCases, spd.switchBreaks, processedIfs, list, spd.switchParts, spd.switchOnFalseParts, spd.switchCaseExpressions);
-
+
return spd;
}
@@ -1174,7 +1182,7 @@ public class ActionGraph extends Graph {
allSwitchParts.add(switchParts);
allSwitchOnFalseParts.add(switchOnFalseParts);
allSwitchExpressions.add(switchExpressions);
- allSwitchCases.add(switchCases);
+ allSwitchCases.add(switchCases);
try {
allSwitchBreaks.add(getMostCommonPart(localData, switchCases, loops, throwStates, new ArrayList<>()));
} catch (InterruptedException ex) {
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java
index cc1af9ef9..b64ecddca 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java
@@ -496,57 +496,57 @@ public class FunctionActionItem extends ActionItem {
}
int regCount = 0;
- if (actions != null && !actions.isEmpty()) {
- localDataCopy.inFunction++;
+
+ localDataCopy.inFunction++;
- for (VariableActionItem v : variables) {
- String varName = v.getVariableName();
- GraphTargetItem stored = v.getStoreValue();
- if (needsFun2) {
- if (v.isDefinition() && !registerNames.contains(varName) && !deeplyUsedVariableNames.contains(varName)
- && !hasEval) {
- registerNames.add(varName);
- }
+ for (VariableActionItem v : variables) {
+ String varName = v.getVariableName();
+ GraphTargetItem stored = v.getStoreValue();
+ if (needsFun2) {
+ if (v.isDefinition() && !registerNames.contains(varName) && !deeplyUsedVariableNames.contains(varName)
+ && !hasEval) {
+ registerNames.add(varName);
}
+ }
- if (registerNames.contains(varName)) {
- if (stored != null) {
- v.setBoxedValue(new StoreRegisterActionItem(null, null, new RegisterNumber(registerNames.indexOf(varName), varName), stored, false));
- } else {
- v.setBoxedValue(new DirectValueActionItem(new RegisterNumber(registerNames.indexOf(varName), varName)));
- }
- } else if (v.isDefinition()) {
- v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
- } else if (stored != null) {
- v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
+ if (registerNames.contains(varName)) {
+ if (stored != null) {
+ v.setBoxedValue(new StoreRegisterActionItem(null, null, new RegisterNumber(registerNames.indexOf(varName), varName), stored, false));
} else {
- v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName)));
+ v.setBoxedValue(new DirectValueActionItem(new RegisterNumber(registerNames.indexOf(varName), varName)));
}
-
- }
- for (int i = 1 /* zero is not preloaded*/; i < registerNames.size(); i++) {
- localDataCopy.registerVars.put(registerNames.get(i), i);
+ } else if (v.isDefinition()) {
+ v.setBoxedValue(new DefineLocalActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
+ } else if (stored != null) {
+ v.setBoxedValue(new SetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName), stored));
+ } else {
+ v.setBoxedValue(new GetVariableActionItem(null, null, ((ActionSourceGenerator) generator).pushConstTargetItem(varName)));
}
+ }
+ for (int i = 1 /* zero is not preloaded*/; i < registerNames.size(); i++) {
+ localDataCopy.registerVars.put(registerNames.get(i), i);
+ }
+
+ if (actions != null && !actions.isEmpty()) {
ret.addAll(asGenerator.toActionList(asGenerator.generate(localDataCopy, actions)));
+ }
+ regCount = registerNames.size();
- regCount = registerNames.size();
-
- //some temporary registers can exceed variable+param count
- for (GraphSourceItem a : ret) {
- if (a instanceof ActionPush) {
- ActionPush apu = (ActionPush) a;
- for (Object o : apu.values) {
- if (o instanceof RegisterNumber) {
- RegisterNumber rn = (RegisterNumber) o;
- if (rn.number >= regCount) {
- regCount++;
- }
+ //some temporary registers can exceed variable+param count
+ for (GraphSourceItem a : ret) {
+ if (a instanceof ActionPush) {
+ ActionPush apu = (ActionPush) a;
+ for (Object o : apu.values) {
+ if (o instanceof RegisterNumber) {
+ RegisterNumber rn = (RegisterNumber) o;
+ if (rn.number >= regCount) {
+ regCount++;
}
}
}
}
- }
+ }
int len = Action.actionsToBytes(asGenerator.toActionList(ret), false, SWF.DEFAULT_VERSION).length;
if (len > 0xFFFF) {
throw new CompilationException("Function body is too large to fit into UI16.", line);
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java
index 90bb6896e..44f655e1b 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf5/ActionSetMember.java
@@ -188,7 +188,7 @@ public class ActionSetMember extends Action {
((GetMemberActionItem) ((DecrementActionItem) value).object).object = ((GetMemberActionItem) ((DecrementActionItem) value).object).object.getThroughDuplicate();
cleanupTemp(((GetMemberActionItem) ((DecrementActionItem) value).object).object, object, output, stack);
if (setter) {
- stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((IncrementActionItem) value).object.getThroughDuplicate()));
+ stack.addToOutput(new PreDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
} else {
stack.addToOutput(new PostDecrementActionItem(action, lineStartAction, ((DecrementActionItem) value).object.getThroughDuplicate()));
}
@@ -252,8 +252,14 @@ public class ActionSetMember extends Action {
} finally {
if (setter) {
stack.finishBlock(output);
- stack.push(output.remove(output.size() - 1));
- stack.moveToStack(output);
+ // Guard against an empty output: if the try block exited via an
+ // exception before producing a statement, removing from an empty
+ // list would throw IndexOutOfBoundsException here and mask the
+ // original exception. Mirrors the check used in cleanupTemp().
+ if (!output.isEmpty()) {
+ stack.push(output.remove(output.size() - 1));
+ stack.moveToStack(output);
+ }
}
}
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java
index 3c419c85c..68ba91f67 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java
@@ -1237,6 +1237,14 @@ public final class Configuration {
@ConfigurationCategory("display")
public static ConfigurationItem useMinimumStrokeWidth1Px = null;
+ @ConfigurationDefaultBoolean(true)
+ @ConfigurationCategory("display")
+ public static ConfigurationItem showLoadingSpinner = null;
+
+ @ConfigurationDefaultString("")
+ @ConfigurationName("xmlExport.formats")
+ public static ConfigurationItem lastSelectedXmlExportFormats = null;
+
private static Map configurationDescriptions = new LinkedHashMap<>();
private static Map configurationTitles = new LinkedHashMap<>();
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/LegacyConfigurationStorage.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/LegacyConfigurationStorage.java
index 658413f86..4c0bfdff1 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/LegacyConfigurationStorage.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/LegacyConfigurationStorage.java
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.configuration;
+import com.jpexs.helpers.AllowedObjectInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -42,7 +43,7 @@ public class LegacyConfigurationStorage implements ConfigurationStorage {
@Override
public Map loadFromFile(String file) {
- try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
+ try (ObjectInputStream ois = new AllowedObjectInputStream(new FileInputStream(file))) {
@SuppressWarnings("unchecked")
Map cfg = (HashMap) ois.readObject();
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java
index 5b5104a93..b98cd0e27 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/FrameExporter.java
@@ -607,7 +607,9 @@ public class FrameExporter {
final Color fbackgroundColor = backgroundColor;
final boolean usesTransparency = settings.mode == FrameExportMode.PNG
|| settings.mode == FrameExportMode.GIF
- || settings.mode == FrameExportMode.WEBP;
+ || settings.mode == FrameExportMode.WEBP
+ || settings.mode == FrameExportMode.WEBP_ANIMATED
+ || settings.mode == FrameExportMode.APNG;
final MyFrameIterator frameImages = new MyFrameIterator(tim, fframes, evl, usesTransparency, backgroundColor, settings, subFramesLength);
switch (settings.mode) {
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java
index aab7f03ff..5ad599577 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ImageExporter.java
@@ -56,6 +56,41 @@ import javax.imageio.ImageIO;
* @author JPEXS
*/
public class ImageExporter {
+
+
+ private static ImageFormat getExportFormat(ImageTag imageTag, ImageExportSettings settings) {
+ ImageFormat fileFormat = imageTag.getOriginalImageFormat();
+ boolean hasSeparateAlpha = false;
+ if (imageTag instanceof HasSeparateAlphaChannel) {
+ HasSeparateAlphaChannel hsac = (HasSeparateAlphaChannel) imageTag;
+ hasSeparateAlpha = hsac.hasAlphaChannel();
+ }
+ if (settings.mode == ImageExportMode.PNG_GIF_JPEG && hasSeparateAlpha) {
+ fileFormat = ImageFormat.PNG;
+ }
+ if (settings.mode == ImageExportMode.PNG) {
+ fileFormat = ImageFormat.PNG;
+ }
+
+ if (settings.mode == ImageExportMode.JPEG) {
+ fileFormat = ImageFormat.JPEG;
+ }
+
+ if (settings.mode == ImageExportMode.BMP) {
+ fileFormat = ImageFormat.BMP;
+ }
+
+ if (settings.mode == ImageExportMode.WEBP) {
+ fileFormat = ImageFormat.WEBP;
+ }
+ return fileFormat;
+ }
+
+ public static String getExportExtension(ImageTag imageTag, ImageExportSettings settings) {
+ ImageFormat fileFormat = getExportFormat(imageTag, settings);
+
+ return ImageHelper.getImageFormatString(fileFormat);
+ }
public List exportImages(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List ret = new ArrayList<>();
@@ -90,31 +125,9 @@ public class ImageExporter {
final ImageTag imageTag = (ImageTag) t;
- ImageFormat fileFormat = imageTag.getOriginalImageFormat();
- ImageFormat originalFormat = fileFormat;
- boolean hasSeparateAlpha = false;
- if (imageTag instanceof HasSeparateAlphaChannel) {
- HasSeparateAlphaChannel hsac = (HasSeparateAlphaChannel) imageTag;
- hasSeparateAlpha = hsac.hasAlphaChannel();
- }
- if (settings.mode == ImageExportMode.PNG_GIF_JPEG && hasSeparateAlpha) {
- fileFormat = ImageFormat.PNG;
- }
- if (settings.mode == ImageExportMode.PNG) {
- fileFormat = ImageFormat.PNG;
- }
-
- if (settings.mode == ImageExportMode.JPEG) {
- fileFormat = ImageFormat.JPEG;
- }
-
- if (settings.mode == ImageExportMode.BMP) {
- fileFormat = ImageFormat.BMP;
- }
-
- if (settings.mode == ImageExportMode.WEBP) {
- fileFormat = ImageFormat.WEBP;
- }
+
+ ImageFormat originalFormat = imageTag.getOriginalImageFormat();
+ ImageFormat fileFormat = getExportFormat(imageTag, settings);
final File file = new File(outdir + File.separator + Helper.makeFileName(imageTag.getCharacterExportFileName() + "." + ImageHelper.getImageFormatString(fileFormat)));
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java
index 9f0f615b7..f12d9656b 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java
@@ -188,7 +188,7 @@ public class MorphShapeExporter {
}
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
- st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
+ st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, new Matrix(), m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
BufferedImage bim = img.getBufferedImage();
if (settings.mode == MorphShapeExportMode.PNG_START_END) {
@@ -222,7 +222,7 @@ public class MorphShapeExporter {
}
m = Matrix.getScaleInstance(settings.zoom);
m.translate(-rect.Xmin, -rect.Ymin);
- st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, m, m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
+ st.toImage(0, 0, 0, new RenderContext(), img, img, false, m, new Matrix(), m, m, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, settings.aaScale);
bim = img.getBufferedImage();
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
index df10574f5..5608504c1 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
@@ -149,7 +149,7 @@ public class ShapeExporter {
Matrix m2 = Matrix.getScaleInstance(settings.zoom);
m2.translate(-rect.Xmin, -rect.Ymin);
- st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, m2, m2, m2, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, aaScale);
+ st.toImage(0, 0, 0, new RenderContext(), img, img, false, m2, new Matrix(), m2, m2, new CXFORMWITHALPHA(), unzoom, false, new ExportRectangle(rect), new ExportRectangle(rect), true, Timeline.DRAW_MODE_ALL, 0, true, aaScale);
BufferedImage bim = img.getBufferedImage();
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java
index 2ca02c1d8..cb81c81b8 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/SoundExporter.java
@@ -64,6 +64,27 @@ import java.util.Set;
*/
public class SoundExporter {
+ public static String getExportExtension(SoundTag soundTag, SoundExportSettings settings) {
+ String ext = "wav";
+ SoundFormat fmt = soundTag.getSoundFormat();
+ switch (fmt.getNativeExportFormat()) {
+ case MP3:
+ if (settings.mode.hasMP3()) {
+ ext = "mp3";
+ }
+ break;
+ case FLV:
+ if (settings.mode.hasFlv()) {
+ ext = "flv";
+ }
+ break;
+ }
+ if (settings.mode == SoundExportMode.FLV) {
+ ext = "flv";
+ }
+ return ext;
+ }
+
public List exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List sounds = new ArrayList<>();
for (Tag t : tags) {
@@ -72,7 +93,7 @@ public class SoundExporter {
}
}
return exportSounds(handler, outdir, sounds, settings, evl);
- }
+ }
public List exportSounds(AbortRetryIgnoreHandler handler, String outdir, List tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
List ret = new ArrayList<>();
@@ -97,24 +118,7 @@ public class SoundExporter {
evl.handleExportingEvent("sound", currentIndex, tags.size(), st.getName());
}
- String ext = ".wav";
- SoundFormat fmt = st.getSoundFormat();
- switch (fmt.getNativeExportFormat()) {
- case MP3:
- if (settings.mode.hasMP3()) {
- ext = ".mp3";
- }
- break;
- case FLV:
- if (settings.mode.hasFlv()) {
- ext = ".flv";
- }
- break;
- }
- if (settings.mode == SoundExportMode.FLV) {
- ext = ".flv";
- }
-
+ String ext = "." + getExportExtension(st, settings);
final File file = new File(outdir + File.separator + Helper.makeFileName(st.getCharacterExportFileName()) + ext);
new RetryTask(() -> {
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) {
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/XamlExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/XamlExporter.java
index 5dd3f75bf..4033c3fad 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/XamlExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/XamlExporter.java
@@ -402,7 +402,7 @@ public class XamlExporter {
int fontId = defineEditText.fontId;
FontTag font = null;
if (fontId == -1) {
- if (defineEditText.fontClass != null) {
+ if (defineEditText.hasFontClass) {
font = swf.getFontByClass(defineEditText.fontClass);
fontId = swf.getCharacterId(font);
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/XmlSwfExportSettings.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/XmlSwfExportSettings.java
new file mode 100644
index 000000000..18368276c
--- /dev/null
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/settings/XmlSwfExportSettings.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010-2026 JPEXS, All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.
+ */
+package com.jpexs.decompiler.flash.exporters.settings;
+
+import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
+import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
+import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
+
+/**
+ *
+ * @author JPEXS
+ */
+public class XmlSwfExportSettings {
+ public ScriptExportMode as12ExportMode;
+ public ImageExportMode imageExportMode;
+ public SoundExportMode defineSoundExportMode;
+
+ public XmlSwfExportSettings() {
+ }
+
+ public XmlSwfExportSettings(ScriptExportMode as12ExportMode, ImageExportMode imageExportMode, SoundExportMode defineSoundExportMode) {
+ if (as12ExportMode != null && as12ExportMode != ScriptExportMode.AS) {
+ throw new IllegalArgumentException("Unsupported script export mode");
+ }
+ this.as12ExportMode = as12ExportMode;
+ if (
+ imageExportMode != null
+ && imageExportMode != ImageExportMode.PNG_GIF_JPEG
+ && imageExportMode != ImageExportMode.PNG_GIF_JPEG_ALPHA
+ ) {
+ throw new IllegalArgumentException("Unsupported image export mode");
+ }
+ this.imageExportMode = imageExportMode;
+ if (defineSoundExportMode != null && defineSoundExportMode != SoundExportMode.MP3_WAV_FLV) {
+ throw new IllegalArgumentException("Unsupported sound export mode");
+ }
+ this.defineSoundExportMode = defineSoundExportMode;
+ }
+}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasTools.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasTools.java
index 6fe219138..6aa923160 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasTools.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasTools.java
@@ -154,11 +154,11 @@ public class AntialiasTools {
for (int i = 1; i < iPts.length; i++) {
path.lineTo(iPts[i].x / (float) FIXED_ONE, iPts[i].y / (float) FIXED_ONE);
- }
-
- if (close) {
- path.closePath();
- }
+ }
+ }
+
+ if (close) {
+ path.closePath();
}
return path;
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasedBitmapExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasedBitmapExporter.java
index c9c4177d9..b41fd555f 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasedBitmapExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/shape/aa/AntialiasedBitmapExporter.java
@@ -204,6 +204,7 @@ public class AntialiasedBitmapExporter extends BitmapExporter {
*
* @return Image
*/
+ @Override
public SerializableImage getImage() {
return image;
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java
index e012fc1a3..c74470d80 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/swf/SwfXmlExporter.java
@@ -16,13 +16,35 @@
*/
package com.jpexs.decompiler.flash.exporters.swf;
+import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.ApplicationInfo;
+import com.jpexs.decompiler.flash.EventListener;
+import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.SWF;
+import com.jpexs.decompiler.flash.exporters.ImageExporter;
+import com.jpexs.decompiler.flash.exporters.SoundExporter;
+import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
+import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
+import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter;
+import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
+import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
+import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
+import com.jpexs.decompiler.flash.exporters.settings.XmlSwfExportSettings;
import com.jpexs.decompiler.flash.helpers.InternalClass;
import com.jpexs.decompiler.flash.helpers.LazyObject;
+import com.jpexs.decompiler.flash.tags.DefineButtonTag;
+import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.UnknownTag;
+import com.jpexs.decompiler.flash.tags.base.ASMSource;
+import com.jpexs.decompiler.flash.tags.base.ButtonAction;
+import com.jpexs.decompiler.flash.tags.base.CharacterTag;
+import com.jpexs.decompiler.flash.tags.base.ImageTag;
+import com.jpexs.decompiler.flash.tags.base.SoundTag;
+import com.jpexs.decompiler.flash.types.annotations.Conditional;
import com.jpexs.decompiler.flash.types.annotations.Internal;
+import com.jpexs.decompiler.flash.types.annotations.parser.AnnotationParseException;
+import com.jpexs.decompiler.flash.types.annotations.parser.ConditionEvaluator;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.ReflectionTools;
@@ -36,9 +58,14 @@ import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLOutputFactory;
@@ -58,10 +85,14 @@ public class SwfXmlExporter {
*/
public static final int XML_EXPORT_VERSION_MAJOR = 2;
+ public static final int XML_EXPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES = 3;
+
/**
* XML export version minor.
+ *
+ * Version 2 - export only fields that meet conditions
*/
- public static final int XML_EXPORT_VERSION_MINOR = 1;
+ public static final int XML_EXPORT_VERSION_MINOR = 2;
private static final Logger logger = Logger.getLogger(SwfXmlExporter.class.getName());
@@ -75,16 +106,100 @@ public class SwfXmlExporter {
* @throws IOException On I/O error
*/
public void exportXml(SWF swf, File outFile) throws IOException {
+ exportXml(swf, outFile, new XmlSwfExportSettings(), null, new AbortRetryIgnoreHandler() {
+ @Override
+ public int handle(Throwable thrown) {
+ return AbortRetryIgnoreHandler.ABORT;
+ }
+
+ @Override
+ public AbortRetryIgnoreHandler getNewInstance() {
+ return this;
+ }
+ });
+ }
+
+ /**
+ * Exports SWF to XML.
+ *
+ * @param swf SWF to export
+ * @param outFile Target file to save to
+ * @param settings Export settings
+ * @param evl Event listener
+ * @param handler Abort/Retry/Ignore handler
+ *
+ * @throws IOException On I/O error
+ */
+ public void exportXml(SWF swf, File outFile, XmlSwfExportSettings settings, EventListener evl, AbortRetryIgnoreHandler handler) throws IOException {
try {
File tmp = File.createTempFile("FFDEC", "XML");
+ String assetsDirName = outFile.getName();
+ if (assetsDirName.contains(".")) {
+ assetsDirName = assetsDirName.substring(0, assetsDirName.lastIndexOf("."));
+ }
+ assetsDirName = assetsDirName + "_assets";
+
+ Map asmExternalFiles = new HashMap<>();
+ if (settings.as12ExportMode != null) {
+ Map externalNameToAsm = swf.getASMs(true);
+ Set existingNames = new HashSet<>();
+ for (String key : externalNameToAsm.keySet()) {
+ ASMSource asm = externalNameToAsm.get(key);
+ String currentOutDir = key + "/";
+ currentOutDir = new File(currentOutDir).getParentFile().toString();
+ currentOutDir = currentOutDir.replace("\\", "/");
+ if (!"/".equals(currentOutDir)) {
+ currentOutDir += "/";
+ }
+ String name = Helper.makeFileName(asm.getExportFileName());
+ int i = 1;
+ String baseName = name;
+ while (existingNames.contains(currentOutDir + name)) {
+ i++;
+ name = baseName + "_" + i;
+ }
+ existingNames.add(currentOutDir + name);
+ asmExternalFiles.put(asm, assetsDirName + "/scripts" + currentOutDir + name + ".as");
+ }
+ }
+
+ Map tagExternalFiles = new IdentityHashMap<>();
+ List imagesList = new ArrayList<>();
+ if (settings.imageExportMode != null) {
+ ImageExportSettings imageExportSetttings = new ImageExportSettings(settings.imageExportMode);
+ Map chars = swf.getCharacters(false);
+ for (int charId : chars.keySet()) {
+ CharacterTag ch = chars.get(charId);
+ if (ch instanceof ImageTag) {
+ ImageTag imageTag = (ImageTag) ch;
+ tagExternalFiles.put(imageTag, assetsDirName + "/images/" + Helper.makeFileName(imageTag.getCharacterExportFileName()) + "." + ImageExporter.getExportExtension(imageTag, imageExportSetttings));
+ imagesList.add(imageTag);
+ }
+ }
+ }
+
+ List soundList = new ArrayList<>();
+ if (settings.defineSoundExportMode != null) {
+ SoundExportSettings soundExportSetttings = new SoundExportSettings(settings.defineSoundExportMode);
+ Map chars = swf.getCharacters(false);
+ for (int charId : chars.keySet()) {
+ CharacterTag ch = chars.get(charId);
+ if (ch instanceof DefineSoundTag) {
+ DefineSoundTag soundTag = (DefineSoundTag) ch;
+ tagExternalFiles.put(soundTag, assetsDirName + "/sounds/" + Helper.makeFileName(soundTag.getCharacterExportFileName()) + "." + SoundExporter.getExportExtension(soundTag, soundExportSetttings));
+ soundList.add(soundTag);
+ }
+ }
+ }
+
try (Writer writer = new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(tmp)))) {
XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
xmlWriter.writeStartDocument();
xmlWriter.writeComment("\r\nWARNING: The structure of this XML is not final.\r\nIn later versions of FFDec it can be changed.\r\nMake sure you use compatible reader/writer based on _xmlExportMajor/_xmlExportMinor keys.\r\n");
- exportXml(swf, xmlWriter);
+ exportXml(asmExternalFiles, tagExternalFiles, swf, xmlWriter);
xmlWriter.writeEndDocument();
xmlWriter.flush();
@@ -100,6 +215,27 @@ public class SwfXmlExporter {
logger.log(Level.SEVERE, "Cannot prettyformat XML");
}
tmp.delete();
+
+ if (settings.as12ExportMode != null) {
+ AS2ScriptExporter exporter = new AS2ScriptExporter();
+ exporter.exportActionScript2(swf, handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/scripts").toFile().getAbsolutePath(), new ScriptExportSettings(settings.as12ExportMode, false, false, false, false), true, evl);
+ }
+ if (settings.imageExportMode != null) {
+ ImageExporter exporter = new ImageExporter();
+ try {
+ exporter.exportImages(handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/images").toFile().getAbsolutePath(), new ReadOnlyTagList(imagesList), new ImageExportSettings(settings.imageExportMode), evl);
+ } catch (InterruptedException ex) {
+ return;
+ }
+ }
+ if (settings.defineSoundExportMode != null) {
+ SoundExporter exporter = new SoundExporter();
+ try {
+ exporter.exportSounds(handler, outFile.getParentFile().toPath().resolve(assetsDirName + "/sounds").toFile().getAbsolutePath(), soundList, new SoundExportSettings(settings.defineSoundExportMode), evl);
+ } catch (InterruptedException ex) {
+ return;
+ }
+ }
} catch (XMLStreamException ex) {
logger.log(Level.SEVERE, null, ex);
}
@@ -108,13 +244,30 @@ public class SwfXmlExporter {
/**
* Exports SWF to XML.
*
+ * @param asmExternalFiles ASM external files
+ * @param tagExternalFiles Tag external files
* @param swf SWF to export
* @param writer XML writer
* @throws IOException On I/O error
* @throws XMLStreamException On XML error
*/
- public void exportXml(SWF swf, XMLStreamWriter writer) throws IOException, XMLStreamException {
- generateXml(writer, "swf", swf, false);
+ private void exportXml(
+ Map asmExternalFiles,
+ Map tagExternalFiles,
+ SWF swf,
+ XMLStreamWriter writer
+ ) throws IOException, XMLStreamException {
+ generateXml(
+ asmExternalFiles.isEmpty() && tagExternalFiles.isEmpty() ? XML_EXPORT_VERSION_MAJOR : XML_EXPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES,
+ asmExternalFiles,
+ tagExternalFiles,
+ swf,
+ null,
+ writer,
+ "swf",
+ swf,
+ false
+ );
}
public List getSwfFieldsCached(Class cls) {
@@ -167,7 +320,17 @@ public class SwfXmlExporter {
return cls != null && (cls.isArray() || List.class.isAssignableFrom(cls));
}
- private void generateXml(XMLStreamWriter writer, String name, Object obj, boolean isListItem) throws XMLStreamException {
+ private void generateXml(
+ int major,
+ Map asmExternalFiles,
+ Map tagExternalFiles,
+ SWF swf,
+ Tag currentTag,
+ XMLStreamWriter writer,
+ String name,
+ Object obj,
+ boolean isListItem
+ ) throws XMLStreamException {
Class cls = obj != null ? obj.getClass() : null;
/*if (obj != null && cls == String.class) {
@@ -215,7 +378,7 @@ public class SwfXmlExporter {
writer.writeStartElement(name);
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
- generateXml(writer, "item", Array.get(value, i), true);
+ generateXml(major, asmExternalFiles, tagExternalFiles, swf, currentTag, writer, "item", Array.get(value, i), true);
}
writer.writeEndElement();
} else if (obj != null) {
@@ -233,9 +396,10 @@ public class SwfXmlExporter {
writer.writeStartElement(name);
if (obj instanceof SWF) {
- writer.writeAttribute("_xmlExportMajor", "" + XML_EXPORT_VERSION_MAJOR);
+ writer.writeAttribute("_xmlExportMajor", "" + major);
writer.writeAttribute("_xmlExportMinor", "" + XML_EXPORT_VERSION_MINOR);
writer.writeAttribute("_generator", ApplicationInfo.applicationVerName);
+ swf = (SWF) obj;
}
writer.writeAttribute("type", clazz.getSimpleName());
@@ -247,12 +411,80 @@ public class SwfXmlExporter {
writer.writeAttribute("charset", ((SWF) obj).getCharset());
}
+ boolean isExternal = false;
+
+ if (obj instanceof Tag) {
+ currentTag = (Tag) obj;
+
+ if (tagExternalFiles.containsKey((Tag) obj)) {
+ writer.writeAttribute("_externalFile", tagExternalFiles.get((Tag) obj));
+ isExternal = true;
+ }
+ }
+
for (Field f : fields) {
- //Multiline multilineA = f.getAnnotation(Multiline.class);
+ //Multiline multilineA = f.getAnnotation(Multiline.class);
+
+ if (isExternal && !"characterID".equals(f.getName()) && !"soundId".equals(f.getName())) {
+ continue;
+ }
+
+ Conditional cond = f.getAnnotation(Conditional.class);
+ if (cond != null) {
+ ConditionEvaluator ev = new ConditionEvaluator(cond);
+ try {
+ Set condFields = ev.getFields();
+ Map fieldMap = new HashMap<>();
+ for (String sf : condFields) {
+ try {
+ Object value = ReflectionTools.getValue(obj, clazz.getField(sf));
+ if (value instanceof Boolean) {
+ fieldMap.put(sf, (Boolean) value);
+ }
+ if (value instanceof Integer) {
+ int intValue = (Integer) value;
+ boolean found = false;
+ for (int i : cond.options()) {
+ if (i == intValue) {
+ found = true;
+ }
+ }
+ fieldMap.put(sf, found);
+ }
+ } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
+ fieldMap.put(sf, true);
+ }
+ }
+ if (!ev.eval(fieldMap, currentTag.getId())) {
+ continue;
+ }
+ } catch (AnnotationParseException ex) {
+ logger.log(Level.SEVERE, null, ex);
+ }
+ }
try {
f.setAccessible(true);
- generateXml(writer, f.getName(), f.get(obj), false);
+ Object value = f.get(obj);
+
+ if ("actionBytes".equals(f.getName())) {
+ if (obj instanceof ASMSource && asmExternalFiles.containsKey((ASMSource) obj)) {
+ value = new ByteArrayRange("00");
+ writer.writeAttribute("_externalActions", asmExternalFiles.get((ASMSource) obj));
+ } else if (obj instanceof DefineButtonTag) {
+ for (ASMSource s : asmExternalFiles.keySet()) {
+ if (s instanceof ButtonAction) {
+ ButtonAction ba = (ButtonAction) s;
+ if (ba.getSourceTag() == obj) {
+ value = new ByteArrayRange("00");
+ writer.writeAttribute("_externalActions", asmExternalFiles.get(s));
+ break;
+ }
+ }
+ }
+ }
+ }
+ generateXml(major, asmExternalFiles, tagExternalFiles, swf, currentTag, writer, f.getName(), value, false);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS2ScriptImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS2ScriptImporter.java
index 36d28c954..abfa9925e 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS2ScriptImporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/AS2ScriptImporter.java
@@ -44,7 +44,6 @@ public class AS2ScriptImporter {
private static final Logger logger = Logger.getLogger(AS2ScriptImporter.class.getName());
-
/**
* Constructor.
*/
@@ -52,8 +51,58 @@ public class AS2ScriptImporter {
}
+ /**
+ * Imports actionScript 1/2 (not P-code) from given file
+ *
+ * @param fileName File to import
+ * @param asm Target to import into
+ * @param listener Import listener
+ * @return True on success
+ * @throws InterruptedException
+ */
+ public boolean importActionScript(String fileName, ASMSource asm, ScriptImporterProgressListener listener) throws InterruptedException {
+ asm.getSwf().informListeners("importing_as", fileName);
+ String txt = Helper.readTextFile(fileName);
+
+ ActionScript2Parser par = new ActionScript2Parser(asm.getSwf(), asm);
+ boolean errored = false;
+ try {
+ asm.setActions(par.actionsFromString(txt, asm.getSwf().getCharset()));
+ } catch (ValueTooLargeException ex) {
+ logger.log(Level.SEVERE, "Script or some of its functions are too large, file: {0}", fileName);
+ errored = true;
+ } catch (ActionParseException ex) {
+ logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
+ errored = true;
+ } catch (CompilationException ex) {
+ logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
+ errored = true;
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
+ errored = true;
+ } catch (InterruptedException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
+ errored = true;
+ }
+
+ if (!errored) {
+ asm.setModified();
+ if (listener != null) {
+ listener.scriptImported();
+ }
+ } else {
+ if (listener != null) {
+ listener.scriptImportError();
+ }
+ }
+ return !errored;
+ }
+
/**
* Imports scripts from given folder.
+ *
* @param scriptsFolder Folder with scripts
* @param asms Map of ASMSource objects
* @return Number of imported scripts
@@ -65,6 +114,7 @@ public class AS2ScriptImporter {
/**
* Imports scripts from given folder.
+ *
* @param scriptsFolder Folder with scripts
* @param asms Map of ASMSource objects
* @param listener Progress listener
@@ -104,42 +154,12 @@ public class AS2ScriptImporter {
String fileName = Path.combine(currentOutDir, name) + ".as";
if (new File(fileName).exists()) {
- asm.getSwf().informListeners("importing_as", fileName);
- String txt = Helper.readTextFile(fileName);
-
- ActionScript2Parser par = new ActionScript2Parser(asm.getSwf(), asm);
- boolean errored = false;
try {
- asm.setActions(par.actionsFromString(txt, asm.getSwf().getCharset()));
- } catch (ValueTooLargeException ex) {
- logger.log(Level.SEVERE, "Script or some of its functions are too large, file: {0}", fileName);
- errored = true;
- } catch (ActionParseException ex) {
- logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
- errored = true;
- } catch (CompilationException ex) {
- logger.log(Level.SEVERE, "%error% on line %line%, file: %file%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)).replace("%file%", fileName), ex);
- errored = true;
- } catch (IOException ex) {
- logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
- errored = true;
+ if (importActionScript(fileName, asm, listener)) {
+ importCount++;
+ }
} catch (InterruptedException ex) {
return importCount;
- } catch (Exception ex) {
- logger.log(Level.SEVERE, "error during script import, file: %file%".replace("%file%", fileName), ex);
- errored = true;
- }
-
- if (!errored) {
- asm.setModified();
- importCount++;
- if (listener != null) {
- listener.scriptImported();
- }
- } else {
- if (listener != null) {
- listener.scriptImportError();
- }
}
}
diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java
index aff1ad0ae..28ec39e52 100644
--- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java
+++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/importers/SwfXmlImporter.java
@@ -37,11 +37,17 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
+import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter;
import com.jpexs.decompiler.flash.tags.CSMSettingsTag;
+import com.jpexs.decompiler.flash.tags.DefineButtonTag;
+import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.TagTypeInfo;
import com.jpexs.decompiler.flash.tags.UnknownTag;
+import com.jpexs.decompiler.flash.tags.base.ASMSource;
+import com.jpexs.decompiler.flash.tags.base.ImageTag;
+import com.jpexs.decompiler.flash.tags.base.SoundImportException;
import com.jpexs.decompiler.flash.types.ALPHABITMAPDATA;
import com.jpexs.decompiler.flash.types.ALPHACOLORMAPDATA;
import com.jpexs.decompiler.flash.types.ARGB;
@@ -108,12 +114,16 @@ import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
+import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.HashArrayList;
import com.jpexs.helpers.Helper;
+import com.jpexs.helpers.IdentityKey;
import com.jpexs.helpers.ReflectionTools;
import com.jpexs.helpers.utf8.Utf8InputStreamReader;
import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@@ -124,7 +134,9 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -145,7 +157,12 @@ public class SwfXmlImporter {
/**
* Maximum XML import version major.
*/
- public static final int MAX_XML_IMPORT_VERSION_MAJOR = 2;
+ public static final int MAX_XML_IMPORT_VERSION_MAJOR = 3;
+
+ /**
+ * Minimum version for using external files - attributes _externalActions, _externalFile
+ */
+ public static final int XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES = 3;
private static final Logger logger = Logger.getLogger(SwfXmlImporter.class.getName());
@@ -221,11 +238,15 @@ public class SwfXmlImporter {
* Imports SWF from input stream.
* @param swf SWF object
* @param in Input stream
+ * @param directory Directory where XML resides for external files resolving
* @throws IOException On I/O error
*/
- public void importSwf(SWF swf, InputStream in) throws IOException {
+ public void importSwf(SWF swf, InputStream in, File directory) throws IOException {
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
+ Map, String> asmExternalActions = new LinkedHashMap<>();
+ Map, String> tagExternalFiles = new LinkedHashMap<>();
+
try {
try (Reader reader = new Utf8InputStreamReader(new BufferedInputStream(in))) {
XMLStreamReader xmlReader = xmlFactory.createXMLStreamReader(reader);
@@ -233,7 +254,7 @@ public class SwfXmlImporter {
xmlReader.nextTag();
xmlReader.require(XMLStreamConstants.START_ELEMENT, null, "swf");
- processElement(xmlReader, swf, swf, null, MAX_XML_IMPORT_VERSION_MAJOR);
+ processElement(xmlReader, swf, swf, null, MAX_XML_IMPORT_VERSION_MAJOR, asmExternalActions, tagExternalFiles);
}
swf.clearAllCache();
@@ -241,16 +262,78 @@ public class SwfXmlImporter {
} catch (XMLStreamException ex) {
logger.log(Level.SEVERE, null, ex);
}
+
+ if (!asmExternalActions.isEmpty()) {
+ for (IdentityKey