Added AS3 - Show Embed tag over asset classes (readonly)

Added AS3 - Checkbox for exporting assets embedded using `Embed` (-exportembed in commandline)
Added FLA export - AS3 - Using `Embed` tag for DefineBinaryData, images not extending BitmapData
This commit is contained in:
Jindra Petřík
2023-10-01 18:21:38 +02:00
parent ef9e80cb21
commit 4eabf89291
22 changed files with 479 additions and 48 deletions
@@ -326,6 +326,8 @@ public class ScriptPack extends AS3ClassTreeItem {
FileTextWriter writer2 = exportSettings.singleFile ? exportSettings.singleFileWriter : writer;
ConvertData convertData = new ConvertData();
convertData.ignoreFrameScripts = exportSettings.ignoreFrameScripts;
convertData.exportEmbed = exportSettings.exportEmbed;
convertData.exportEmbedFlaMode = exportSettings.exportEmbedFlaMode;
toSource(abcIndex, writer2, abc.script_info.get(scriptIndex).traits.traits, convertData, exportSettings.mode, parallel, exportSettings.ignoreFrameScripts);
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "The file path is probably too long", ex);
@@ -872,4 +872,21 @@ public final class AbcIndexing {
}
return null;
}
public boolean isInstanceOf(ABC abc, int classIndex, DottedChain searchClassName) {
DottedChain clsName = abc.instance_info.get(classIndex).getName(abc.constants).getNameWithNamespace(abc.constants, false);
if (searchClassName.equals(clsName)) {
return true;
}
if (abc.instance_info.get(classIndex).super_index == 0) {
return false;
}
DottedChain parentClassName = abc.constants.getMultiname(abc.instance_info.get(classIndex).super_index).getNameWithNamespace(abc.constants, false);
AbcIndexing.ClassIndex ci = findClass(new TypeItem(parentClassName), abc, null);
if (ci == null) {
return false;
}
return isInstanceOf(ci.abc, ci.index, searchClassName);
}
}
@@ -611,9 +611,15 @@ public class ActionScript3Parser {
expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE);
s = lex();
}
metadata.add(en);
expected(s, lexer.yyline(), SymbolType.BRACKET_CLOSE);
s = lex();
/**
* Skip Embed metadata - these are loaded automatically by the assignment in SymbolClass tag
*/
if (!"Embed".equals(name)) {
metadata.add(en);
}
}
lexer.pushback(s);
return metadata;
@@ -34,6 +34,10 @@ public class ConvertData {
public boolean thisHasDefaultToPrimitive;
public boolean ignoreFrameScripts;
public boolean exportEmbed;
public boolean exportEmbedFlaMode;
public ConvertData() {
deobfuscationMode = Configuration.autoDeobfuscate.get() ? 1 : 0;
@@ -21,7 +21,17 @@ import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.Helper;
import java.util.List;
@@ -80,7 +90,101 @@ public class InstanceInfo {
return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames);
}
public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List<DottedChain> fullyQualifiedNames, boolean allowPrivate) {
public GraphTextWriter getClassHeaderStr(GraphTextWriter writer, ABC abc, List<DottedChain> fullyQualifiedNames, boolean allowPrivate, boolean allowEmbed) {
final String ASSETS_DIR = "/_assets/";
if (allowEmbed) {
if (abc.getSwf() != null) {
String className = getName(abc.constants).getNameWithNamespace(abc.constants, false).toRawString();
CharacterTag ct = abc.getSwf().getCharacterByClass(className);
if (ct != null) {
if (ct instanceof DefineBinaryDataTag) {
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".bin\", mimeType=\"application/octet-stream\")]").newLine();
}
if (ct instanceof ImageTag) {
ImageTag it = (ImageTag) ct;
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ((ImageTag) ct).getImageFormat().getExtension() + "\")]").newLine();
}
if (ct instanceof DefineSpriteTag) {
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + "assets.swf\", symbol=\"" + className + "\")]").newLine();
}
if (ct instanceof DefineSoundTag) {
//should be mp3, otherwise it won't work. Should we convert this?
DefineSoundTag st = (DefineSoundTag) ct;
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + "." + (st.getSoundFormat().formatId == SoundFormat.FORMAT_MP3 ? "mp3" : "wav") + "\")]").newLine();
}
if (ct instanceof FontTag) {
FontTag ft = (FontTag) ct;
boolean hasFontAlignZones = false;
List<CharacterIdTag> sameIdTags = ft.getSwf().getCharacterIdTags(ft.getFontId());
for (CharacterIdTag sit : sameIdTags) {
if (sit instanceof DefineFontAlignZonesTag) {
hasFontAlignZones = true;
break;
}
}
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".ttf\",").newLine();
writer.appendNoHilight("fontName=\"" + ft.getFontName() + "\",").newLine();
writer.appendNoHilight("mimeType=\"application/x-font\",").newLine();
writer.appendNoHilight("fontWeight=\"" + (ft.isBold() ? "bold" : "normal") + "\",").newLine();
writer.appendNoHilight("fontStyle=\"" + (ft.isItalic() ? "italic" : "normal") + "\",").newLine();
String fontChars = ft.getCharacters();
if (!fontChars.isEmpty()) {
Character firstC = null;
Character lastC = null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fontChars.length(); i++) {
char c = fontChars.charAt(i);
if (firstC == null) {
firstC = c;
lastC = c;
continue;
}
if (lastC + 1 != c) {
if (!sb.isEmpty()) {
sb.append(",");
}
if (firstC == lastC) {
sb.append(String.format("U+%04X", (int) firstC));
} else {
sb.append(String.format("U+%04X-%04X", (int) firstC, (int) lastC));
}
firstC = c;
}
lastC = c;
}
if (!sb.isEmpty()) {
sb.append(",");
}
if (firstC == lastC) {
sb.append(String.format("U+%04X", (int) firstC));
} else {
sb.append(String.format("U+%04X-%04X", (int) firstC, (int) lastC));
}
writer.appendNoHilight("unicodeRange=\"").appendNoHilight(sb.toString()).appendNoHilight("\",").newLine();
}
writer.appendNoHilight("advancedAntiAliasing=\"" + (hasFontAlignZones ? "true" : "false") + "\",").newLine();
writer.appendNoHilight("embedAsCFF=\"false\"").newLine();
writer.appendNoHilight(")]").newLine();
}
if (ct instanceof DefineFont4Tag) {
DefineFont4Tag ft4 = (DefineFont4Tag)ct;
writer.appendNoHilight("[Embed(source=\"" + ASSETS_DIR + ct.getCharacterExportFileName() + ".cff\",").newLine();
writer.appendNoHilight("fontName=\"" + ft4.fontName + "\",").newLine();
writer.appendNoHilight("mimeType=\"application/x-font\",").newLine();
writer.appendNoHilight("fontWeight=\"" + (ft4.fontFlagsBold ? "bold" : "normal") + "\",").newLine();
writer.appendNoHilight("fontStyle=\"" + (ft4.fontFlagsItalic ? "italic" : "normal") + "\",").newLine();
writer.appendNoHilight("embedAsCFF=\"true\"").newLine();
writer.appendNoHilight(")]").newLine();
}
}
}
}
String modifiers;
Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants);
modifiers = ns.getPrefix(abc);
@@ -104,7 +208,7 @@ public class InstanceInfo {
writer.appendNoHilight(modifiers + objType);
String classTypeName = abc.constants.getMultiname(name_index).getNameWithNamespace(abc.constants, true).toRawString();
writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, null/* No full names here*/, false, true), HighlightSpecialType.CLASS_NAME, classTypeName);
if (super_index > 0) {
@@ -40,6 +40,10 @@ import com.jpexs.decompiler.flash.helpers.GraphTextWriter;
import com.jpexs.decompiler.flash.helpers.NulWriter;
import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType;
import com.jpexs.decompiler.flash.search.MethodId;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.ScopeStack;
@@ -109,7 +113,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
@Override
public GraphTextWriter toStringHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel, boolean insideInterface) {
abc.instance_info.get(class_info).getClassHeaderStr(writer, abc, fullyQualifiedNames, false);
abc.instance_info.get(class_info).getClassHeaderStr(writer, abc, fullyQualifiedNames, false, false /*??*/);
return writer;
}
@@ -117,13 +121,15 @@ public class TraitClass extends Trait implements TraitWithSlot {
public void convertHeader(Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel) {
}
@Override
public GraphTextWriter toString(AbcIndexing abcIndex, Trait parent, ConvertData convertData, String path, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List<DottedChain> fullyQualifiedNames, boolean parallel, boolean insideInterface) throws InterruptedException {
InstanceInfo instanceInfo = abc.instance_info.get(class_info);
boolean isInterface = instanceInfo.isInterface();
boolean isInterface = instanceInfo.isInterface();
Multiname instanceInfoMultiname = instanceInfo.getName(abc.constants);
DottedChain packageName = instanceInfoMultiname.getNamespace(abc.constants).getName(abc.constants); //assume not null name
@@ -132,11 +138,42 @@ public class TraitClass extends Trait implements TraitWithSlot {
String instanceInfoName = instanceInfoMultiname.getName(abc.constants, fullyQualifiedNames, false, true);
getMetaData(parent, convertData, abc, writer);
boolean allowEmbed = true;
if (convertData.exportEmbedFlaMode) {
allowEmbed = false;
if (abc.getSwf() != null) {
CharacterTag ct = abc.getSwf().getCharacterByClass(instanceInfoMultiname.getNameWithNamespace(abc.constants, false).toRawString());
if (ct == null) {
allowEmbed = false;
} else {
if (ct instanceof DefineBinaryDataTag) {
allowEmbed = true;
}
if (ct instanceof ImageTag) {
allowEmbed = true;
if (abcIndex.isInstanceOf(abc, class_info, DottedChain.parseNoSuffix("flash.display.BitmapData"))) {
allowEmbed = false;
}
}
if (ct instanceof DefineFont4Tag) {
allowEmbed = true;
}
if (ct.getClassNames().size() > 1) {
allowEmbed = true;
}
}
}
}
//class header
instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false);
writer.endTrait();
instanceInfo.getClassHeaderStr(writer, abc, fullyQualifiedNames, false, allowEmbed);
writer.endTrait();
writer.startBlock();
writer.startClass(class_info);
@@ -177,7 +214,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
//instance initializer - constructor
if (!instanceInfo.isInterface()) {
String modifier = "public ";
Multiname m = abc.constants.getMultiname(instanceInfo.name_index);
Multiname m = abc.constants.getMultiname(instanceInfo.name_index);
writer.newLine();
writer.startTrait(GraphTextWriter.TRAIT_INSTANCE_INITIALIZER);
@@ -209,7 +246,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
//instance methods
instanceInfo.instance_traits.toString(abcIndex, new Class[]{TraitClass.class, TraitFunction.class, TraitMethodGetterSetter.class}, this, convertData, path +/*packageName +*/ "/" + instanceInfoName, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel, convertData.ignoreFrameScripts ? frameTraitNames : new ArrayList<>(), isInterface);
writer.endClass();
writer.endClass();
writer.endBlock(); // class
writer.newLine();
return writer;
@@ -236,8 +273,8 @@ public class TraitClass extends Trait implements TraitWithSlot {
}
} else {
convertData.thisHasDefaultToPrimitive = true;
}
ScopeStack newScopeStack = (ScopeStack)scopeStack.clone();
}
ScopeStack newScopeStack = (ScopeStack) scopeStack.clone();
//class initializer
int bodyIndex = abc.findBodyIndex(classInfo.cinit_index);
if (bodyIndex != -1) {
@@ -245,27 +282,27 @@ public class TraitClass extends Trait implements TraitWithSlot {
List<Traits> ts = new ArrayList<>();
ts.add(classInfo.static_traits);
List<MethodBody> callStack = new ArrayList<>();
callStack.add(abc.bodies.get(bodyIndex));
callStack.add(abc.bodies.get(bodyIndex));
if (!abc.instance_info.get(class_info).isInterface()) {
AbcIndexing.ClassIndex cls = abcIndex.findClass(AbcIndexing.multinameToType(abc.instance_info.get(class_info).name_index, abc.constants), abc, scriptIndex);
List<AbcIndexing.ClassIndex> clsList = new ArrayList<>();
cls = cls.parent;
while(cls != null) {
while (cls != null) {
clsList.add(0, cls);
cls = cls.parent;
}
for (AbcIndexing.ClassIndex cls2: clsList) {
for (AbcIndexing.ClassIndex cls2 : clsList) {
newScopeStack.push(new ClassAVM2Item(cls2.abc.instance_info.get(cls2.index).getName(cls2.abc.constants).getNameWithNamespace(cls2.abc.constants, true)));
}
}
}
}
abc.bodies.get(bodyIndex).convert(callStack, abcIndex, convertData, path +/*packageName +*/ "/" + instanceInfoName + ".staticinitializer", exportMode, true, classInfo.cinit_index, scriptIndex, class_info, abc, this, newScopeStack, GraphTextWriter.TRAIT_CLASS_INITIALIZER, writer, fullyQualifiedNames, ts, true, new HashSet<>());
newScopeStack.push(new ClassAVM2Item(abc.instance_info.get(class_info).getName(abc.constants)));
newScopeStack.push(new ClassAVM2Item(abc.instance_info.get(class_info).getName(abc.constants)));
classInitializerIsEmpty = !writer.getMark();
}
}
//constructor - instance initializer
if (!instanceInfo.isInterface()) {
bodyIndex = abc.findBodyIndex(instanceInfo.iinit_index);
@@ -274,7 +311,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
ts.add(instanceInfo.instance_traits);
MethodBody constructorBody = abc.bodies.get(bodyIndex);
List<MethodBody> callStack = new ArrayList<>();
callStack.add(constructorBody);
callStack.add(constructorBody);
constructorBody.convert(callStack, abcIndex, convertData, path +/*packageName +*/ "/" + instanceInfoName + ".initializer", exportMode, false, instanceInfo.iinit_index, scriptIndex, class_info, abc, this, new ScopeStack(), GraphTextWriter.TRAIT_INSTANCE_INITIALIZER, writer, fullyQualifiedNames, ts, true, new HashSet<>());
if (convertData.ignoreFrameScripts) {
@@ -372,7 +409,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
writer.appendNoHilight("instance ").hilightSpecial(abc.constants.multinameToString(ii.name_index), HighlightSpecialType.INSTANCE_NAME).newLine();
writer.indent();
writer.appendNoHilight("extends ").hilightSpecial(abc.constants.multinameToString(ii.super_index), HighlightSpecialType.EXTENDS).newLine();
for(int iface : ii.interfaces) {
for (int iface : ii.interfaces) {
writer.appendNoHilight("implements ").hilightSpecial(abc.constants.multinameToString(iface), HighlightSpecialType.IMPLEMENTS).newLine();
}
if ((ii.flags & InstanceInfo.CLASS_SEALED) == InstanceInfo.CLASS_SEALED) {
@@ -386,7 +423,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
}
if ((ii.flags & InstanceInfo.CLASS_PROTECTEDNS) == InstanceInfo.CLASS_PROTECTEDNS) {
writer.appendNoHilight("flag PROTECTEDNS").newLine();
}
}
if ((ii.flags & InstanceInfo.CLASS_NON_NULLABLE) == InstanceInfo.CLASS_NON_NULLABLE) {
writer.appendNoHilight("flag NON_NULLABLE").newLine();
}
@@ -396,7 +433,7 @@ public class TraitClass extends Trait implements TraitWithSlot {
writer.unindent();
writer.appendNoHilight("end ; instance").newLine();
writer.unindent();
writer.appendNoHilight("end ; class").newLine();
writer.appendNoHilight("end ; class").newLine();
return writer;
}