Added: Generator Templates (.swt) files support and related tags (Flash 3-5)

(CharacterSet, GenCommand, NameCharacter, DefineTextFormat, FontRef)
DefineVideo tag support (Flash 4)
SerialNumber tag support (before Flash 7)
This commit is contained in:
Jindra Petřík
2025-08-29 00:02:38 +02:00
parent d7665cf093
commit 1e9bf6be88
29 changed files with 1243 additions and 53 deletions
+61 -5
View File
@@ -1346,7 +1346,7 @@ public class Main {
JFileChooser fc = new JFileChooser();
fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get()));
FileFilter allSupportedFilter = new FileFilter() {
private final String[] supportedExtensions = new String[]{".swf", ".spl", ".gfx"};
private final String[] supportedExtensions = new String[]{".swf", ".spl", ".swt", ".gfx"};
@Override
public boolean accept(File f) {
@@ -1371,16 +1371,31 @@ public class Main {
public boolean accept(File f) {
return (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swf"))
|| (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".spl"))
|| (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swt"))
|| (f.isDirectory());
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swf_spl");
return AppStrings.translate("filter.swf_spl_swt");
}
};
fc.addChoosableFileFilter(swfFilter);
FileFilter swtFilter = new FileFilter() {
@Override
public boolean accept(File f) {
return (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swt"))
|| f.isDirectory();
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swt");
}
};
fc.addChoosableFileFilter(swtFilter);
FileFilter gfxFilter = new FileFilter() {
@Override
public boolean accept(File f) {
@@ -2235,6 +2250,18 @@ public class Main {
}
};
FileFilter swtFilter = new FileFilter() {
@Override
public boolean accept(File f) {
return (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swt")) || (f.isDirectory());
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swt");
}
};
FileFilter gfxFilter = new FileFilter() {
@Override
public boolean accept(File f) {
@@ -2337,8 +2364,16 @@ public class Main {
fc.addChoosableFileFilter(swfFilter);
fc.setFileFilter(gfxFilter);
} else if (openable instanceof SWF) {
fc.setFileFilter(swfFilter);
SWF swf = (SWF) openable;
if (swf.getFile() != null && swf.getFile().toLowerCase(Locale.ENGLISH).endsWith(".swt")) {
fc.setFileFilter(swtFilter);
fc.addChoosableFileFilter(swfFilter);
} else {
fc.setFileFilter(swfFilter);
fc.addChoosableFileFilter(swtFilter);
}
fc.addChoosableFileFilter(gfxFilter);
} else if (openable instanceof ABC) {
fc.setFileFilter(abcFilter);
}
@@ -2362,6 +2397,12 @@ public class Main {
}
((SWF) openable).gfx = true;
}
if (selFilter == swtFilter) {
if (!fileName.toLowerCase(Locale.ENGLISH).endsWith(".swt")) {
fileName += ".swt";
}
}
if (selFilter == abcFilter) {
if (!fileName.toLowerCase(Locale.ENGLISH).endsWith(".abc")) {
@@ -2446,7 +2487,7 @@ public class Main {
}
fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get()));
FileFilter allSupportedFilter = new FileFilter() {
private final String[] supportedExtensions = new String[]{".swf", ".spl", ".gfx", ".swc", ".zip", ".iggy", ".abc"};
private final String[] supportedExtensions = new String[]{".swf", ".spl", ".swt", ".gfx", ".swc", ".zip", ".iggy", ".abc"};
@Override
public boolean accept(File f) {
@@ -2471,16 +2512,31 @@ public class Main {
public boolean accept(File f) {
return (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swf"))
|| (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".spl"))
|| (f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swt"))
|| (f.isDirectory());
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swf_spl");
return AppStrings.translate("filter.swf_spl_swt");
}
};
fc.addChoosableFileFilter(swfFilter);
FileFilter swtFilter = new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase(Locale.ENGLISH).endsWith(".swt")
|| f.isDirectory();
}
@Override
public String getDescription() {
return AppStrings.translate("filter.swt");
}
};
fc.addChoosableFileFilter(swtFilter);
FileFilter swcFilter = new FileFilter() {
@Override
public boolean accept(File f) {
@@ -1096,4 +1096,7 @@ menu.settings.autoDeobfuscateIdentifiers = Deobfuscate identifiers
deobfuscate_options.deobfuscateIdentifiers = Deobfuscate identifiers
node.unknown = unknown
node.errored = errored
node.errored = errored
filter.swf_spl_swt = SWF files (*.swf, *.spl, *.swt)
filter.swt = Generator templates (*.swt)
@@ -53,6 +53,8 @@ import com.jpexs.decompiler.flash.tags.DefineScalingGridTag;
import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineTextFormatTag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
@@ -67,6 +69,7 @@ import com.jpexs.decompiler.flash.tags.ImportAssets2Tag;
import com.jpexs.decompiler.flash.tags.ImportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.MetadataTag;
import com.jpexs.decompiler.flash.tags.NameCharacterTag;
import com.jpexs.decompiler.flash.tags.PlaceImagePrivateTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
@@ -101,6 +104,7 @@ import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont;
@@ -1111,11 +1115,14 @@ public abstract class AbstractTagTree extends JTree {
public static List<Integer> getMappedTagIdsForClass(Class<?> cls) {
if (cls == DefineSpriteTag.class) {
return Arrays.asList(DefineScalingGridTag.ID, DoInitActionTag.ID);
return Arrays.asList(DefineScalingGridTag.ID, DoInitActionTag.ID, NameCharacterTag.ID);
}
if (FontTag.class.isAssignableFrom(cls)) {
return Arrays.asList(DefineFontNameTag.ID, DefineFontAlignZonesTag.ID, DefineFontInfoTag.ID, DefineFontInfo2Tag.ID);
}
if (StaticTextTag.class.isAssignableFrom(cls)) {
return Arrays.asList(CSMSettingsTag.ID, DefineTextFormatTag.ID);
}
if (TextTag.class.isAssignableFrom(cls)) {
return Arrays.asList(CSMSettingsTag.ID);
}
@@ -1125,6 +1132,12 @@ public abstract class AbstractTagTree extends JTree {
if (cls == DefineButton2Tag.class) {
return Arrays.asList(DefineButtonSoundTag.ID, DefineScalingGridTag.ID);
}
if (ImageTag.class.isAssignableFrom(cls)) {
return Arrays.asList(NameCharacterTag.ID);
}
if (cls == DefineSoundTag.class) {
return Arrays.asList(NameCharacterTag.ID);
}
return new ArrayList<>();
}
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.gui.tagtree;
import com.jpexs.decompiler.flash.tags.CSMSettingsTag;
import com.jpexs.decompiler.flash.tags.CharacterSetTag;
import com.jpexs.decompiler.flash.tags.DebugIDTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
@@ -49,8 +50,10 @@ import com.jpexs.decompiler.flash.tags.DefineShapeTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
import com.jpexs.decompiler.flash.tags.DefineTextFormatTag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DefineVideoTag;
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
import com.jpexs.decompiler.flash.tags.DoABCTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
@@ -61,11 +64,14 @@ import com.jpexs.decompiler.flash.tags.EnableTelemetryTag;
import com.jpexs.decompiler.flash.tags.EndTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.FontRefTag;
import com.jpexs.decompiler.flash.tags.FrameLabelTag;
import com.jpexs.decompiler.flash.tags.GenCommandTag;
import com.jpexs.decompiler.flash.tags.ImportAssets2Tag;
import com.jpexs.decompiler.flash.tags.ImportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.MetadataTag;
import com.jpexs.decompiler.flash.tags.NameCharacterTag;
import com.jpexs.decompiler.flash.tags.PlaceImagePrivateTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
@@ -76,6 +82,7 @@ import com.jpexs.decompiler.flash.tags.ProtectTag;
import com.jpexs.decompiler.flash.tags.RemoveObject2Tag;
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
import com.jpexs.decompiler.flash.tags.ScriptLimitsTag;
import com.jpexs.decompiler.flash.tags.SerialNumberTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.SetTabIndexTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
@@ -96,7 +103,10 @@ import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap;
import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage;
import com.jpexs.decompiler.flash.tags.gfx.ExporterInfo;
import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -104,7 +114,7 @@ import java.util.Map;
*/
public class TagIdClassMap {
private static final Map<Integer, Class<?>> tagIdClassMap = new HashMap<>();
private static final Map<Integer, List<Class<?>>> tagIdClassMap = new HashMap<>();
private static final Map<Class, Integer> classTagIdMap = new HashMap<>();
@@ -189,15 +199,38 @@ public class TagIdClassMap {
addTag(DefineSubImage.ID, DefineSubImage.class);
addTag(ExporterInfo.ID, ExporterInfo.class);
addTag(FontTextureInfo.ID, FontTextureInfo.class);
addTag(DefineVideoTag.ID, DefineVideoTag.class);
addTag(GenCommandTag.ID, GenCommandTag.class);
addTag(FontRefTag.ID, FontRefTag.class);
addTag(DefineTextFormatTag.ID, DefineTextFormatTag.class);
addTag(NameCharacterTag.ID, NameCharacterTag.class);
addTag(CharacterSetTag.ID, CharacterSetTag.class);
addTag(SerialNumberTag.ID, SerialNumberTag.class);
}
private static void addTag(int tagId, Class<?> cl) {
tagIdClassMap.put(tagId, cl);
if (!tagIdClassMap.containsKey(tagId)) {
tagIdClassMap.put(tagId, new ArrayList<>());
}
tagIdClassMap.get(tagId).add(cl);
classTagIdMap.put(cl, tagId);
}
public static Class<?> getClassByTagId(int tagId) {
return tagIdClassMap.get(tagId);
public static Class<?> getClassByTagId(int tagId, int swfVersion) {
List<Class<?>> classes = tagIdClassMap.get(tagId);
if (classes.size() == 1) {
return classes.get(0);
}
for (Class<?> cls : classes) {
SWFVersion ver = cls.getAnnotation(SWFVersion.class);
if (ver != null) {
if (swfVersion >= ver.from() && swfVersion <= ver.to()) {
return cls;
}
}
}
return null;
}
public static Integer getTagIdByClass(Class cl) {
@@ -21,6 +21,7 @@ import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.MainPanel;
import com.jpexs.decompiler.flash.gui.TreeNodeType;
import com.jpexs.decompiler.flash.gui.View;
import com.jpexs.decompiler.flash.tags.CharacterSetTag;
import com.jpexs.decompiler.flash.tags.DebugIDTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
@@ -46,8 +47,10 @@ import com.jpexs.decompiler.flash.tags.DefineShapeTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
import com.jpexs.decompiler.flash.tags.DefineTextFormatTag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DefineVideoTag;
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
import com.jpexs.decompiler.flash.tags.DoABCTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
@@ -57,11 +60,14 @@ import com.jpexs.decompiler.flash.tags.EnableDebuggerTag;
import com.jpexs.decompiler.flash.tags.EnableTelemetryTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.FontRefTag;
import com.jpexs.decompiler.flash.tags.FrameLabelTag;
import com.jpexs.decompiler.flash.tags.GenCommandTag;
import com.jpexs.decompiler.flash.tags.ImportAssets2Tag;
import com.jpexs.decompiler.flash.tags.ImportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.MetadataTag;
import com.jpexs.decompiler.flash.tags.NameCharacterTag;
import com.jpexs.decompiler.flash.tags.PlaceImagePrivateTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObject3Tag;
@@ -269,7 +275,7 @@ public class TagTree extends AbstractTagTree {
}
break;
case TagTreeModel.FOLDER_MOVIES:
ret = Arrays.asList(DefineVideoStreamTag.ID);
ret = Arrays.asList(DefineVideoStreamTag.ID, DefineVideoTag.ID);
break;
case TagTreeModel.FOLDER_SOUNDS:
ret = Arrays.asList(DefineSoundTag.ID);
@@ -281,7 +287,7 @@ public class TagTree extends AbstractTagTree {
if (gfx) {
ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID, DefineCompactedFont.ID);
} else {
ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID);
ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID, FontRefTag.ID);
}
break;
case TagTreeModel.FOLDER_BINARY_DATA:
@@ -293,7 +299,8 @@ public class TagTree extends AbstractTagTree {
RemoveObjectTag.ID, RemoveObject2Tag.ID, ShowFrameTag.ID, FrameLabelTag.ID,
StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID,
SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID,
SetTabIndexTag.ID, PlaceImagePrivateTag.ID);
SetTabIndexTag.ID, PlaceImagePrivateTag.ID,
GenCommandTag.ID);
break;
case TagTreeModel.FOLDER_OTHERS:
ret = Arrays.asList(
@@ -308,7 +315,9 @@ public class TagTree extends AbstractTagTree {
JPEGTablesTag.ID, MetadataTag.ID, ProductInfoTag.ID, ProtectTag.ID, ScriptLimitsTag.ID,
SetBackgroundColorTag.ID,
//SetTabIndexTag.ID,
SymbolClassTag.ID);
SymbolClassTag.ID,
CharacterSetTag.ID
);
break;
}
@@ -142,6 +142,7 @@ import com.jpexs.decompiler.flash.types.CLIPACTIONS;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.HasCharacterId;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.ByteArrayRange;
@@ -2021,17 +2022,29 @@ public class TagTreeContextMenu extends JPopupMenu {
void call(ActionEvent evt, TreeItem item, Class<?> cl, TreeNodeType createNodeType);
}
private void addAddTagMenuFolder(JMenu addTagMenu, String folder, boolean gfx, TreeItem item, AddTagActionListener listener) {
@SuppressWarnings("unchecked")
private void addAddTagMenuFolder(JMenu addTagMenu, String folder, int swfVersion, boolean gfx, TreeItem item, AddTagActionListener listener) {
String folderTranslated = AppStrings.translate("node." + folder);
JMenu folderMenu = new JMenu(folderTranslated);
folderMenu.setIcon(View.getIcon("folder" + folder.toLowerCase(Locale.ENGLISH) + "16"));
Map<Integer, TagTypeInfo> classes = Tag.getKnownClasses();
Map<Integer, List<TagTypeInfo>> classes = Tag.getKnownClasses();
List<Integer> allowedTagTypes = new ArrayList<>(TagTree.getSwfFolderItemNestedTagIds(folder, gfx));
Set<Integer> mappedTagTypes = new LinkedHashSet<>();
for (int i : allowedTagTypes) {
mappedTagTypes.addAll(AbstractTagTree.getMappedTagIdsForClass(classes.get(i).getCls()));
List<TagTypeInfo> tagTypeInfos = classes.get(i);
for (TagTypeInfo tagTypeInfo : tagTypeInfos) {
if (tagTypeInfos.size() > 1) {
SWFVersion ver = (SWFVersion) tagTypeInfo.getCls().getAnnotation(SWFVersion.class);
if (ver != null) {
if (swfVersion < ver.from() || swfVersion > ver.to()) {
continue;
}
}
}
mappedTagTypes.addAll(AbstractTagTree.getMappedTagIdsForClass(tagTypeInfo.getCls()));
}
}
if (allowedTagTypes.isEmpty() && mappedTagTypes.isEmpty()) {
return;
@@ -2053,7 +2066,6 @@ public class TagTreeContextMenu extends JPopupMenu {
private void addAddTagInsideMenuItems(TreeItem item) {
AddTagActionListener listener = this::addTagInsideActionPerformed;
Map<Integer, TagTypeInfo> classes = Tag.getKnownClasses();
SWF currentSwf = mainPanel.getCurrentSwf();
if (currentSwf == null) {
return;
@@ -2104,8 +2116,6 @@ public class TagTreeContextMenu extends JPopupMenu {
if (parent == null) {
return;
}
Map<Integer, TagTypeInfo> classes = Tag.getKnownClasses();
SWF currentSwf = mainPanel.getCurrentSwf();
if (currentSwf == null) {
@@ -2175,6 +2185,7 @@ public class TagTreeContextMenu extends JPopupMenu {
}
private void addAddTagMenuItems(List<Integer> allowedTagTypes, JMenu addTagMenu, TreeItem item, AddTagActionListener listener, String parentFolder) {
int swfVersion = mainPanel.getCurrentSwf().version;
if (allowedTagTypes == null) {
boolean gfx = mainPanel.getCurrentSwf().gfx;
@@ -2193,14 +2204,14 @@ public class TagTreeContextMenu extends JPopupMenu {
TagTreeModel.FOLDER_OTHERS
};
for (String folder : folders) {
addAddTagMenuFolder(addTagMenu, folder, gfx, item, listener);
addAddTagMenuFolder(addTagMenu, folder, swfVersion, gfx, item, listener);
}
return;
}
for (Integer tagId : allowedTagTypes) {
final Class<?> cl = TagIdClassMap.getClassByTagId(tagId);
final Class<?> cl = TagIdClassMap.getClassByTagId(tagId, swfVersion);
String className = cl.getSimpleName();
if (className.endsWith("Tag")) {
className = className.substring(0, className.length() - 3);