mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-27 13:54:49 +00:00
feat(xml export): allow external as1/2 scripts, images and defineSounds (#2707)
This commit is contained in:
@@ -1240,6 +1240,10 @@ public final class Configuration {
|
||||
@ConfigurationDefaultBoolean(true)
|
||||
@ConfigurationCategory("display")
|
||||
public static ConfigurationItem<Boolean> showLoadingSpinner = null;
|
||||
|
||||
@ConfigurationDefaultString("")
|
||||
@ConfigurationName("xmlExport.formats")
|
||||
public static ConfigurationItem<String> lastSelectedXmlExportFormats = null;
|
||||
|
||||
private static Map<String, String> configurationDescriptions = new LinkedHashMap<>();
|
||||
private static Map<String, String> configurationTitles = new LinkedHashMap<>();
|
||||
|
||||
@@ -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<File> exportImages(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, ImageExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<File> 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)));
|
||||
|
||||
|
||||
@@ -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<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, ReadOnlyTagList tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<SoundTag> sounds = new ArrayList<>();
|
||||
for (Tag t : tags) {
|
||||
@@ -72,7 +93,7 @@ public class SoundExporter {
|
||||
}
|
||||
}
|
||||
return exportSounds(handler, outdir, sounds, settings, evl);
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> exportSounds(AbortRetryIgnoreHandler handler, String outdir, List<SoundTag> tags, final SoundExportSettings settings, EventListener evl) throws IOException, InterruptedException {
|
||||
List<File> 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))) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,31 @@
|
||||
*/
|
||||
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;
|
||||
@@ -39,8 +58,12 @@ 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;
|
||||
@@ -62,9 +85,11 @@ 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 = 2;
|
||||
@@ -81,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<ASMSource, String> asmExternalFiles = new HashMap<>();
|
||||
if (settings.as12ExportMode != null) {
|
||||
Map<String, ASMSource> externalNameToAsm = swf.getASMs(true);
|
||||
Set<String> 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<Tag, String> tagExternalFiles = new IdentityHashMap<>();
|
||||
List<Tag> imagesList = new ArrayList<>();
|
||||
if (settings.imageExportMode != null) {
|
||||
ImageExportSettings imageExportSetttings = new ImageExportSettings(settings.imageExportMode);
|
||||
Map<Integer, CharacterTag> 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<SoundTag> soundList = new ArrayList<>();
|
||||
if (settings.defineSoundExportMode != null) {
|
||||
SoundExportSettings soundExportSetttings = new SoundExportSettings(settings.defineSoundExportMode);
|
||||
Map<Integer, CharacterTag> 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();
|
||||
@@ -106,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);
|
||||
}
|
||||
@@ -114,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(swf, null, writer, "swf", swf, false);
|
||||
private void exportXml(
|
||||
Map<ASMSource, String> asmExternalFiles,
|
||||
Map<Tag, String> 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<Field> getSwfFieldsCached(Class cls) {
|
||||
@@ -173,7 +320,17 @@ public class SwfXmlExporter {
|
||||
return cls != null && (cls.isArray() || List.class.isAssignableFrom(cls));
|
||||
}
|
||||
|
||||
private void generateXml(SWF swf, Tag currentTag, XMLStreamWriter writer, String name, Object obj, boolean isListItem) throws XMLStreamException {
|
||||
private void generateXml(
|
||||
int major,
|
||||
Map<ASMSource, String> asmExternalFiles,
|
||||
Map<Tag, String> 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) {
|
||||
@@ -221,7 +378,7 @@ public class SwfXmlExporter {
|
||||
writer.writeStartElement(name);
|
||||
int length = Array.getLength(value);
|
||||
for (int i = 0; i < length; i++) {
|
||||
generateXml(swf, currentTag, 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) {
|
||||
@@ -239,16 +396,12 @@ 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;
|
||||
}
|
||||
|
||||
if (obj instanceof Tag) {
|
||||
currentTag = (Tag) obj;
|
||||
}
|
||||
|
||||
writer.writeAttribute("type", clazz.getSimpleName());
|
||||
|
||||
if (obj instanceof UnknownTag) {
|
||||
@@ -258,8 +411,23 @@ 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) {
|
||||
@@ -297,7 +465,26 @@ public class SwfXmlExporter {
|
||||
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
generateXml(swf, currentTag, 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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IdentityKey<Object>, String> asmExternalActions = new LinkedHashMap<>();
|
||||
Map<IdentityKey<Tag>, 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<Object> objKey : asmExternalActions.keySet()) {
|
||||
ASMSource asm = null;
|
||||
String fileName = asmExternalActions.get(objKey);
|
||||
Object obj = objKey.get();
|
||||
if (obj instanceof ASMSource) {
|
||||
asm = (ASMSource) obj;
|
||||
}
|
||||
if (obj instanceof DefineButtonTag) {
|
||||
DefineButtonTag defineButton = (DefineButtonTag) obj;
|
||||
asm = defineButton.getSubItems().get(0);
|
||||
}
|
||||
if (asm != null) {
|
||||
AS2ScriptImporter importer = new AS2ScriptImporter();
|
||||
try {
|
||||
importer.importActionScript(directory.toPath().resolve(fileName).toFile().getAbsolutePath(), asm, null);
|
||||
} catch (InterruptedException ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tagExternalFiles.isEmpty()) {
|
||||
for (IdentityKey<Tag> tagKey : tagExternalFiles.keySet()) {
|
||||
String fileName = tagExternalFiles.get(tagKey);
|
||||
Tag tag = tagKey.get();
|
||||
if (tag == null) {
|
||||
continue;
|
||||
}
|
||||
if (tag instanceof ImageTag) {
|
||||
ImageTag imageTag = (ImageTag) tag;
|
||||
ImageImporter importer = new ImageImporter();
|
||||
importer.importImage(imageTag, Helper.readFile(directory.toPath().resolve(fileName).toFile().getAbsolutePath()), -1);
|
||||
String baseName = new File(fileName).getName();
|
||||
if (baseName.contains(".")) {
|
||||
baseName = baseName.substring(0, baseName.lastIndexOf("."));
|
||||
}
|
||||
String alphaFile = new File(fileName).getParentFile().getAbsolutePath() + "/" + baseName + ".alpha.png";
|
||||
|
||||
if (new File(alphaFile).exists()) {
|
||||
importer.importImageAlpha(imageTag, Helper.readFile(alphaFile));
|
||||
}
|
||||
} else if (tag instanceof DefineSoundTag) {
|
||||
DefineSoundTag defineSoundTag = (DefineSoundTag) tag;
|
||||
SoundImporter importer = new SoundImporter();
|
||||
int format = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
|
||||
if (fileName.toLowerCase(Locale.ENGLISH).endsWith(".mp3")) {
|
||||
format = SoundFormat.FORMAT_MP3;
|
||||
}
|
||||
try (FileInputStream fis = new FileInputStream(directory.toPath().resolve(fileName).toFile().getAbsolutePath())) {
|
||||
importer.importDefineSound(defineSoundTag, fis, format);
|
||||
} catch (SoundImportException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot import sound", ex);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Cannot read sound", ex);
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.WARNING, "Unrecognized tag type for external file: {0}", tag.getTagName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setSwfAndTimelined(SWF swf) {
|
||||
for (Tag t : swf.getTags()) {
|
||||
t.setSwf(swf);
|
||||
t.setSwf(swf, true);
|
||||
t.setTimelined(swf);
|
||||
if (t instanceof DefineSpriteTag) {
|
||||
DefineSpriteTag s = (DefineSpriteTag) t;
|
||||
for (Tag st : s.getTags()) {
|
||||
st.setSwf(swf);
|
||||
st.setSwf(swf, true);
|
||||
st.setTimelined(s);
|
||||
}
|
||||
}
|
||||
@@ -269,7 +352,7 @@ public class SwfXmlImporter {
|
||||
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
|
||||
try {
|
||||
XMLStreamReader reader = xmlFactory.createXMLStreamReader(new StringReader(xml));
|
||||
return processObject(reader, requiredType, swf, null, 1);
|
||||
return processObject(reader, requiredType, swf, null, 1, new HashMap<>(), new HashMap<>());
|
||||
} catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InstantiationException
|
||||
| InvocationTargetException | XMLStreamException ex) {
|
||||
Logger.getLogger(SwfXmlImporter.class.getName()).log(Level.SEVERE, null, ex);
|
||||
@@ -311,7 +394,7 @@ public class SwfXmlImporter {
|
||||
}*/
|
||||
}
|
||||
|
||||
private void processElement(XMLStreamReader reader, Object obj, SWF swf, Tag tag, int xmlExportMajor) throws XMLStreamException {
|
||||
private void processElement(XMLStreamReader reader, Object obj, SWF swf, Tag tag, int xmlExportMajor, Map<IdentityKey<Object>, String> asmExternalActions, Map<IdentityKey<Tag>, String> tagExternalFiles) throws XMLStreamException {
|
||||
// Check if element started and start if needed
|
||||
if (!reader.isStartElement()) {
|
||||
reader.nextTag();
|
||||
@@ -369,6 +452,30 @@ public class SwfXmlImporter {
|
||||
if (name.equals("reserved3") && "FileAttributesTag".equals(attributes.get("type"))) {
|
||||
name = "reservedB";
|
||||
}
|
||||
|
||||
if (name.equals("_externalActions")) {
|
||||
if (xmlExportMajor < XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
logger.log(Level.WARNING, "For _externalActions attribute _xmlExportMajor must be >= 3. The attribute is ignored.");
|
||||
continue;
|
||||
}
|
||||
asmExternalActions.put(new IdentityKey<>(obj), val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (obj instanceof Tag && name.equals("_externalFile")) {
|
||||
if (xmlExportMajor < XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
logger.log(Level.WARNING, "For _externalFile attribute _xmlExportMajor must be >= 3. The attribute is ignored.");
|
||||
continue;
|
||||
}
|
||||
tagExternalFiles.put(new IdentityKey<>((Tag) obj), val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.equals("actionBytes") && attributes.containsKey("_externalActions")) {
|
||||
if (xmlExportMajor >= XML_IMPORT_VERSION_MAJOR_WITH_EXTERNAL_FILES) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.equals("type")) {
|
||||
try {
|
||||
@@ -397,7 +504,7 @@ public class SwfXmlImporter {
|
||||
// Check for list item elements
|
||||
reader.nextTag();
|
||||
while (reader.isStartElement()) {
|
||||
Object childObj = processObject(reader, reqType, swf, tag, xmlExportMajor);
|
||||
Object childObj = processObject(reader, reqType, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
list.add(childObj);
|
||||
|
||||
reader.nextTag();
|
||||
@@ -414,7 +521,7 @@ public class SwfXmlImporter {
|
||||
|
||||
setFieldValue(field, obj, value);
|
||||
} else {
|
||||
Object childObj = processObject(reader, null, swf, tag, xmlExportMajor);
|
||||
Object childObj = processObject(reader, null, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
setFieldValue(field, obj, childObj);
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException
|
||||
@@ -432,7 +539,7 @@ public class SwfXmlImporter {
|
||||
}
|
||||
}
|
||||
|
||||
private Object processObject(XMLStreamReader reader, Class requiredType, SWF swf, Tag tag, int xmlExportMajor) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, XMLStreamException {
|
||||
private Object processObject(XMLStreamReader reader, Class requiredType, SWF swf, Tag tag, int xmlExportMajor, Map<IdentityKey<Object>, String> asmExternalActions, Map<IdentityKey<Tag>, String> tagExternalFiles) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, XMLStreamException {
|
||||
// Check if element started and start if needed
|
||||
if (!reader.isStartElement()) {
|
||||
reader.nextTag();
|
||||
@@ -465,7 +572,7 @@ public class SwfXmlImporter {
|
||||
tag = (Tag) childObj;
|
||||
}
|
||||
|
||||
processElement(reader, childObj, swf, tag, xmlExportMajor);
|
||||
processElement(reader, childObj, swf, tag, xmlExportMajor, asmExternalActions, tagExternalFiles);
|
||||
ret = childObj;
|
||||
} else {
|
||||
String isNullAttr = attributes.get("isNull");
|
||||
|
||||
@@ -377,4 +377,17 @@ public class DefineButton2Tag extends ButtonTag implements ASMSourceContainer {
|
||||
needed.add(rec.characterId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwf(SWF swf, boolean deep) {
|
||||
super.setSwf(swf, deep);
|
||||
|
||||
if (deep) {
|
||||
if (actions != null) {
|
||||
for (BUTTONCONDACTION action : actions) {
|
||||
action.setSourceTag(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,5 +329,5 @@ public class DefineButtonTag extends ButtonTag implements ASMSourceContainer {
|
||||
for (BUTTONRECORD rec : characters) {
|
||||
needed.add(rec.characterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFOutputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD;
|
||||
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
|
||||
import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.MATRIX;
|
||||
@@ -361,4 +362,19 @@ public abstract class PlaceObjectTypeTag extends Tag implements CharacterIdTag,
|
||||
result += "_" + getDepth();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSwf(SWF swf, boolean deep) {
|
||||
super.setSwf(swf, deep);
|
||||
|
||||
if (deep) {
|
||||
CLIPACTIONS clipActions = getClipActions();
|
||||
if (clipActions != null) {
|
||||
for (CLIPACTIONRECORD rec : clipActions.clipActionRecords) {
|
||||
rec.setParentClipActions(clipActions);
|
||||
rec.setSourceTag(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
libsrc/ffdec_lib/src/com/jpexs/helpers/IdentityKey.java
Normal file
44
libsrc/ffdec_lib/src/com/jpexs/helpers/IdentityKey.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.helpers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IdentityKey<T> {
|
||||
private final T value;
|
||||
|
||||
public IdentityKey(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof IdentityKey
|
||||
&& ((IdentityKey<?>) obj).value == value;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public class SwfXmlExportImportTest extends FileTestBase {
|
||||
|
||||
SWF swf2 = new SWF();
|
||||
try ( FileInputStream fis = new FileInputStream(outFile)) {
|
||||
new SwfXmlImporter().importSwf(swf2, fis);
|
||||
new SwfXmlImporter().importSwf(swf2, fis, outFile.getParentFile());
|
||||
}
|
||||
|
||||
if (swf.getTags().size() != swf2.getTags().size()) {
|
||||
|
||||
Reference in New Issue
Block a user