Added: FLA/FlashDevelop/IDEA export - A link to all classes (sound, font, images) is added so no class is missed during compilation

This commit is contained in:
Jindra Petřík
2024-08-18 09:47:53 +02:00
parent 58e61907f2
commit 9617c2ac2f
14 changed files with 201 additions and 51 deletions

View File

@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
- Updated Flash player to SWF version map
- Harman AIR 51 float support compatibility
- FlashDevelop project export - option to export AIR project (select correct type in the file save dialog)
- FLA/FlashDevelop/IDEA export - A link to all classes (sound, font, images) is added so no class is missed during compilation
### Fixed
- [#2266] StartSound/2 and VideoFrame tags, classNames not taken as dependencies (needed chars)

View File

@@ -133,7 +133,7 @@ public class DecompilerPool {
}
boolean parallel = Configuration.parallelSpeedUp.get();
HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true);
pack.toSource(abcIndex, writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel, false);
pack.toSource(abcIndex, writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel, false, false);
writer.finishHilights();
HighlightedText result = new HighlightedText(writer);

View File

@@ -297,9 +297,11 @@ public class ScriptPack extends AS3ClassTreeItem {
* @param convertData Convert data
* @param exportMode Export mode
* @param parallel Parallel
* @param exportAllClasses Export all classes - reference it in document
* class
* @throws InterruptedException On interrupt
*/
private void appendTo(AbcIndexing abcIndex, GraphTextWriter writer, List<Trait> traits, ConvertData convertData, ScriptExportMode exportMode, boolean parallel) throws InterruptedException {
private void appendTo(AbcIndexing abcIndex, GraphTextWriter writer, List<Trait> traits, ConvertData convertData, ScriptExportMode exportMode, boolean parallel, boolean exportAllClasses) throws InterruptedException {
boolean first = true;
//script initializer
int script_init = abc.script_info.get(scriptIndex).init_index;
@@ -307,21 +309,21 @@ public class ScriptPack extends AS3ClassTreeItem {
if (!isSimple && traitIndices.isEmpty()) {
for (Trait t : abc.script_info.get(scriptIndex).traits.traits) {
if (t instanceof TraitSlotConst) {
continue;
}
String fullName = t.getName(abc).getNameWithNamespace(abc.constants, false).toPrintableString(true);
writer.appendNoHilight("include \"" + fullName.replace(".", "/") + ".as\";").newLine();
}
writer.newLine();
}
}
for (int t : traitIndices) {
Trait trait = traits.get(t);
if (trait instanceof TraitSlotConst) {
continue;
}
@@ -342,7 +344,7 @@ public class ScriptPack extends AS3ClassTreeItem {
}
first = false;
}
List<DottedChain> fullyQualifiedNames = new ArrayList<>();
if (!first) {
writer.newLine();
@@ -354,16 +356,15 @@ public class ScriptPack extends AS3ClassTreeItem {
Trait.writeImports(null, script_init, abcIndex, scriptIndex, -1, true, abc, writer, ignorePackage, fullyQualifiedNames);
first = true;
//Slot const last
for (int t : traitIndices) {
Trait trait = traits.get(t);
if (!(trait instanceof TraitSlotConst)) {
continue;
}
if (convertData.assignedValues.containsKey((TraitSlotConst) trait)) {
continue;
}
@@ -384,19 +385,19 @@ public class ScriptPack extends AS3ClassTreeItem {
}
first = false;
}
if (bodyIndex != -1 && (isSimple || traitIndices.isEmpty())) {
//Note: There must be trait/method highlight even if the initializer is empty to TraitList in GUI to work correctly
writer.startTrait(GraphTextWriter.TRAIT_SCRIPT_INITIALIZER);
writer.startMethod(script_init, null);
if (exportMode != ScriptExportMode.AS_METHOD_STUBS) {
if (!scriptInitializerIsEmpty) {
if (!scriptInitializerIsEmpty) {
List<MethodBody> callStack = new ArrayList<>();
callStack.add(abc.bodies.get(bodyIndex));
if (!first) {
writer.newLine();
writer.newLine();
}
abc.bodies.get(bodyIndex).toString(callStack, abcIndex, path + "/.scriptinitializer", exportMode, abc, null, writer, fullyQualifiedNames, new HashSet<>());
abc.bodies.get(bodyIndex).toString(callStack, abcIndex, path + "/.scriptinitializer", exportMode, abc, null, writer, fullyQualifiedNames, new HashSet<>());
} else {
writer.append("");
}
@@ -408,6 +409,16 @@ public class ScriptPack extends AS3ClassTreeItem {
first = false;
}
}
if (exportAllClasses) {
String documentClass = abc.getSwf().getDocumentClass();
if (documentClass != null) {
if (path.toRawString().equals(documentClass)) {
writer.append("//Include all classes in the build").append("\r\n");
writer.append("function __ffdec_include_classes() { FFDecIncludeClasses; }");
}
}
}
}
/**
@@ -420,9 +431,10 @@ public class ScriptPack extends AS3ClassTreeItem {
* @param exportMode Export mode
* @param parallel Parallel
* @param ignoreFrameScripts Whether to ignore frame scripts
* @param exportAllClasses Export all classes
* @throws InterruptedException On interrupt
*/
public void toSource(AbcIndexing abcIndex, GraphTextWriter writer, final List<Trait> traits, final ConvertData convertData, final ScriptExportMode exportMode, final boolean parallel, boolean ignoreFrameScripts) throws InterruptedException {
public void toSource(AbcIndexing abcIndex, GraphTextWriter writer, final List<Trait> traits, final ConvertData convertData, final ScriptExportMode exportMode, final boolean parallel, boolean ignoreFrameScripts, boolean exportAllClasses) throws InterruptedException {
writer.suspendMeasure();
int timeout = Configuration.decompilationTimeoutFile.get();
try {
@@ -460,7 +472,7 @@ public class ScriptPack extends AS3ClassTreeItem {
}
writer.continueMeasure();
appendTo(abcIndex, writer, traits, convertData, exportMode, parallel);
appendTo(abcIndex, writer, traits, convertData, exportMode, parallel, exportAllClasses);
}
/**
@@ -492,7 +504,7 @@ public class ScriptPack extends AS3ClassTreeItem {
convertData.exportEmbed = exportSettings.exportEmbed;
convertData.exportEmbedFlaMode = exportSettings.exportEmbedFlaMode;
convertData.assetsDir = exportSettings.assetsDir;
toSource(abcIndex, writer2, abc.script_info.get(scriptIndex).traits.traits, convertData, exportSettings.mode, parallel, exportSettings.ignoreFrameScripts);
toSource(abcIndex, writer2, abc.script_info.get(scriptIndex).traits.traits, convertData, exportSettings.mode, parallel, exportSettings.ignoreFrameScripts, exportSettings.includeAllClasses);
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "The file path is probably too long", ex);
}

View File

@@ -1019,6 +1019,10 @@ public final class Configuration {
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("script")
public static ConfigurationItem<Boolean> warningAddFunction = null;
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("export")
public static ConfigurationItem<Boolean> linkAllClasses = null;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -81,6 +81,7 @@ import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.ScopeStack;
@@ -92,6 +93,7 @@ import com.jpexs.helpers.XmlPrettyFormat;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -381,6 +383,7 @@ public class AS3ScriptExporter {
/**
* Export ActionScript 3 scripts.
*
* @param swf SWF
* @param handler AbortRetryIgnoreHandler
* @param outdir Output directory
@@ -405,6 +408,56 @@ public class AS3ScriptExporter {
int cnt = 1;
List<ExportPackTask> tasks = new ArrayList<>();
Set<String> files = new HashSet<>();
String documentClass = swf.getDocumentClass();
StringBuffer includeClassesBuilder = new StringBuffer();
String documentPkg = DottedChain.parseNoSuffix(documentClass).getWithoutLast().toPrintableString(true);
StringBuilder importsBuilder = new StringBuilder();
for (ScriptPack item : packs) {
if (!item.isSimple && Configuration.ignoreCLikePackages.get()) {
continue;
}
if (ignoredClasses.contains(item.getClassPath().toRawString())) {
continue;
}
if (flexClass != null && item.getClassPath().toRawString().equals(flexClass)) {
continue;
}
String rawClassName = item.getClassPath().toRawString();
CharacterTag character = swf.getCharacterByClass(rawClassName);
//For some reasons Sprites do not work...
boolean allowedType = (character instanceof SoundTag)
|| (character instanceof ImageTag)
|| (character instanceof FontTag);
if (allowedType) {
if (!item.getClassPath().packageStr.isTopLevel()) {
importsBuilder.append(" import ").append(item.getClassPath().toString()).append(";\r\n");
}
includeClassesBuilder.append(" ").append(item.getClassPath().toString()).append(";\r\n");
}
}
if (documentClass != null && !includeClassesBuilder.isEmpty()) {
StringBuilder prep = new StringBuilder();
prep.append(" /**\r\n");
prep.append(" * This class contains references to all decompiled sound/image/font classes.\r\n");
prep.append(" * It is needed for compilation otherwise some classes will be missed.\r\n");
prep.append(" */\r\n");
prep.append(" public class FFDecIncludeClasses\r\n");
prep.append(" {\r\n");
includeClassesBuilder.insert(0, prep);
}
//If no sound/image classes found, then do not include FFDecIncludeClasses at all
if (includeClassesBuilder.isEmpty()) {
exportSettings = exportSettings.clone();
exportSettings.includeAllClasses = false;
}
for (ScriptPack item : packs) {
if (!item.isSimple && Configuration.ignoreCLikePackages.get()) {
continue;
@@ -446,6 +499,35 @@ public class AS3ScriptExporter {
tasks.add(new ExportPackTask(swf.getAbcIndex(), handler, cnt++, packs.size(), item.getClassPath(), item, file, exportSettings, parallel, evl));
}
if (!includeClassesBuilder.isEmpty()) {
includeClassesBuilder.append(" }\r\n");
includeClassesBuilder.append("}\r\n");
StringBuilder prepend = new StringBuilder();
prepend.append("package ").append(documentPkg).append("\r\n");
prepend.append("{\r\n");
prepend.append(importsBuilder.toString());
prepend.append("\r\n");
includeClassesBuilder.insert(0, prepend.toString());
if (exportSettings.includeAllClasses) {
java.nio.file.Path outPath = new File(outdir).toPath();
if (!documentPkg.isEmpty()) {
outPath = outPath.resolve(documentPkg);
}
File ffdecIncludeFilePath = outPath.resolve("FFDecIncludeClasses.as").toFile();
try (FileOutputStream fos = new FileOutputStream(ffdecIncludeFilePath)) {
fos.write(Utf8Helper.getBytes(includeClassesBuilder.toString()));
} catch (FileNotFoundException ex) {
Logger.getLogger(AS3ScriptExporter.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(AS3ScriptExporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
if (!parallel || tasks.size() < 2) {
try {
CancellableWorker.call(new Callable<Void>() {

View File

@@ -18,6 +18,8 @@ package com.jpexs.decompiler.flash.exporters.settings;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Script export settings.
@@ -71,6 +73,11 @@ public class ScriptExportSettings {
*/
public String assetsDir;
/**
* Modify main class to reference all classes
*/
public boolean includeAllClasses = true;
/**
* Constructor.
* @param mode Mode
@@ -88,7 +95,7 @@ public class ScriptExportSettings {
boolean exportEmbedFlaMode,
boolean resampleWav
) {
this(mode, singleFile, ignoreFrameScripts, exportEmbed, exportEmbedFlaMode, resampleWav, "/_assets/");
this(mode, singleFile, ignoreFrameScripts, exportEmbed, exportEmbedFlaMode, resampleWav, "/_assets/", false);
}
public ScriptExportSettings(
@@ -98,7 +105,8 @@ public class ScriptExportSettings {
boolean exportEmbed,
boolean exportEmbedFlaMode,
boolean resampleWav,
String assetsDir
String assetsDir,
boolean includeAllClasses
) {
this.mode = mode;
this.singleFile = singleFile;
@@ -107,6 +115,7 @@ public class ScriptExportSettings {
this.exportEmbedFlaMode = exportEmbedFlaMode;
this.resampleWav = resampleWav;
this.assetsDir = assetsDir;
this.includeAllClasses = includeAllClasses;
}
public String getFileExtension() {
@@ -127,4 +136,14 @@ public class ScriptExportSettings {
throw new Error("Unsupported script export mode: " + mode);
}
}
@Override
public ScriptExportSettings clone() {
try {
return (ScriptExportSettings) super.clone();
} catch (CloneNotSupportedException ex) {
//ignored
}
return null;
}
}

View File

@@ -343,7 +343,7 @@ public class SwfFlashDevelopExporter {
}
boolean parallel = Configuration.parallelSpeedUp.get();
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, false, true, false, false);
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, false, true, false, false, "/_assets/", Configuration.linkAllClasses.get());
swf.exportActionScript(handler, outFile.toPath().getParent().resolve(srcPath).toFile().getAbsolutePath(), scriptExportSettings, parallel, eventListener);
}
}

View File

@@ -316,7 +316,7 @@ public class SwfIntelliJIdeaExporter {
}
boolean parallel = Configuration.parallelSpeedUp.get();
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, false, true, false, false);
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, false, true, false, false, "/_assets/", Configuration.linkAllClasses.get());
swf.exportActionScript(handler, new File(outDir, "src").getAbsolutePath(), scriptExportSettings, parallel, eventListener);
}
}

View File

@@ -1463,16 +1463,16 @@ public class XFLConverter {
return date.getTime() / 1000;
}
private void convertLibrary(Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
private void convertLibrary(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
statusStack.pushStatus("media");
convertMedia(charactersExportedInFirstFrame, lastImportedId, characterNameMap, characterImportLinkageURL, characters, swf, characterVariables, characterClasses, tags, files, datfiles, writer, statusStack);
convertMedia(lastItemIdNumber, charactersExportedInFirstFrame, lastImportedId, characterNameMap, characterImportLinkageURL, characters, swf, characterVariables, characterClasses, tags, files, datfiles, writer, statusStack);
statusStack.popStatus();
statusStack.pushStatus("symbols");
convertSymbols(charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, files, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
convertSymbols(lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, tags, files, flaVersion, writer, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
statusStack.popStatus();
}
private void convertSymbols(Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
private void convertSymbols(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, Map<CharacterTag, ScriptPack> characterScriptPacks, List<CharacterTag> nonLibraryShapes, String backgroundColor, ReadOnlyTagList tags, HashMap<String, byte[]> files, FLAVersion flaVersion, XFLXmlWriter writer, Map<PlaceObjectTypeTag, MultiLevelClip> placeToMaskedSymbol, List<Integer> multiUsageMorphShapes, StatusStack statusStack) throws XMLStreamException {
//boolean hasSymbol = false;
Reference<Integer> nextClipId = new Reference<>(-1);
writer.writeStartElement("symbols");
@@ -1486,10 +1486,12 @@ public class XFLConverter {
statusStack.pushStatus(symbol.toString());
XFLXmlWriter symbolStr = new XFLXmlWriter();
String itemId = generateItemId(lastItemIdNumber);
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
"xmlns", "http://ns.adobe.com/xfl/2008/",
"name", getSymbolName(lastImportedId, characterNameMap, swf, symbol),
"itemID", itemId,
"lastModified", Long.toString(getTimestamp(swf))}); //TODO:itemID
if (characterImportLinkageURL.containsKey(symbol)) {
symbolStr.writeAttribute("linkageImportForRS", "true");
@@ -1705,7 +1707,7 @@ public class XFLConverter {
}
final ScriptPack spriteScriptPack = characterScriptPacks.containsKey(sprite) ? characterScriptPacks.get(sprite) : null;
extractMultilevelClips(lastImportedId, characterNameMap, sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
extractMultilevelClips(lastItemIdNumber, lastImportedId, characterNameMap, sprite.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
convertTimelines(lastImportedId, characterNameMap, swf, swf.getAbcIndex(), sprite, characterVariables.get(sprite), nonLibraryShapes, tags, sprite.getTags(), getSymbolName(lastImportedId, characterNameMap, swf, symbol), flaVersion, files, symbolStr, spriteScriptPack, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
@@ -1732,6 +1734,7 @@ public class XFLConverter {
// write symbLink
writer.writeStartElement("Include", new String[]{"href", symbolFile});
writer.writeAttribute("itemID", itemId);
if (itemIcon != null) {
writer.writeAttribute("itemIcon", itemIcon);
}
@@ -1747,11 +1750,11 @@ public class XFLConverter {
}
statusStack.pushStatus("extracting multilevel clips");
extractMultilevelClips(lastImportedId, characterNameMap, swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
extractMultilevelClips(lastItemIdNumber, lastImportedId, characterNameMap, swf.getTags(), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
statusStack.popStatus();
statusStack.pushStatus("converting multiusage morphshapes");
extractMultiUsageMorphShapes(lastImportedId, characterNameMap, writer, swf, nonLibraryShapes, flaVersion, files, multiUsageMorphShapes, statusStack);
extractMultiUsageMorphShapes(lastItemIdNumber, lastImportedId, characterNameMap, writer, swf, nonLibraryShapes, flaVersion, files, multiUsageMorphShapes, statusStack);
statusStack.popStatus();
/*if (hasSymbol) {
@@ -1759,7 +1762,7 @@ public class XFLConverter {
writer.writeEndElement();
}
private void convertSoundMedia(Map<CharacterTag, String> characterImportLinkageURL, SWF swf, ReadOnlyTagList tags, SoundTag symbol, XFLXmlWriter writer, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles) throws XMLStreamException {
private void convertSoundMedia(Reference<Integer> lastItemIdNumber, Map<CharacterTag, String> characterImportLinkageURL, SWF swf, ReadOnlyTagList tags, SoundTag symbol, XFLXmlWriter writer, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles) throws XMLStreamException {
int soundFormat = 0;
int soundRate = 0;
boolean soundType = false;
@@ -1929,6 +1932,7 @@ public class XFLConverter {
files.put(symbolFile, data);
writer.writeStartElement("DOMSoundItem", new String[]{
"name", symbolFile,
"itemID", generateItemId(lastItemIdNumber),
"sourceLastImported", Long.toString(getTimestamp(swf)),
"externalFileSize", Integer.toString(data.length)});
if ((symbol instanceof CharacterTag) && characterImportLinkageURL.containsKey((CharacterTag) symbol)) {
@@ -1945,7 +1949,7 @@ public class XFLConverter {
writer.writeAttribute("sampleCount", soundSampleCount);
}
private void convertMedia(Set<CharacterTag> charactersExportedInFirstFrame, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
private void convertMedia(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, Map<CharacterTag, String> characterImportLinkageURL, Set<CharacterTag> characters, SWF swf, Map<CharacterTag, String> characterVariables, Map<CharacterTag, String> characterClasses, ReadOnlyTagList tags, HashMap<String, byte[]> files, HashMap<String, byte[]> datfiles, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
boolean hasMedia = false;
for (CharacterTag symbol : characters) {
if (symbol instanceof ImageTag
@@ -2003,8 +2007,9 @@ public class XFLConverter {
ImageFormat format = imageTag.getImageFormat();
String symbolFile = getSymbolName(lastImportedId, characterNameMap, swf, symbol, "Bitmap") + imageTag.getImageFormat().getExtension();
files.put(symbolFile, imageBytes);
writer.writeStartElement("DOMBitmapItem", new String[]{
writer.writeStartElement("DOMBitmapItem", new String[]{
"name", symbolFile,
"itemID", generateItemId(lastItemIdNumber),
"sourceLastImported", Long.toString(getTimestamp(swf)),
"externalFileSize", Integer.toString(imageBytes.length)});
@@ -2061,7 +2066,7 @@ public class XFLConverter {
statusStack.popStatus();
} else if (symbol instanceof DefineSoundTag) {
statusStack.pushStatus(symbol.toString());
convertSoundMedia(characterImportLinkageURL, swf, tags, (DefineSoundTag) symbol, writer, files, datfiles);
convertSoundMedia(lastItemIdNumber, characterImportLinkageURL, swf, tags, (DefineSoundTag) symbol, writer, files, datfiles);
if (characterImportLinkageURL.containsKey(symbol)) {
writer.writeAttribute("linkageImportForRS", "true");
@@ -2148,6 +2153,7 @@ public class XFLConverter {
files.put(symbolFile, data);
writer.writeStartElement("DOMVideoItem", new String[]{
"name", symbolFile,
"itemID", generateItemId(lastItemIdNumber),
"sourceLastImported", Long.toString(getTimestamp(swf)),
"externalFileSize", Integer.toString(data.length)});
if (characterImportLinkageURL.containsKey(symbol)) {
@@ -2188,7 +2194,7 @@ public class XFLConverter {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t;
for (SoundStreamFrameRange range : head.getRanges()) {
statusStack.pushStatus(range.toString());
convertSoundMedia(characterImportLinkageURL, swf, tags, range, writer, files, datfiles);
convertSoundMedia(lastItemIdNumber, characterImportLinkageURL, swf, tags, range, writer, files, datfiles);
writer.writeEndElement();
statusStack.popStatus();
}
@@ -2200,7 +2206,7 @@ public class XFLConverter {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) st;
for (SoundStreamFrameRange range : head.getRanges()) {
statusStack.pushStatus(range.toString());
convertSoundMedia(characterImportLinkageURL, swf, sprite.getTags(), range, writer, files, datfiles);
convertSoundMedia(lastItemIdNumber, characterImportLinkageURL, swf, sprite.getTags(), range, writer, files, datfiles);
writer.writeEndElement();
statusStack.popStatus();
}
@@ -2675,7 +2681,7 @@ public class XFLConverter {
}
}
private static void convertFonts(Set<CharacterTag> charactersExportedInFirstFrame, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Set<CharacterTag> characters, Map<CharacterTag, String> characterClasses, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
private static void convertFonts(Reference<Integer> lastItemIdNumber, Set<CharacterTag> charactersExportedInFirstFrame, Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap, SWF swf, Set<CharacterTag> characters, Map<CharacterTag, String> characterClasses, XFLXmlWriter writer, StatusStack statusStack) throws XMLStreamException {
boolean hasFont = false;
int fontCounter = 0;
for (CharacterTag t : characters) {
@@ -2758,6 +2764,7 @@ public class XFLConverter {
fontCounter++;
writer.writeStartElement("DOMFontItem", new String[]{
"name", getSymbolName(lastImportedId, characterNameMap, swf, font, "Font"),
"itemID", generateItemId(lastItemIdNumber),
"font", fontName,
"size", "0",
"id", Integer.toString(fontCounter),
@@ -3159,7 +3166,9 @@ public class XFLConverter {
}
private void addExtractedClip(
Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
Map<CharacterTag, String> characterNameMap,
ReadOnlyTagList timelineTags,
XFLXmlWriter writer,
SWF swf,
@@ -3174,7 +3183,7 @@ public class XFLConverter {
) throws XMLStreamException {
XFLXmlWriter symbolStr = new XFLXmlWriter();
extractMultilevelClips(lastImportedId, characterNameMap, timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
extractMultilevelClips(lastItemIdNumber, lastImportedId, characterNameMap, timelineTags, writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
if (nextClipId.getVal() < 0) {
nextClipId.setVal(swf.getNextCharacterId());
@@ -3183,11 +3192,12 @@ public class XFLConverter {
}
int objectId = nextClipId.getVal();
String itemId = generateItemId(lastItemIdNumber);
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
"xmlns", "http://ns.adobe.com/xfl/2008/",
"name", getMaskedSymbolName(objectId),
"itemID", itemId,
"lastModified", Long.toString(getTimestamp(swf))});
symbolStr.writeAttribute("symbolType", "graphic");
@@ -3199,6 +3209,7 @@ public class XFLConverter {
files.put(symbolFile, Utf8Helper.getBytes(symbolStr2));
writer.writeStartElement("Include", new String[]{"href", symbolFile});
writer.writeAttribute("itemID", itemId);
writer.writeAttribute("itemIcon", "1");
writer.writeAttribute("loadImmediate", false);
if (flaVersion.ordinal() >= FLAVersion.CS5_5.ordinal()) {
@@ -3263,7 +3274,9 @@ public class XFLConverter {
}
private void extractMultiUsageMorphShapes(
Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
Map<CharacterTag, String> characterNameMap,
XFLXmlWriter writer,
SWF swf,
List<CharacterTag> nonLibraryShapes,
@@ -3276,11 +3289,13 @@ public class XFLConverter {
for (int objectId : multiUsageMorphShapes) {
statusStack.pushStatus(swf.getCharacter(objectId).toString());
String itemId = generateItemId(lastItemIdNumber);
XFLXmlWriter symbolStr = new XFLXmlWriter();
symbolStr.writeStartElement("DOMSymbolItem", new String[]{
"xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
"xmlns", "http://ns.adobe.com/xfl/2008/",
"name", getSymbolName(lastImportedId, characterNameMap, swf, swf.getCharacter(objectId)),
"itemID", itemId,
"lastModified", Long.toString(getTimestamp(swf))});
symbolStr.writeAttribute("symbolType", "graphic");
@@ -3308,6 +3323,7 @@ public class XFLConverter {
files.put(symbolFile, Utf8Helper.getBytes(symbolStr2));
writer.writeStartElement("Include", new String[]{"href", symbolFile});
writer.writeAttribute("itemID", itemId);
writer.writeAttribute("itemIcon", "1");
writer.writeAttribute("loadImmediate", false);
if (flaVersion.ordinal() >= FLAVersion.CS5_5.ordinal()) {
@@ -3320,7 +3336,9 @@ public class XFLConverter {
}
private void extractMultilevelClips(
Reference<Integer> lastImportedId, Map<CharacterTag, String> characterNameMap,
Reference<Integer> lastItemIdNumber,
Reference<Integer> lastImportedId,
Map<CharacterTag, String> characterNameMap,
ReadOnlyTagList timelineTags,
XFLXmlWriter writer,
SWF swf,
@@ -3520,7 +3538,7 @@ public class XFLConverter {
//set timelined?
delegatedTimeline.add(showFrame);
}
addExtractedClip(lastImportedId, characterNameMap, new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
addExtractedClip(lastItemIdNumber, lastImportedId, characterNameMap, new ReadOnlyTagList(delegatedTimeline), writer, swf, nextClipId, nonLibraryShapes, backgroundColor, flaVersion, files, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
placeToMaskedSymbol.put(secondPlace, new MultiLevelClip(secondPlace, nextClipId.getVal(), numFrames));
}
}
@@ -4670,6 +4688,7 @@ public class XFLConverter {
Set<CharacterTag> charactersExportedInFirstFrame = new LinkedIdentityHashSet<>();
Map<CharacterTag, String> characterImportLinkageURL = new IdentityHashMap<>();
Reference<Integer> lastItemIdNumber = new Reference<>(0);
int frame = 1;
for (Tag tag : swf.getTags()) {
if (tag instanceof ImportTag) {
@@ -4717,9 +4736,9 @@ public class XFLConverter {
}
}
}
convertFonts(charactersExportedInFirstFrame, lastImportedId, characterNameMap, swf, characters, characterClasses, domDocument, statusStack);
convertFonts(lastItemIdNumber, charactersExportedInFirstFrame, lastImportedId, characterNameMap, swf, characters, characterClasses, domDocument, statusStack);
convertLibrary(charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
convertLibrary(lastItemIdNumber, charactersExportedInFirstFrame, characterImportLinkageURL, characters, lastImportedId, characterNameMap, swf, characterVariables, characterClasses, characterScriptPacks, nonLibraryShapes, backgroundColor, swf.getTags(), files, datfiles, flaVersion, domDocument, placeToMaskedSymbol, multiUsageMorphShapes, statusStack);
//domDocument.writeStartElement("timelines");
ScriptPack documentScriptPack = null;
@@ -5203,7 +5222,7 @@ public class XFLConverter {
}
if (useAS3 && settings.exportScript) {
try {
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true, false, true, true);
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(ScriptExportMode.AS, false, true, false, true, true, "/_assets/", Configuration.linkAllClasses.get());
swf.exportActionScript(handler, scriptsDir.getAbsolutePath(), scriptExportSettings, parallel, null);
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error during ActionScript3 export", ex);
@@ -5365,7 +5384,14 @@ public class XFLConverter {
private static double twipToPixel(double tw) {
return tw / SWF.unitDivisor;
}
private static String generateItemId(Reference<Integer> lastItemIdNumber) {
lastItemIdNumber.setVal(lastItemIdNumber.getVal() + 1);
String epochHex = String.format("%1$08x", Math.round(System.currentTimeMillis() / 1000));
String numberHex = String.format("%1$08x", lastItemIdNumber.getVal());
return epochHex + "-" + numberHex;
}
private static class HTMLTextParser extends DefaultHandler {
public XFLXmlWriter result = new XFLXmlWriter();

View File

@@ -137,7 +137,7 @@ public class ActionScript3DeobfuscatorTest extends ActionScriptTestBase {
HighlightedTextWriter writer = new HighlightedTextWriter(new CodeFormatting(), false);
par.addScript(str, "Test.as", 0, 0, swf.getDocumentClass(), abc);
abc.script_info.get(0).getPacks(abc, 0, "", new ArrayList<>()).get(0).toSource(swf.getAbcIndex(), writer, abc.script_info.get(0).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false);
abc.script_info.get(0).getPacks(abc, 0, "", new ArrayList<>()).get(0).toSource(swf.getAbcIndex(), writer, abc.script_info.get(0).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false, false);
writer.finishHilights();
return writer.toString();
}

View File

@@ -91,7 +91,7 @@ public class DirectEditingTest extends FileTestBase {
System.out.println("Recompiling:" + classPathString + "...");
try {
en.toSource(swf.getAbcIndex(), htw, abc.script_info.get(s).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false);
en.toSource(swf.getAbcIndex(), htw, abc.script_info.get(s).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false, false);
htw.finishHilights();
String original = htw.toString();
abc.replaceScriptPack(As3ScriptReplacerFactory.createFFDec() /*TODO: test the otherone*/, en, original, new ArrayList<>());

View File

@@ -68,7 +68,7 @@ public class ActionScript3ClassTest extends ActionScript3DecompileTestBase {
HighlightedTextWriter writer = null;
try {
writer = new HighlightedTextWriter(new CodeFormatting(), false);
scriptPack.toSource(swf.getAbcIndex(), writer, abc.script_info.get(scriptPack.scriptIndex).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false);
scriptPack.toSource(swf.getAbcIndex(), writer, abc.script_info.get(scriptPack.scriptIndex).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false, false);
} catch (InterruptedException ex) {
fail();
}

View File

@@ -528,3 +528,6 @@ config.name.warningAbcClean=Warn on Abc clean action
config.description.warningAbcClean=Show warning before doing Abc clean action
config.name.warningAddFunction=Warn on adding new function in AS3 P-code
config.description.warningAddFunction=Show warning before creating new function in AS3 P-code. It also shows some info how the action works.
#after 21.0.2
config.name.linkAllClasses=Add link to all classes (sound, font, image)
config.description.linkAllClasses=Add special script that links all (sound, font, image) classes in the SWF. This is useful when no other script links them, to be still available in compiled file.

View File

@@ -526,3 +526,6 @@ config.name.warningAbcClean=Varovat p\u0159i Abc \u010di\u0161t\u011bn\u00ed
config.description.warningAbcClean=Zobrazovat varov\u00e1n\u00ed p\u0159ed proveden\u00edm Abc \u010di\u0161t\u011bn\u00ed
config.name.warningAddFunction=Varovat p\u0159i p\u0159id\u00e1v\u00e1n\u00ed nov\u00e9 funkce v AS3 P-k\u00f3du
config.description.warningAddFunction=Zobrazovat varov\u00e1n\u00ed p\u0159ed p\u0159id\u00e1n\u00edm nov\u00e9 funkce v AS3 P-k\u00f3du. Tak\u00e9 to zobrazuje n\u011bjak\u00e9 informace o tom, jak tato akce funguje.
#after 21.0.2
config.name.linkAllClasses=P\u0159idat vazbu na v\u0161echny t\u0159\u00eddy (zvuky, p\u00edsma, obr\u00e1zky)
config.description.linkAllClasses=P\u0159id\u00e1 speci\u00e1ln\u00ed skript kter\u00fd odkazuje na v\u0161echny t\u0159\u00eddy (zvuky, p\u00edsma, obr\u00e1zky) v SWF souboru. Tohle je u\u017eite\u010dn\u00e9 pokud \u017e\u00e1dn\u00fd jin\u00fd skript na n\u011b neodkazuje, aby byly st\u00e1le sou\u010d\u00e1st\u00ed zkompilovan\u00e9ho souboru.