/*
* Copyright (C) 2010-2026 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package com.jpexs.decompiler.flash.console;
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
import com.jpexs.decompiler.flash.ApplicationInfo;
import com.jpexs.decompiler.flash.Bundle;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.OpenableSourceInfo;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFCompression;
import com.jpexs.decompiler.flash.SearchMode;
import com.jpexs.decompiler.flash.SwfOpenException;
import com.jpexs.decompiler.flash.UrlResolver;
import com.jpexs.decompiler.flash.ValueTooLargeException;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.ABCInputStream;
import com.jpexs.decompiler.flash.abc.RenameType;
import com.jpexs.decompiler.flash.abc.ScriptPack;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel;
import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException;
import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser;
import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler;
import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScript3Parser;
import com.jpexs.decompiler.flash.abc.types.Float4;
import com.jpexs.decompiler.flash.abc.types.MethodBody;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.usages.simple.ABCCleaner;
import com.jpexs.decompiler.flash.action.parser.ActionParseException;
import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser;
import com.jpexs.decompiler.flash.action.parser.script.ActionScript2Parser;
import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream;
import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream;
import com.jpexs.decompiler.flash.amf.amf3.Amf3Value;
import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException;
import com.jpexs.decompiler.flash.amf.amf3.Traits;
import com.jpexs.decompiler.flash.amf.amf3.types.ObjectType;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.configuration.ConfigurationItem;
import com.jpexs.decompiler.flash.configuration.TomlConfigurationStorage;
import com.jpexs.decompiler.flash.docs.As12PCodeDocs;
import com.jpexs.decompiler.flash.docs.As3PCodeDocs;
import com.jpexs.decompiler.flash.exporters.BinaryDataExporter;
import com.jpexs.decompiler.flash.exporters.DualPdfGraphics2D;
import com.jpexs.decompiler.flash.exporters.Font4Exporter;
import com.jpexs.decompiler.flash.exporters.FontExporter;
import com.jpexs.decompiler.flash.exporters.FrameExporter;
import com.jpexs.decompiler.flash.exporters.ImageExporter;
import com.jpexs.decompiler.flash.exporters.MorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.MovieExporter;
import com.jpexs.decompiler.flash.exporters.PreviewExporter;
import com.jpexs.decompiler.flash.exporters.ShapeExporter;
import com.jpexs.decompiler.flash.exporters.SoundExporter;
import com.jpexs.decompiler.flash.exporters.SymbolClassExporter;
import com.jpexs.decompiler.flash.exporters.TextExporter;
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ExeExportMode;
import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FontExportMode;
import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode;
import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode;
import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode;
import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode;
import com.jpexs.decompiler.flash.exporters.modes.TextExportMode;
import com.jpexs.decompiler.flash.exporters.script.LinkReportExporter;
import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.Font4ExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.SymbolClassExportSettings;
import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings;
import com.jpexs.decompiler.flash.exporters.swf.SwfToSwcExporter;
import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter;
import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer;
import com.jpexs.decompiler.flash.gui.AppStrings;
import com.jpexs.decompiler.flash.gui.Main;
import com.jpexs.decompiler.flash.gui.SearchInMemory;
import com.jpexs.decompiler.flash.gui.SearchInMemoryListener;
import com.jpexs.decompiler.flash.gui.SwfInMemory;
import com.jpexs.decompiler.flash.gui.helpers.CheckResources;
import com.jpexs.decompiler.flash.gui.jna.platform.win32.Kernel32;
import com.jpexs.decompiler.flash.gui.translator.Translator;
import com.jpexs.decompiler.flash.helpers.FileTextWriter;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.importers.AS2ScriptImporter;
import com.jpexs.decompiler.flash.importers.AS3ScriptImporter;
import com.jpexs.decompiler.flash.importers.As3ScriptReplaceException;
import com.jpexs.decompiler.flash.importers.As3ScriptReplaceExceptionItem;
import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory;
import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface;
import com.jpexs.decompiler.flash.importers.BinaryDataImporter;
import com.jpexs.decompiler.flash.importers.FFDecAs3ScriptReplacer;
import com.jpexs.decompiler.flash.importers.FontImporter;
import com.jpexs.decompiler.flash.importers.ImageImporter;
import com.jpexs.decompiler.flash.importers.MorphShapeImporter;
import com.jpexs.decompiler.flash.importers.MovieImporter;
import com.jpexs.decompiler.flash.importers.ScriptImporterProgressListener;
import com.jpexs.decompiler.flash.importers.ShapeImporter;
import com.jpexs.decompiler.flash.importers.SoundImporter;
import com.jpexs.decompiler.flash.importers.SpriteImporter;
import com.jpexs.decompiler.flash.importers.SwfXmlImporter;
import com.jpexs.decompiler.flash.importers.SymbolClassImporter;
import com.jpexs.decompiler.flash.importers.TextImporter;
import com.jpexs.decompiler.flash.importers.amf.AmfParseException;
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer;
import com.jpexs.decompiler.flash.importers.svg.SvgImporter;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
import com.jpexs.decompiler.flash.tags.FileAttributesTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.PlaceObject4Tag;
import com.jpexs.decompiler.flash.tags.ScriptLimitsTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
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.HasSeparateAlphaChannel;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundImportException;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.base.UnsupportedSamplingRateException;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
import com.jpexs.decompiler.flash.treeitems.OpenableList;
import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.decompiler.flash.xfl.FLAVersion;
import com.jpexs.decompiler.flash.xfl.XFLExportSettings;
import com.jpexs.decompiler.graph.CompilationException;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.MemoryInputStream;
import com.jpexs.helpers.Path;
import com.jpexs.helpers.ProgressListener;
import com.jpexs.helpers.ReflectionTools;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.stat.StatisticData;
import com.jpexs.helpers.stat.Statistics;
import com.jpexs.helpers.streams.SeekableInputStream;
import com.jpexs.helpers.utf8.Utf8Helper;
import com.jpexs.process.Process;
import com.jpexs.process.ProcessTools;
import com.sun.jna.Platform;
import gnu.jpdf.PDFGraphics;
import gnu.jpdf.PDFJob;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import macromedia.asc.util.Decimal128;
import org.fusesource.jansi.Ansi;
import org.fusesource.jansi.AnsiConsole;
/**
* @author JPEXS
*/
public class CommandLineArgumentParser {
private static final Logger logger = Logger.getLogger(CommandLineArgumentParser.class.getName());
private static boolean commandLineMode = false;
private static boolean showStat = false;
private static String stdOut = null;
private static String stdErr = null;
private static final String METADATA_FORMAT_JSLIKE = "jslike";
private static final String METADATA_FORMAT_RAW = "raw";
public static boolean isCommandLineMode() {
return commandLineMode;
}
public static void printConfigurationSettings() {
Map fields = Configuration.getConfigurationFields(false, false);
String[] keys = new String[fields.size()];
keys = fields.keySet().toArray(keys);
Arrays.sort(keys);
System.out.println("Available keys[current setting]-type:");
for (String name : keys) {
Field field = fields.get(name);
if (ConfigurationItem.isInternal(field)) {
continue;
}
ConfigurationItem> item = ConfigurationItem.getItem(field);
Object value = item.get();
Class> type = ConfigurationItem.getConfigurationFieldType(field);
String valueString = objectToString(value, type);
String typeString = objectTypeToString(type);
if (typeString != null) {
System.out.println(name + "[" + valueString + "]-" + typeString);
}
}
}
private static String objectTypeToString(Class> type) {
if (type == String.class) {
return "string";
} else if (type == Calendar.class) {
return "date";
} else if ((type == Integer.class) || (type == Long.class)) {
return "integer";
} else if ((type == Double.class) || (type == Float.class)) {
return "float";
} else if (type == Boolean.class) {
return "bool";
} else if (type.isEnum()) {
return "enum";
}
return null;
}
private static String objectToString(Object obj, Class> type) {
if (obj == null) {
return "null";
}
if (type == String.class) {
//return '"' + obj.toString() + '"';
return obj.toString();
} else if (type == Calendar.class) {
return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(((Calendar) obj).getTime());
} else if ((type == Integer.class) || (type == Long.class) || (type == Double.class) || (type == Float.class)) {
return obj.toString();
} else if (type == Boolean.class) {
return ((boolean) (Boolean) obj) ? "true" : "false";
} else if (type.isEnum()) {
return obj.toString();
}
return null;
}
public static void printCmdLineUsage(String filter, boolean webHelp) {
printCmdLineUsage(ansiSystemOut(), webHelp, filter);
}
public static void printCmdLineUsage(PrintStream out, boolean webHelp, String filter) {
CommandLineHelp.printCmdLineUsage(out, filter);
printCmdLineUsageExamples(out, filter);
System.out.println();
System.out.println("You can use special value \"/dev/stdin\" for input files to read data \r\nfrom standard input (even on Windows)");
}
private static void printCmdLineUsageExamples(PrintStream out, String filter) {
if (filter == null) {
return;
}
AnsiConsole.systemInstall();
out.println();
out.println("@|underline,bold Examples|@:");
final String PREFIX = "ffdec ";
boolean exampleFound = false;
if (filter == null) {
out.println(PREFIX + "myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("export") || filter.equals("format") || filter.equals("selectclass") || filter.equals("onerror")) {
out.println(PREFIX + "-export script \"C:\\decompiled\" myfile.swf");
out.println(PREFIX + "-selectclass com.example.MyClass,com.example.SecondClass -export script \"C:\\decompiled\" myfile.swf");
out.println(PREFIX + "-format script:pcode -export script \"C:\\decompiled\" myfile.swf");
out.println(PREFIX + "-format script:pcode,text:plain -export script,text,image \"C:\\decompiled\" myfile.swf");
out.println(PREFIX + "-format fla:cs5.5 -export fla \"C:\\sources\\myfile.fla\" myfile.swf");
out.println(PREFIX + "-onerror ignore -export script \"C:\\decompiled\" myfile.swf");
out.println(PREFIX + "-onerror retry 5 -export script \"C:\\decompiled\" myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("cli")) {
out.println(PREFIX + "-cli myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("dumpswf")) {
out.println(PREFIX + "-dumpSWF myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("compress")) {
out.println(PREFIX + "-compress myfile.swf myfilecomp.swf");
exampleFound = true;
}
if (filter == null || filter.equals("decompress")) {
out.println(PREFIX + "-decompress myfile.swf myfiledec.swf");
exampleFound = true;
}
if (filter == null || filter.equals("config")) {
out.println(PREFIX + "-config autoDeobfuscate=1,parallelSpeedUp=0 export script \"C:\\decompiled\" myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("deobfuscate")) {
out.println(PREFIX + "-deobfuscate max myas3file_secure.swf myas3file.swf");
exampleFound = true;
}
if (filter == null || filter.equals("enabledebugging")) {
out.println(PREFIX + "-enabledebugging -injectas3 myas3file.swf myas3file_debug.swf");
out.println(PREFIX + "-enabledebugging -generateswd myas2file.swf myas2file_debug.swf");
exampleFound = true;
}
if (filter == null || filter.equals("doc")) {
out.println(PREFIX + "-doc -type as3.pcode.instructions -format html");
out.println(PREFIX + "-doc -type as12.pcode.instructions -format html");
out.println(PREFIX + "-doc -type as3.pcode.instructions -format html -locale en -out as3_docs_en.html");
exampleFound = true;
}
if (filter == null || filter.equals("getinstancemetadata")) {
out.println(PREFIX + "-getInstanceMetadata -instance myobj -key keyone myfile.swf");
out.println(PREFIX + "-getInstanceMetadata -instance myobj2 -outputFormat raw -outfile out.amf myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("setinstancemetadata")) {
out.println(PREFIX + "-setInstanceMetadata -instance myobj -key mykey -value 1234 myfile.swf");
out.println(PREFIX + "-setInstanceMetadata -instance myobj -key my -inputFormat raw -datafile value.amf -outfile modified.swf myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("removeinstancemetadata")) {
out.println(PREFIX + "-removeInstanceMetadata -instance myobj -key mykey -outfile result.swf myfile.swf");
out.println(PREFIX + "-removeInstanceMetadata -instance myobj myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("swf2exe")) {
out.println(PREFIX + "-swf2exe wrapper result.exe myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("header")) {
out.println(PREFIX + "-header myfile.swf");
out.println(PREFIX + "-header -set version 10 -set width 800px -set framerate 23.5 myfile.swf outfile.swf");
out.println(PREFIX + "-header -set displayrect [0,0,800px,600px] myfile.swf outfile.swf");
out.println(PREFIX + "-header -set gfx true myfile.swf outfile.swf");
exampleFound = true;
}
if (!exampleFound) {
out.println("Sorry, no example found for command " + filter + ",\r\nLet us know in issue tracker when you need it.");
}
AnsiConsole.systemUninstall();
//out.println();
//out.println("Instead of \"java -jar ffdec.jar\" you can use ffdec.bat on Windows, ffdec.sh on Linux/MacOs");
}
/**
* Parses the console arguments
*
* @param arguments Arguments
* @return paths to the file which should be opened or null
* @throws java.io.IOException On error
*/
public static String[] parseArguments(String[] arguments) throws IOException {
return parseArgumentsInternal(arguments);
}
private static PrintStream ansiSystemOut() {
return new PrintStream(System.out) {
@Override
public void print(String s) {
super.print(Ansi.ansi().render(s));
}
};
}
private static void ansiPrint(String str) {
System.out.print(Ansi.ansi().render(str));
}
private static void ansiPrintln(String str) {
System.out.println(Ansi.ansi().render(str));
}
private static String[] parseArgumentsInternal(String[] arguments) throws IOException {
Level traceLevel = Level.WARNING;
Stack args = new Stack<>();
for (int i = arguments.length - 1; i >= 0; i--) {
String arg = arguments[i];
if (arg.length() > 0) {
args.add(arg);
}
}
AbortRetryIgnoreHandler handler = null;
UrlResolver urlResolver = new ConsoleUrlResolver(false, false, false, new HashMap<>());
Map format = new HashMap<>();
Map changedImports = new LinkedHashMap<>();
double zoom = 1;
Double morphDuration = PreviewExporter.MORPH_SHAPE_DEFAULT_DURATION;
int morphNumFrames = 10;
String charset = Charset.defaultCharset().name();
boolean cliMode = false;
boolean air = false;
boolean exportEmbed = false;
boolean transparentBackground = false;
Selection selection = new Selection();
Selection selectionIds = new Selection();
List selectionClasses = null;
String nextParam = null;
String nextParamOriginal = null;
int subLength = 1;
OUTER:
while (true) {
nextParamOriginal = args.pop();
if (nextParamOriginal != null) {
nextParam = nextParamOriginal.toLowerCase(Locale.ENGLISH);
}
if (nextParam == null) {
nextParam = "";
}
switch (nextParam) {
case "-air":
air = true;
break;
case "-cli":
cliMode = true;
break;
case "-selectid":
selectionIds = parseSelect(args, false, "selectid");
break;
case "-select":
selection = parseSelect(args, true, "select");
break;
case "-selectclass":
selectionClasses = parseSelectClass(args);
break;
case "-sublength":
subLength = parseSubLength(args);
break;
case "-exportembed":
exportEmbed = true;
break;
case "-ignorebackground":
transparentBackground = true;
break;
case "-zoom":
zoom = parseZoom(args);
break;
case "-morphduration":
morphDuration = parseMorphDuration(args);
break;
case "-morphnumframes":
morphNumFrames = parseMorphNumFrames(args);
break;
case "-format":
format = parseFormat(args);
break;
case "-charset":
charset = parseCharset(args);
break;
case "-config":
parseConfig(args);
if (args.isEmpty()) {
Configuration.saveConfig();
System.out.println("Configuration saved");
return null;
}
break;
case "-configfile":
parseConfigFile(args);
break;
case "-onerror":
handler = parseOnError(args);
break;
case "-timeout":
parseTimeout(args);
break;
case "-exporttimeout":
parseExportTimeout(args);
break;
case "-exportfiletimeout":
parseExportFileTimeout(args);
break;
case "-stat":
parseStat(args);
break;
case "-info":
parseInfo(args, charset);
System.exit(0);
break;
case "-stdout":
parseStdOut(args);
break;
case "-stderr":
parseStdErr(args);
break;
case "-affinity":
parseAffinity(args);
break;
case "-priority":
parsePriority(args);
break;
case "-verbose":
traceLevel = Level.FINE;
break;
case "-debug":
for (int i = 0; i < arguments.length; i++) {
System.out.println(i + ".:" + arguments[i]);
}
Configuration._debugMode.set(true);
break;
case "-importassets":
urlResolver = parseImportAssets(args, changedImports);
break;
case "-changeimport":
parseChangeImport(args, changedImports);
break;
default:
break OUTER;
}
if (args.isEmpty()) {
return null;
}
}
String command = "";
if (nextParam == null) {
nextParam = "";
}
if (nextParam.startsWith("-")) {
command = nextParam.substring(1);
}
if (command.equals("header")) {
parseHeader(args);
System.exit(0);
} else if (command.equals("translator")) {
Translator.main(new String[]{});
} else if (command.equals("swf2exe")) {
parseSwf2Exe(args, charset);
System.exit(0);
} else if (command.equals("abcclean")) {
parseAbcClean(args, charset);
System.exit(0);
} else if (command.equals("abcmerge")) {
parseAbcMerge(args, charset);
System.exit(0);
} else if (command.equals("swf2swc")) {
parseSwf2Swc(args, charset);
System.exit(0);
} else if (command.equals("linkreport")) {
parseLinkReport(selectionClasses, args, charset);
System.exit(0);
} else if (command.equals("getinstancemetadata")) {
parseGetInstanceMetadata(args, charset);
System.exit(0);
} else if (command.equals("setinstancemetadata")) {
parseSetInstanceMetadata(args, charset);
System.exit(0);
} else if (command.equals("removeinstancemetadata")) {
parseRemoveInstanceMetadata(args, charset);
System.exit(0);
} else if (command.equals("removefromcontextmenu")) {
if (!args.isEmpty()) {
badArguments(command);
}
ContextMenuTools.addToContextMenu(false, true);
System.exit(0);
} else if (command.equals("addtocontextmenu")) {
if (!args.isEmpty()) {
badArguments(command);
}
ContextMenuTools.addToContextMenu(true, true);
System.exit(0);
} else if (command.equals("proxy")) {
parseProxy(args);
} else if (command.equals("export")) {
parseExport(selectionClasses, selection, selectionIds, args, handler, traceLevel, format, zoom, charset, exportEmbed, transparentBackground, urlResolver, subLength, morphDuration, morphNumFrames);
System.exit(0);
} else if (command.equals("compress")) {
parseCompress(args);
System.exit(0);
} else if (command.equals("decompress")) {
parseDecompress(args);
System.exit(0);
} else if (command.equals("encrypt")) {
parseDecrypt(args);
System.exit(0);
} else if (command.equals("decrypt")) {
parseDecrypt(args);
System.exit(0);
} else if (command.equals("swf2xml")) {
parseSwf2Xml(args, charset);
System.exit(0);
} else if (command.equals("xml2swf")) {
parseXml2Swf(args, charset);
System.exit(0);
} else if (command.equals("extract")) {
parseExtract(args);
System.exit(0);
} else if (command.equals("memorysearch")) {
parseMemorySearch(args);
System.exit(0);
} else if (command.equals("deobfuscate")) {
parseDeobfuscate(args, charset);
System.exit(0);
} else if (command.equals("renameinvalididentifiers")) {
parseRenameInvalidIdentifiers(args);
System.exit(0);
} else if (command.equals("dumpswf")) {
parseDumpSwf(args);
System.exit(0);
} else if (command.equals("dumpas2")) {
parseDumpAS2(args, charset);
System.exit(0);
} else if (command.equals("dumpas3")) {
parseDumpAS3(args, charset);
System.exit(0);
} else if (command.equals("enabledebugging")) {
parseEnableDebugging(args, charset);
System.exit(0);
} else if (command.equals("flashpaper2pdf")) {
parseFlashPaperToPdf(selection, zoom, args, charset);
System.exit(0);
} else if (command.equals("replace")) {
parseReplace(args, charset, air);
System.exit(0);
} else if (command.equals("replacealpha")) {
parseReplaceAlpha(args, charset);
System.exit(0);
} else if (command.equals("replacecharacter")) {
parseReplaceCharacter(args, charset);
System.exit(0);
} else if (command.equals("replacecharacterid")) {
parseReplaceCharacterId(args, charset);
System.exit(0);
} else if (command.equals("convert")) {
parseConvert(args, charset);
System.exit(0);
} else if (command.equals("remove")) {
parseRemove(args, charset);
System.exit(0);
} else if (command.equals("removecharacter")) {
parseRemoveCharacter(args, false, charset);
System.exit(0);
} else if (command.equals("removecharacterwithdependencies")) {
parseRemoveCharacter(args, true, charset);
System.exit(0);
} else if (command.equals("doc")) {
parseDoc(args);
} else if (command.equals("importsymbolclass")) {
parseImportSymbolClass(args, charset);
System.exit(0);
} else if (command.equals("importmovies")) {
parseImportMovies(args, charset);
System.exit(0);
} else if (command.equals("importsounds")) {
parseImportSounds(args, charset);
System.exit(0);
} else if (command.equals("importshapes")) {
parseImportShapes(args, charset);
System.exit(0);
} else if (command.equals("importimages")) {
parseImportImages(args, charset);
System.exit(0);
} else if (command.equals("importsprites")) {
parseImportSprites(args, charset);
System.exit(0);
} else if (command.equals("importtext")) {
parseImportText(args, charset);
System.exit(0);
} else if (command.equals("importscript")) {
parseImportScript(args, charset, air, handler);
System.exit(0);
} else if (command.equals("as3compiler")) {
ActionScript3Parser.compile(null /*?*/, args.pop(), args.pop(), 0, 0);
} else if (nextParam.equals("--debugtool")) {
parseDebugTool(args, charset);
} else if (nextParam.equals("--compareresources")) {
parseCompareResources(args);
System.exit(0);
} else if (nextParam.equals("--resourcedates")) {
parseResourceDates(args);
System.exit(0);
} else if (nextParam.equals("-listconfigs")) {
printHeader();
printConfigurationSettings();
System.exit(0);
} else if (nextParam.equals("-storeconfigfile")) {
parseStoreConfigFile(args);
System.exit(0);
} else if (nextParam.equals("-help") || nextParam.equals("--help") || nextParam.equals("/?") || nextParam.equals("\\_") /* /? translates as this on windows */) {
parseHelp(args);
System.exit(0);
} else if (nextParam.equals("--webhelp")) { //for generating commandline usage on webpages
ByteArrayOutputStream whbaos = new ByteArrayOutputStream();
printCmdLineUsage(new PrintStream(whbaos, true), true, null);
String wh = new String(whbaos.toByteArray());
wh = wh.replace("<", "<").replace(">", ">");
System.out.println(wh);
System.exit(0);
} else {
args.push(nextParamOriginal); // file names should be the original one
List fileNames = new ArrayList<>();
boolean allParamIsAFile = true;
while (!args.isEmpty()) {
String arg = args.pop();
if (arg.equals("-custom")) {
parseCustom(args);
break;
}
fileNames.add(arg);
File file = new File(arg);
if (!file.exists() || !file.isFile()) {
allParamIsAFile = false;
}
}
if (allParamIsAFile) {
String[] fileNamesArray = fileNames.toArray(new String[fileNames.size()]);
if (cliMode) {
loadFiles(fileNamesArray);
return null;
} else {
return fileNamesArray;
}
} else {
badArguments();
}
}
return null;
}
public static void printHeader() {
AnsiConsole.systemInstall();
ansiPrintln(ApplicationInfo.cliApplicationVerName);
AnsiConsole.systemUninstall();
for (int i = 0; i < ApplicationInfo.applicationVerName.length(); i++) {
System.out.print("-");
}
System.out.println();
}
public static void badArguments() {
badArguments(null);
}
public static void badArguments(String command) {
System.err.println("Error: Bad Commandline Arguments!");
printCmdLineUsage(command, false);
System.exit(1);
}
@SuppressWarnings("unchecked")
private static void setConfigurations(String cfgStr) {
String[] cfgs;
if (cfgStr.contains(",")) {
cfgs = cfgStr.split(",");
} else {
cfgs = new String[]{cfgStr};
}
Map fields = Configuration.getConfigurationFields(true, false);
for (String c : cfgs) {
String[] cp = c.split("=");
if (cp.length == 1) {
cp = new String[]{cp[0], "1"};
}
String nameLowerCase = cp[0].toLowerCase(Locale.ENGLISH);
Field field = fields.get(nameLowerCase);
ConfigurationItem> item = ConfigurationItem.getItem(field);
String stringValue = cp[1];
Class> type = ConfigurationItem.getConfigurationFieldType(field);
if (type == String.class) {
System.out.println("Config " + item.getName() + " set to " + stringValue);
((ConfigurationItem) item).set(stringValue);
} else if (type == Calendar.class) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date dateValue;
try {
dateValue = dateFormat.parse(stringValue);
GregorianCalendar calendarValue = new GregorianCalendar();
calendarValue.setTime(dateValue);
((ConfigurationItem) item).set(calendarValue);
} catch (ParseException ex) {
Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex);
}
} else if (type == Integer.class) {
int intValue = Integer.parseInt(stringValue);
((ConfigurationItem) item).set(intValue);
} else if ((type == Integer.class) || (type == Long.class)) {
long longValue = Long.parseLong(stringValue);
((ConfigurationItem) item).set(longValue);
} else if (type == Double.class) {
double doubleValue = Double.parseDouble(stringValue);
((ConfigurationItem) item).set(doubleValue);
} else if (type == Float.class) {
float floatValue = Float.parseFloat(stringValue);
((ConfigurationItem) item).set(floatValue);
} else if (type == Boolean.class) {
Boolean boolValue = parseBooleanConfigValue(stringValue);
if (boolValue != null) {
System.out.println("Config " + item.getName() + " set to " + boolValue);
((ConfigurationItem) item).set(boolValue);
} else {
System.out.println("Invalid config value for " + item.getName() + ": " + stringValue);
}
} else if (type.isEnum()) {
Enum enumValue = Enum.valueOf((Class) type, stringValue);
ConfigurationItem uncheckedItem = (ConfigurationItem) item;
uncheckedItem.set(enumValue);
}
if ("locale".equals(nameLowerCase)) {
Main.initLang();
}
}
}
private static Boolean parseBooleanConfigValue(String value) {
if (value == null) {
return null;
}
Boolean bValue = null;
value = value.toLowerCase(Locale.ENGLISH);
if (value.equals("0") || value.equals("false") || value.equals("no") || value.equals("off")) {
bValue = false;
}
if (value.equals("1") || value.equals("true") || value.equals("yes") || value.equals("on")) {
bValue = true;
}
return bValue;
}
private static void parseConfig(Stack args) {
if (args.isEmpty()) {
System.err.println("Config values expected");
badArguments("config");
}
setConfigurations(args.pop());
}
private static void parseStoreConfigFile(Stack args) {
boolean comments = false;
boolean all = false;
String configFile;
while (true) {
if (args.isEmpty()) {
System.err.println("Configuration file expected");
badArguments("configfile");
}
configFile = args.pop();
if (configFile.equals("-comments")) {
comments = true;
} else if (configFile.equals("-all")) {
all = true;
} else {
if (!args.isEmpty()) {
badArguments("configfile");
}
break;
}
}
TomlConfigurationStorage storage = new TomlConfigurationStorage();
storage.saveToFile(configFile, comments, !all);
System.out.println("Configuration saved to \"" + configFile + "\"");
}
private static void parseConfigFile(Stack args) {
if (args.isEmpty()) {
System.err.println("Configuration file expected");
badArguments("configfile");
}
String configFile = args.pop();
if (!new File(configFile).exists()) {
System.err.println("Configuration file \"" + configFile + "\" does not exist!");
badArguments("configfile");
}
Configuration.loadFromFile(configFile);
System.out.println("Configuration loaded from the file \"" + configFile + "\"");
}
private static void parseSwf2Exe(Stack args, String charset) {
if (args.size() != 3) {
badArguments("swf2exe");
}
final String type = args.pop();
final File outFile = new File(args.pop());
final File swfFile = new File(args.pop());
ExeExportMode exportMode = enumFromStr(type, ExeExportMode.class);
processReadSWF(swfFile, null, (SWF swf, OutputStream stdout) -> {
Main.saveFileToExe(swf, exportMode, outFile);
}, charset);
}
private static void parseAbcMerge(Stack args, String charset) {
if (args.size() < 2) {
badArguments("abcmerge");
}
final File outFile = new File(args.pop());
final File swfFile = new File(args.pop());
processModifySWF(swfFile, outFile, null, (SWF swf, OutputStream stdout) -> {
List abcList = swf.getAbcList();
if (!abcList.isEmpty() && abcList.size() > 1) {
ABC firstAbc = abcList.get(0).getABC();
for (int i = 1; i < abcList.size(); i++) {
firstAbc.mergeABC(abcList.get(i).getABC());
}
for (int i = 1; i < abcList.size(); i++) {
swf.removeTag((Tag) abcList.get(i));
}
}
}, charset);
}
private static void parseAbcClean(Stack args, String charset) {
if (args.size() < 2) {
badArguments("abcclean");
}
final File inFile = new File(args.pop());
final File outFile = new File(args.pop());
processModifyAbc(inFile, outFile, null, new AbcAction() {
@Override
public void abcAction(ABC abc, OutputStream stdout) throws IOException {
ABCCleaner cleaner = new ABCCleaner();
cleaner.clean(abc);
}
}, charset);
}
private static void parseSwf2Swc(Stack args, String charset) {
if (args.size() < 2) {
badArguments("swf2swc");
}
final File outFile = new File(args.pop());
final File swfFile = new File(args.pop());
processReadSWF(swfFile, null, (SWF swf, OutputStream stdout) -> {
SwfToSwcExporter exporter = new SwfToSwcExporter();
try {
exporter.exportSwf(swf, outFile, false);
} catch (IOException | InterruptedException ex) {
Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex);
}
}, charset);
}
private static void parseLinkReport(List selectionClasses, Stack args, String charset) {
if (args.isEmpty()) {
badArguments("linkreport");
}
File stdOutFile = null;
File swfFile = null;
while (!args.isEmpty()) {
String paramName = args.pop().toLowerCase(Locale.ENGLISH);
switch (paramName) {
case "-outfile":
if (args.empty()) {
System.err.println("Missing output file");
badArguments("linkreport");
}
stdOutFile = new File(args.pop());
break;
default:
if (!args.isEmpty()) {
badArguments("linkreport");
}
swfFile = new File(paramName);
}
}
if (swfFile == null) {
System.err.println("No SWF file specified");
badArguments("getinstancemetadata");
}
processReadSWF(swfFile, stdOutFile, (SWF swf, OutputStream stdout) -> {
LinkReportExporter lre = new LinkReportExporter();
List reportPacks;
try {
reportPacks = selectionClasses != null ? swf.getScriptPacksByClassNames(selectionClasses) : swf.getAS3Packs();
} catch (Exception ex) {
System.err.println("Error while getting packs");
System.exit(1);
return;
}
try {
String reportStr = lre.generateReport(swf, reportPacks, null);
stdout.write(reportStr.getBytes("UTF-8"));
} catch (InterruptedException ex) {
System.err.println("Report generation interrupted");
}
}, charset);
}
private static void parseGetInstanceMetadata(Stack args, String charset) {
if (args.size() < 3) {
badArguments("getinstancemetadata");
}
Set processedParams = new HashSet<>();
String format = METADATA_FORMAT_JSLIKE;
String key = null;
String instance = null;
File stdOutFile = null;
File swfFile = null;
while (!args.empty()) {
String paramName = args.pop().toLowerCase(Locale.ENGLISH);
if (processedParams.contains(paramName)) {
System.err.println("Parameter " + paramName + " can appear only once.");
}
switch (paramName) {
case "-instance":
if (args.isEmpty()) {
System.err.println("Missing instance name");
badArguments("getinstancemetadata");
}
instance = args.pop();
break;
case "-outputformat":
if (args.empty()) {
System.err.println("Missing format value");
badArguments("getinstancemetadata");
}
format = args.pop();
if (!Arrays.asList(METADATA_FORMAT_RAW, METADATA_FORMAT_JSLIKE).contains(format)) {
System.err.println("Invalid output format");
badArguments("getinstancemetadata");
}
break;
case "-key":
if (args.empty()) {
System.err.println("Missing key value");
badArguments("getinstancemetadata");
}
key = args.pop();
break;
case "-datafile":
if (args.empty()) {
System.err.println("Missing datafile file");
badArguments("getinstancemetadata");
}
stdOutFile = new File(args.pop());
break;
default:
if (!args.isEmpty()) {
badArguments("getinstancemetadata");
}
swfFile = new File(paramName);
paramName = null;
}
if (paramName != null) {
processedParams.add(paramName);
}
}
if (instance == null) {
System.err.println("No instance specified");
badArguments("getinstancemetadata");
}
if (swfFile == null) {
System.err.println("No SWF file specified");
badArguments("getinstancemetadata");
}
final String fInstance = instance;
final String fKey = key;
final String fFormat = format;
processReadSWF(swfFile, stdOutFile, new SwfAction() {
@Override
public void swfAction(SWF swf, OutputStream stdout) throws IOException {
if (!processTimelined(swf, stdout)) {
System.err.println("No instance with name " + fInstance + " found");
System.exit(0);
}
}
private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException {
ReadOnlyTagList rtl = tim.getTags();
for (int i = 0; i < rtl.size(); i++) {
Tag t = rtl.get(i);
if (t instanceof Timelined) {
if (processTimelined((Timelined) t, stdout)) {
return true;
}
}
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t;
String instanceName = pt.getInstanceName();
if (fInstance.equals(instanceName)) {
Amf3Value oldValue = pt.getAmfData();
if (oldValue == null) {
System.err.println("No metadata for instance " + instanceName + " found");
System.exit(1); //TODO? Different exit code
}
Object actualValue = oldValue.getValue();
Object displayVal = actualValue;
if (fKey != null) {
if (actualValue instanceof ObjectType) {
ObjectType ot = (ObjectType) actualValue;
if (ot.containsDynamicMember(fKey)) {
displayVal = ot.getDynamicMember(fKey);
} else {
System.err.println("No value with key " + fKey + " exists");
System.err.println("Available keys: " + String.join(",", ot.dynamicMembersKeySet()));
System.exit(1);
}
} else {
System.err.println("Metadata present, but not as Object type, cannot get key " + fKey);
System.exit(1);
}
}
switch (fFormat) {
case METADATA_FORMAT_JSLIKE:
stdout.write(Utf8Helper.getBytes(Amf3Exporter.amfToString(displayVal, " ", System.lineSeparator()) + System.lineSeparator()));
break;
case METADATA_FORMAT_RAW:
Amf3OutputStream aos = new Amf3OutputStream(stdout);
try {
aos.writeValue(displayVal);
} catch (NoSerializerExistsException ex) {
//should not happen
}
break;
}
return true;
}
}
}
return false;
}
}, charset);
}
private static void parseSetInstanceMetadata(Stack args, String charset) {
if (args.size() < 3) {
badArguments("setinstancemetadata");
}
Set processedParams = new HashSet<>();
String format = METADATA_FORMAT_JSLIKE;
String key = null;
String instance = null;
File outFile = null;
File swfFile = null;
String value = null;
File valueFile = null;
while (!args.empty()) {
String paramName = args.pop().toLowerCase(Locale.ENGLISH);
if (processedParams.contains(paramName)) {
System.err.println("Parameter " + paramName + " can appear only once.");
}
switch (paramName) {
case "-instance":
if (args.isEmpty()) {
System.err.println("Missing instance name");
badArguments("setinstancemetadata");
}
instance = args.pop();
break;
case "-inputformat":
if (args.empty()) {
System.err.println("Missing format value");
badArguments("setinstancemetadata");
}
format = args.pop();
if (!Arrays.asList(METADATA_FORMAT_RAW, METADATA_FORMAT_JSLIKE).contains(format)) {
System.err.println("Invalid output format");
badArguments("setinstancemetadata");
}
break;
case "-key":
if (args.empty()) {
System.err.println("Missing key value");
badArguments("setinstancemetadata");
}
key = args.pop();
break;
case "-value":
if (args.empty()) {
System.err.println("Missing value");
badArguments("setinstancemetadata");
}
value = args.pop();
break;
case "-outfile":
if (args.empty()) {
System.err.println("Missing outFile");
badArguments("setinstancemetadata");
}
outFile = new File(args.pop());
break;
case "-datafile":
if (args.empty()) {
System.err.println("Missing datafile file");
badArguments("setinstancemetadata");
}
valueFile = new File(args.pop());
break;
default:
if (!args.isEmpty()) {
badArguments("setinstancemetadata");
}
swfFile = new File(paramName);
paramName = null;
}
if (paramName != null) {
processedParams.add(paramName);
}
}
if (instance == null) {
System.err.println("No instance specified");
badArguments("getinstancemetadata");
}
if (swfFile == null) {
System.err.println("No SWF file specified");
badArguments("getinstancemetadata");
}
if (outFile == null) {
outFile = swfFile;
}
byte[] valueBytes = new byte[]{};
if (valueFile != null) {
try {
valueBytes = Helper.readFileEx(valueFile.getAbsolutePath());
} catch (IOException ex) {
System.err.println("Cannot read value: " + ex.getMessage());
System.exit(1);
return;
}
} else if (value != null) {
valueBytes = Utf8Helper.getBytes(value);
}
if (valueBytes.length == 0) {
valueBytes = Helper.readStream(System.in);
}
if (valueBytes.length < 1) {
System.err.println("No value to set specified");
System.exit(1);
}
Object amfValue = null;
try {
switch (format) {
case METADATA_FORMAT_JSLIKE:
Amf3Importer importer = new Amf3Importer();
amfValue = importer.stringToAmf(value);
break;
case METADATA_FORMAT_RAW:
Amf3InputStream ais = new Amf3InputStream(new MemoryInputStream(valueBytes));
amfValue = ais.readValue("val");
break;
}
} catch (IOException | AmfParseException | NoSerializerExistsException ex) {
System.err.println("Error parsing input value: " + ex.getMessage());
System.exit(1);
return;
}
final String fInstance = instance;
final String fKey = key;
final Object fAmfValue = amfValue;
processModifySWF(swfFile, outFile, null, new SwfAction() {
@Override
public void swfAction(SWF swf, OutputStream stdout) throws IOException {
if (!processTimelined(swf, stdout)) {
System.err.println("No instance with name " + fInstance + " found");
System.exit(0);
}
}
private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException {
ReadOnlyTagList rtl = tim.getTags();
for (int i = 0; i < rtl.size(); i++) {
Tag t = rtl.get(i);
if (t instanceof Timelined) {
if (processTimelined((Timelined) t, stdout)) {
return true;
}
}
if (t instanceof PlaceObjectTypeTag) {
PlaceObjectTypeTag pt = (PlaceObjectTypeTag) t;
String instanceName = pt.getInstanceName();
if (fInstance.equals(instanceName)) {
Amf3Value oldValue = pt.getAmfData();
if (oldValue != null && oldValue.getValue() == null) {
oldValue = null;
}
if (oldValue != null && fKey != null) { //it has AMFData and we are going to set key
Object actualValue = oldValue.getValue();
if (actualValue instanceof ObjectType) { //add it to ObjectType
ObjectType ot = (ObjectType) actualValue;
ot.putDynamicMember(fKey, fAmfValue);
t.setModified(true);
oldValue.setValue(ot);
System.out.println("Key " + fKey + " added");
System.out.println("New instance data for " + instanceName + ":");
System.out.println(Amf3Exporter.amfToString(ot, " ", System.lineSeparator()));
return true;
}
}
PlaceObject4Tag pt4;
if (pt instanceof PlaceObject4Tag) {
pt4 = (PlaceObject4Tag) pt;
} else {
pt4 = new PlaceObject4Tag(
pt.getSwf(), pt.flagMove(), pt.getDepth(), pt.getClassName(), pt.getCharacterId(), pt.getMatrix(), pt.getColorTransform() == null ? null : new CXFORMWITHALPHA(pt.getColorTransform()), pt.getRatio(),
pt.getInstanceName(), pt.getClipDepth(), pt.getFilters(), pt.getBlendMode(), pt.getBitmapCache(), pt.getVisible(), pt.getBackgroundColor(), pt.getClipActions(), pt.getAmfData(), pt.hasImage());
tim.replaceTag(i, pt4);
}
Object newValue;
if (fKey != null) {
ObjectType ot = new ObjectType(new Traits("", true, new ArrayList<>()));
ot.put(fKey, fAmfValue);
newValue = ot;
} else {
newValue = fAmfValue;
}
pt4.amfData = new Amf3Value(newValue);
pt4.setModified(true);
System.out.println("New instance data for " + instanceName + ":");
System.out.println(Amf3Exporter.amfToString(newValue, " ", System.lineSeparator()));
return true;
}
}
}
return false;
}
}, charset);
}
private static void parseRemoveInstanceMetadata(Stack args, String charset) {
if (args.size() < 2) {
badArguments("removeinstancemetadata");
}
Set processedParams = new HashSet<>();
String key = null;
String instance = null;
File swfFile = null;
File outFile = null;
while (!args.empty()) {
String paramName = args.pop().toLowerCase(Locale.ENGLISH);
if (processedParams.contains(paramName)) {
System.err.println("Parameter " + paramName + " can appear only once.");
}
switch (paramName) {
case "-instance":
if (args.isEmpty()) {
System.err.println("Missing instance name");
badArguments("removeinstancemetadata");
}
instance = args.pop();
break;
case "-key":
if (args.empty()) {
System.err.println("Missing key value");
badArguments("removeinstancemetadata");
}
key = args.pop();
break;
case "-outfile":
if (args.empty()) {
System.err.println("Missing outFile");
badArguments("removeinstancemetadata");
}
outFile = new File(args.pop());
break;
default:
if (!args.isEmpty()) {
badArguments("removeinstancemetadata");
}
swfFile = new File(paramName);
paramName = null;
}
if (paramName != null) {
processedParams.add(paramName);
}
}
if (instance == null) {
System.err.println("No instance specified");
badArguments("removeinstancemetadata");
}
if (swfFile == null) {
System.err.println("No SWF file specified");
badArguments("removeinstancemetadata");
}
if (outFile == null) {
outFile = swfFile;
}
final String fInstance = instance;
final String fKey = key;
processModifySWF(swfFile, outFile, null, new SwfAction() {
@Override
public void swfAction(SWF swf, OutputStream stdout) throws IOException {
if (!processTimelined(swf, stdout)) {
System.err.println("No instance with name " + fInstance + " found");
System.exit(0);
}
}
private boolean processTimelined(Timelined tim, OutputStream stdout) throws IOException {
ReadOnlyTagList rtl = tim.getTags();
for (int i = 0; i < rtl.size(); i++) {
Tag t = rtl.get(i);
if (t instanceof Timelined) {
if (processTimelined((Timelined) t, stdout)) {
return true;
}
}
if (t instanceof PlaceObject4Tag) {
PlaceObject4Tag pt4 = (PlaceObject4Tag) t;
String instanceName = pt4.getInstanceName();
if (fInstance.equals(instanceName)) {
Amf3Value oldValue = pt4.getAmfData();
if (oldValue == null) {
System.err.println("No metadata for instance " + instanceName + " found");
System.exit(1); //TODO? Different exit code
}
Object actualValue = oldValue.getValue();
if (fKey != null) {
if (actualValue instanceof ObjectType) {
ObjectType ot = (ObjectType) actualValue;
if (ot.containsDynamicMember(fKey)) {
ot.remove(fKey);
oldValue.setValue(ot);
System.out.println("Key " + fKey + " removed");
System.out.println("New instance data for " + instanceName + ":");
System.out.println(Amf3Exporter.amfToString(ot, " ", System.lineSeparator()));
pt4.setModified(true);
return true;
} else {
System.err.println("No value with key " + fKey + " exists");
System.err.println("Available keys: " + String.join(",", ot.dynamicMembersKeySet()));
System.exit(1);
}
} else {
System.err.println("Metadata present, but not as Object type, cannot remove key " + fKey);
System.exit(1);
}
} else {
pt4.amfData = null;
pt4.setModified(true);
System.out.println("Whole metadata removed for instance " + instanceName);
}
return true;
}
}
}
return false;
}
}, charset);
}
private static class Range {
public Integer prefix;
public Integer min;
public Integer max;
public Range(Integer prefix, Integer min, Integer max) {
this.prefix = prefix;
this.min = min;
this.max = max;
}
public boolean contains(int index) {
return contains(0, index);
}
public boolean contains(int prefix, int index) {
if (this.prefix == null && prefix == 0) {
return false;
}
if (this.prefix != null && this.prefix != prefix) {
return false;
}
int minimum = min == null ? Integer.MIN_VALUE : min;
int maximum = max == null ? Integer.MAX_VALUE : max;
return index >= minimum && index <= maximum;
}
}
private static class Selection {
public List ranges;
public Selection() {
this.ranges = new ArrayList<>();
this.ranges.add(new Range(0, null, null));
}
public Selection(List ranges) {
this.ranges = ranges;
}
public boolean contains(int index) {
return contains(0, index);
}
public boolean contains(int prefix, int index) {
boolean prefixFound = false;
for (Range r : ranges) {
if (r.prefix == prefix || (prefix != 0 && r.prefix == null)) {
prefixFound = true;
}
if (r.contains(prefix, index)) {
return true;
}
}
if (!prefixFound) {
return true;
}
return false;
}
}
private static Selection parseSelect(Stack args, boolean allowPrefix, String command) {
List ret = new ArrayList<>();
if (args.isEmpty()) {
System.err.println("range parameter expected");
badArguments(command);
}
String range = args.pop();
String[] ranges;
if (range.contains(",")) {
ranges = range.split(",", -1);
} else {
ranges = new String[]{range};
}
Integer prefix = 0;
for (String r : ranges) {
Integer min = null;
Integer max = null;
if (r.contains(":")) {
if (!allowPrefix) {
System.err.println("invalid range");
badArguments(command);
}
String[] ps = r.split(":", -1);
if (ps.length != 2) {
System.err.println("invalid range");
badArguments(command);
}
if ("all".equals(ps[0])) {
prefix = null;
} else {
try {
prefix = Integer.parseInt(ps[0]);
} catch (NumberFormatException nfe) {
System.err.println("invalid range");
badArguments(command);
}
if (prefix < 0) {
System.err.println("invalid range");
badArguments(command);
}
}
r = ps[1];
}
if (r.contains("-")) {
String[] ps = r.split("\\-", -1);
if (ps.length != 2) {
System.err.println("invalid range");
badArguments(command);
}
try {
if (!"".equals(ps[0])) {
min = Integer.parseInt(ps[0]);
}
if (!"".equals(ps[1])) {
max = Integer.parseInt(ps[1]);
}
} catch (NumberFormatException nfe) {
System.err.println("invalid range");
badArguments(command);
}
} else {
try {
min = Integer.parseInt(r);
max = min;
} catch (NumberFormatException nfe) {
System.err.println("invalid range");
badArguments(command);
}
}
ret.add(new Range(prefix, min, max));
}
return new Selection(ret);
}
private static int parseSubLength(Stack args) {
if (args.isEmpty()) {
System.err.println("sub length parameter expected");
badArguments("sublength");
}
try {
int subLen = Integer.parseInt(args.pop());
if (subLen < 1) {
System.err.println("Minimum for sub length is 1");
badArguments("sublength");
subLen = 1;
}
return subLen;
} catch (NumberFormatException nfe) {
System.err.println("Invalid sub length");
badArguments("sublength");
}
return 1;
}
private static double parseZoom(Stack args) {
if (args.isEmpty()) {
System.err.println("zoom parameter expected");
badArguments("zoom");
}
try {
return Double.parseDouble(args.pop());
} catch (NumberFormatException nfe) {
System.err.println("invalid zoom");
badArguments("zoom");
}
return 1;
}
private static double parseMorphDuration(Stack args) {
if (args.isEmpty()) {
System.err.println("duration parameter expected");
badArguments("morphduration");
}
try {
double val = Double.parseDouble(args.pop());
if (val <= 0) {
throw new NumberFormatException();
}
return val;
} catch (NumberFormatException nfe) {
System.err.println("invalid duration");
badArguments("morphduration");
}
return 2;
}
private static int parseMorphNumFrames(Stack args) {
if (args.isEmpty()) {
System.err.println("number of frames parameter expected");
badArguments("morphnumframes");
}
try {
int val = Integer.parseInt(args.pop());
if (val < 2) {
throw new NumberFormatException();
}
return val;
} catch (NumberFormatException nfe) {
System.err.println("invalid number of frames");
badArguments("morphnumframes");
}
return 50;
}
private static AbortRetryIgnoreHandler parseOnError(Stack args) {
int errorMode = AbortRetryIgnoreHandler.UNDEFINED;
int retryCount = 0;
if (args.isEmpty()) {
System.err.println("onerror parameter expected");
badArguments("onerror");
}
String errorModeParameter = args.pop();
switch (errorModeParameter) {
case "abort":
errorMode = AbortRetryIgnoreHandler.ABORT;
break;
case "retry":
errorMode = AbortRetryIgnoreHandler.RETRY;
if (args.isEmpty()) {
System.err.println("onerror retry count parameter expected");
badArguments("onerror");
}
try {
retryCount = Integer.parseInt(args.pop());
} catch (NumberFormatException nex) {
System.err.println("Bad retry count number");
}
break;
case "ignore":
errorMode = AbortRetryIgnoreHandler.IGNORE;
break;
}
return new ConsoleAbortRetryIgnoreHandler(errorMode, retryCount);
}
private static void parseTimeout(Stack args) {
if (args.isEmpty()) {
System.err.println("timeout parameter expected");
badArguments("timeout");
}
try {
int timeout = Integer.parseInt(args.pop());
Configuration.decompilationTimeoutSingleMethod.set(timeout);
} catch (NumberFormatException nex) {
System.err.println("Bad timeout value");
}
}
private static void parseExportTimeout(Stack args) {
if (args.isEmpty()) {
System.err.println("timeout parameter expected");
badArguments("exporttimeout");
}
try {
int timeout = Integer.parseInt(args.pop());
Configuration.exportTimeout.set(timeout);
} catch (NumberFormatException nex) {
System.err.println("Bad timeout value");
}
}
private static void parseExportFileTimeout(Stack args) {
if (args.isEmpty()) {
System.err.println("timeout parameter expected");
badArguments("exportfiletimeout");
}
try {
int timeout = Integer.parseInt(args.pop());
Configuration.decompilationTimeoutFile.set(timeout);
} catch (NumberFormatException nex) {
System.err.println("Bad timeout value");
}
}
private static void parseStat(Stack args) {
showStat = true;
Configuration.showStat = showStat;
}
private static void parseStdOut(Stack args) {
if (args.isEmpty()) {
System.err.println("stdOut parameter expected");
badArguments("stdout");
}
stdOut = args.pop();
}
private static void parseStdErr(Stack args) {
if (args.isEmpty()) {
System.err.println("stdErr parameter expected");
badArguments("stderr");
}
stdErr = args.pop();
}
private static void parseAffinity(Stack args) {
if (Platform.isWindows()) {
if (args.isEmpty()) {
System.err.println("affinity parameter expected");
badArguments("affinity");
}
try {
int affinityMask = Integer.parseInt(args.pop());
Kernel32.INSTANCE.SetProcessAffinityMask(Kernel32.INSTANCE.GetCurrentProcess(), affinityMask);
} catch (NumberFormatException nex) {
System.err.println("Bad affinityMask value");
}
} else {
System.err.println("Process affinity setting is only available on Windows platform.");
}
}
private static void parseChangeImport(Stack args, Map changedImports) {
if (args.size() < 2) {
System.err.println("source and target of import expected");
badArguments("changeimport");
}
changedImports.put(args.pop(), args.pop());
}
private static UrlResolver parseImportAssets(Stack args, Map changedImports) {
if (args.isEmpty()) {
System.err.println("importassets options expected");
badArguments("importassets");
}
String impOptionsStr = args.pop();
String[] impOptionsArr = impOptionsStr.split(",", -1);
boolean doResolve = false;
boolean doAsk = false;
boolean localOnly = false;
for (String impOption : impOptionsArr) {
switch (impOption) {
case "yes":
doResolve = true;
break;
case "ask":
doAsk = true;
break;
case "local":
localOnly = true;
break;
default:
System.err.println("incorrect importassets option: " + impOption);
badArguments("importassets");
}
}
return new ConsoleUrlResolver(doResolve, doAsk, localOnly, changedImports);
}
private static void parsePriority(Stack args) {
if (Platform.isWindows()) {
if (args.isEmpty()) {
System.err.println("priority parameter expected");
badArguments("priority");
}
String priority = args.pop();
int priorityClass = 0;
switch (priority) {
case "low":
priorityClass = Kernel32.IDLE_PRIORITY_CLASS;
break;
case "belownormal":
priorityClass = Kernel32.BELOW_NORMAL_PRIORITY_CLASS;
break;
case "normal":
priorityClass = Kernel32.NORMAL_PRIORITY_CLASS;
break;
case "abovenormal":
priorityClass = Kernel32.ABOVE_NORMAL_PRIORITY_CLASS;
break;
case "high":
priorityClass = Kernel32.HIGH_PRIORITY_CLASS;
break;
case "realtime":
priorityClass = Kernel32.REALTIME_PRIORITY_CLASS;
break;
default:
System.err.println("Bad affinityMask value");
}
if (priorityClass != 0) {
Kernel32.INSTANCE.SetPriorityClass(Kernel32.INSTANCE.GetCurrentProcess(), priorityClass);
}
} else {
System.err.println("Process priority setting is only available on Windows platform.");
}
}
private static void parseDebugTool(Stack args, String charset) {
String cmd = args.pop().toLowerCase(Locale.ENGLISH);
switch (cmd) {
case "findtag": {
String folder = args.pop();
String tagIdOrName = args.pop();
int tagId;
try {
tagId = Integer.parseInt(tagIdOrName);
} catch (NumberFormatException e) {
tagId = Tag.getKnownClassesByName().get(tagIdOrName).getId();
}
PrintStream oldOut = System.out;
PrintStream oldErr = System.err;
PrintStream nullStream = new PrintStream(new OutputStream() {
@Override
public void write(int b) {
}
});
System.setOut(nullStream);
System.setErr(nullStream);
Main.initLogging(Configuration._debugMode.get());
File[] files = new File(folder).listFiles(getSwfFilter());
for (File file : files) {
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, file.getAbsolutePath(), file.getName());
try {
SWF swf = new SWF(new FileInputStream(file), sourceInfo.getFile(), sourceInfo.getFileTitle(), Configuration.parallelSpeedUp.get(), charset);
swf.openableList = new OpenableList();
swf.openableList.sourceInfo = sourceInfo;
boolean found = false;
for (Tag tag : swf.getTags()) {
if (tag.getId() == tagId) {
found = true;
break;
}
}
if (found) {
oldOut.println("Tag found in file: " + file.getAbsolutePath());
}
} catch (IOException | InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
System.setOut(oldOut);
System.setErr(oldErr);
Main.initLogging(Configuration._debugMode.get());
break;
}
case "finderrorheader": {
String folder = args.pop();
PrintStream oldOut = System.out;
PrintStream oldErr = System.err;
PrintStream nullStream = new PrintStream(new OutputStream() {
@Override
public void write(int b) {
}
});
System.setOut(nullStream);
System.setErr(nullStream);
Main.initLogging(Configuration._debugMode.get());
File[] files = new File(folder).listFiles(getSwfFilter());
for (File file : files) {
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, file.getAbsolutePath(), file.getName());
try {
SWF swf = new SWF(new FileInputStream(file), sourceInfo.getFile(), sourceInfo.getFileTitle(), Configuration.parallelSpeedUp.get(), charset);
swf.openableList = new OpenableList();
swf.openableList.sourceInfo = sourceInfo;
boolean found = false;
for (Tag tag : swf.getTags()) {
if (tag instanceof JPEGTablesTag) {
JPEGTablesTag jtt = (JPEGTablesTag) tag;
if (ImageTag.hasErrorHeader(jtt.jpegData)) {
found = true;
break;
}
} else if (tag instanceof DefineBitsJPEG2Tag) {
DefineBitsJPEG2Tag jpeg = (DefineBitsJPEG2Tag) tag;
if (ImageTag.hasErrorHeader(jpeg.imageData)) {
found = true;
break;
}
} else if (tag instanceof DefineBitsJPEG3Tag) {
DefineBitsJPEG3Tag jpeg = (DefineBitsJPEG3Tag) tag;
if (ImageTag.hasErrorHeader(jpeg.imageData)) {
found = true;
break;
}
}
}
if (found) {
oldOut.println("Tag found in file: " + file.getAbsolutePath());
}
} catch (IOException | InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
System.setOut(oldOut);
System.setErr(oldErr);
Main.initLogging(Configuration._debugMode.get());
break;
}
}
}
private static void parseCompareResources(Stack args) {
String revision = args.pop().toLowerCase(Locale.ENGLISH);
String revision2 = null;
if (!args.isEmpty()) {
revision2 = args.pop();
}
CheckResources.compareResources(System.out, revision, revision2);
}
private static void parseResourceDates(Stack args) {
CheckResources.checkTranslationDate(System.out);
}
private static void parseHelp(Stack args) {
String filter = null;
if (!args.isEmpty()) {
filter = args.pop();
if (filter.startsWith("-")) {
filter = filter.substring(1);
}
}
printHeader();
printCmdLineUsage(filter, false);
}
private static void parseProxy(Stack args) {
System.err.println("Proxy functionality was REMOVED");
System.exit(1);
}
private static List parseSelectClass(Stack args) {
if (args.size() < 1) {
badArguments("selectclass");
}
List ret = new ArrayList<>();
String classesStr = args.pop();
String[] classes;
if (classesStr.contains(",")) {
classes = classesStr.split(",");
} else {
classes = new String[]{classesStr};
}
ret.addAll(Arrays.asList(classes));
return ret;
}
private static void parseExport(
List selectionClasses,
Selection selection,
Selection selectionIds,
Stack args,
AbortRetryIgnoreHandler handler,
Level traceLevel,
Map formats,
double zoom,
String charset,
boolean exportEmbed,
boolean transparentBackground,
UrlResolver urlResolver,
int subFrameLength,
double morphDuration,
int morphNumFrames
) {
if (args.size() < 3) {
badArguments("export");
}
String[] validExportItems = new String[]{
"script",
"script_as2",
"script_as3",
"image",
"shape",
"morphshape",
"movie",
"font",
"font4",
"frame",
"sprite",
"button",
"sound",
"binarydata",
"symbolclass",
"text",
"all",
"fla",
"xfl"
};
String[] removedExportFormats = new String[]{
"as", "pcode", "hex", "pcodehex", "all_as", "all_pcode", "all_pcodehex", "all_hex", "textplain"
};
if (handler == null) {
handler = new ConsoleAbortRetryIgnoreHandler(AbortRetryIgnoreHandler.UNDEFINED, 0);
}
String exportFormatString = args.pop().toLowerCase(Locale.ENGLISH);
List exportFormats = Arrays.asList(exportFormatString.split(","));
long startTime = System.currentTimeMillis();
File outDirBase = new File(args.pop());
String inFileOrFolderStr = args.pop();
File inFileOrFolder = new File(inFileOrFolderStr);
if (!StdInAwareFileInputStream.STDIN_PATH.equals(inFileOrFolderStr) && !inFileOrFolder.exists()) {
System.err.println("Input SWF file does not exist!");
badArguments("export");
}
if (!args.isEmpty() && args.peek().equals("-selectas3class")) {
System.err.println("Error: -selectas3class parameter was REMOVED. Please use -selectclass instead. See --help for usage.");
System.exit(1);
}
printHeader();
boolean exportOK = true;
List as3classes = new ArrayList<>();
if (selectionClasses != null) {
as3classes.addAll(selectionClasses);
}
Map stat = new HashMap<>();
try {
File[] inFiles;
boolean singleFile = true;
if (inFileOrFolder.isDirectory()) {
singleFile = false;
inFiles = inFileOrFolder.listFiles(getSwfFilter());
} else {
inFiles = new File[]{inFileOrFolder};
}
for (File inFile : inFiles) {
String inFileName = Path.getFileNameWithoutExtension(inFile);
if (stdOut != null) {
String outFilePath = stdOut.replace("{swfFile}", inFileName);
Path.createDirectorySafe(new File(outFilePath).getParentFile());
System.setOut(new PrintStream(new FileOutputStream(outFilePath, true)));
}
if (stdErr != null) {
String errFilePath = stdErr.replace("{swfFile}", inFileName);
Path.createDirectorySafe(new File(errFilePath).getParentFile());
System.setErr(new PrintStream(new FileOutputStream(errFilePath, true)));
Main.initLogging(Configuration._debugMode.get());
}
long startTimeSwf = 0;
if (!singleFile) {
startTimeSwf = System.currentTimeMillis();
System.out.println("Start exporting " + inFile.getName());
}
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, inFile.getAbsolutePath(), inFile.getName());
SWF swf;
try {
swf = new SWF(new BufferedInputStream(new StdInAwareFileInputStream(inFile)), sourceInfo.getFile(), sourceInfo.getFileTitle(), null, Configuration.parallelSpeedUp.get(), false, true, urlResolver, charset);
} catch (FileNotFoundException | SwfOpenException ex) {
// FileNotFoundException when anti virus software blocks to open the file
logger.log(Level.SEVERE, "Failed to open swf: " + inFile.getName(), ex);
continue;
}
swf.openableList = new OpenableList();
swf.openableList.sourceInfo = sourceInfo;
String outDir = outDirBase.getAbsolutePath();
if (!singleFile) {
outDir = Path.combine(outDir, inFile.getName());
}
List extags = new ArrayList<>();
for (Tag t : swf.getTags()) {
if (t instanceof CharacterIdTag) {
CharacterIdTag c = (CharacterIdTag) t;
if (selectionIds.contains(c.getCharacterId())) {
extags.add(t);
}
} else if (selectionIds.contains(0)) {
extags.add(t);
}
}
final Level level = traceLevel;
swf.addEventListener(new EventListener() {
@Override
public void handleExportingEvent(String type, int index, int count, Object data) {
if (level.intValue() <= Level.FINE.intValue()) {
String text = "Exporting ";
if (type != null && type.length() > 0) {
text += type + " ";
}
System.out.println(text + index + "/" + count + " " + data);
}
}
@Override
public void handleExportedEvent(String type, int index, int count, Object data) {
String text = "Exported ";
if (type != null && type.length() > 0) {
text += type + " ";
}
System.out.println(text + index + "/" + count + " " + data);
}
@Override
public void handleEvent(String event, Object data) {
}
});
// First check all the specified export formats
for (String exportFormat : exportFormats) {
if (Arrays.asList(removedExportFormats).contains(exportFormat)) {
System.err.println("Error: Export format : " + exportFormat + " was REMOVED. Run application with --help parameter to see available formats.");
System.exit(1);
} else if (!Arrays.asList(validExportItems).contains(exportFormat)) {
System.err.println("Invalid export item:" + exportFormat);
badArguments("export");
}
}
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
// Here the exportFormats array should contain only validitems
commandLineMode = true;
boolean exportAll = exportFormats.contains("all");
boolean multipleExportTypes = exportAll || exportFormats.size() > 1;
EventListener evl = swf.getExportEventListener();
if (exportAll || exportFormats.contains("image")) {
System.out.println("Exporting images...");
new ImageExporter().exportImages(handler, outDir + (multipleExportTypes ? File.separator + ImageExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("shape")) {
System.out.println("Exporting shapes...");
new ShapeExporter().exportShapes(handler, outDir + (multipleExportTypes ? File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME : ""), swf, new ReadOnlyTagList(extags), new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class), zoom), evl, zoom, aaScale);
}
if (exportAll || exportFormats.contains("morphshape")) {
System.out.println("Exporting morphshapes...");
new MorphShapeExporter().exportMorphShapes(handler, outDir + (multipleExportTypes ? File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class), zoom, aaScale, morphDuration, morphNumFrames), evl);
}
if (exportAll || exportFormats.contains("movie")) {
System.out.println("Exporting movies...");
new MovieExporter().exportMovies(handler, outDir + (multipleExportTypes ? File.separator + MovieExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("font")) {
System.out.println("Exporting fonts...");
new FontExporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("font4")) {
System.out.println("Exporting DefineFont4s...");
new Font4Exporter().exportFonts(handler, outDir + (multipleExportTypes ? File.separator + FontExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new Font4ExportSettings(enumFromStr(formats.get("font4"), Font4ExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("sound")) {
System.out.println("Exporting sounds...");
new SoundExporter().exportSounds(handler, outDir + (multipleExportTypes ? File.separator + SoundExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("binarydata")) {
System.out.println("Exporting binaryData...");
new BinaryDataExporter().exportBinaryData(handler, outDir + (multipleExportTypes ? File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("symbolclass")) {
System.out.println("Exporting symbolClass...");
new SymbolClassExporter().exportNames(handler, outDir + (multipleExportTypes ? File.separator + SymbolClassExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new SymbolClassExportSettings(enumFromStr(formats.get("symbolclass"), SymbolClassExportMode.class)), evl);
}
if (exportAll || exportFormats.contains("text")) {
System.out.println("Exporting texts...");
Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext"));
if (singleTextFile == null) {
singleTextFile = Configuration.textExportSingleFile.get();
}
new TextExporter().exportTexts(handler, outDir + (multipleExportTypes ? File.separator + TextExportSettings.EXPORT_FOLDER_NAME : ""), new ReadOnlyTagList(extags), new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile, zoom), evl);
}
FrameExporter frameExporter = new FrameExporter();
if (exportAll || exportFormats.contains("frame")) {
System.out.println("Exporting frames...");
List frames = new ArrayList<>();
for (int i = 0; i < swf.frameCount; i++) {
if (selection.contains(0, i + 1)) {
frames.add(i);
}
}
FrameExportSettings fes = new FrameExportSettings(enumFromStr(formats.get("frame"), FrameExportMode.class), zoom, transparentBackground, aaScale);
frameExporter.exportFrames(handler, outDir + (multipleExportTypes ? File.separator + FrameExportSettings.EXPORT_FOLDER_NAME : ""), swf, 0, frames, subFrameLength, fes, evl);
}
if (exportAll || exportFormats.contains("sprite")) {
System.out.println("Exporting sprite...");
SpriteExportSettings ses = new SpriteExportSettings(enumFromStr(formats.get("sprite"), SpriteExportMode.class), zoom, aaScale);
for (Tag t : extags) {
if (t instanceof DefineSpriteTag) {
List frames = new ArrayList<>();
int spriteId = ((DefineSpriteTag) t).getCharacterId();
for (int i = 0; i < ((DefineSpriteTag) t).getFrameCount(); i++) {
if (selection.contains(spriteId, i + 1)) {
frames.add(i);
}
}
frameExporter.exportSpriteFrames(handler, outDir + (multipleExportTypes ? File.separator + SpriteExportSettings.EXPORT_FOLDER_NAME : ""), swf, spriteId, frames, subFrameLength, ses, evl);
}
}
}
if (exportAll || exportFormats.contains("button")) {
System.out.println("Exporting buttons...");
ButtonExportSettings bes = new ButtonExportSettings(enumFromStr(formats.get("button"), ButtonExportMode.class), zoom, aaScale);
for (Tag t : extags) {
if (t instanceof ButtonTag) {
frameExporter.exportButtonFrames(handler, outDir + (multipleExportTypes ? File.separator + ButtonExportSettings.EXPORT_FOLDER_NAME : ""), swf, ((ButtonTag) t).getCharacterId(), null, bes, evl);
}
}
}
boolean parallel = Configuration.parallelSpeedUp.get();
Boolean singleScriptFile = parseBooleanConfigValue(formats.get("singlescript"));
if (singleScriptFile == null) {
singleScriptFile = Configuration.scriptExportSingleFile.get();
}
if (parallel && singleScriptFile) {
logger.log(Level.WARNING, AppStrings.translate("export.script.singleFileParallelModeWarning"));
singleScriptFile = false;
}
ScriptExportSettings scriptExportSettings = new ScriptExportSettings(enumFromStr(formats.get("script"), ScriptExportMode.class), singleScriptFile, false, exportEmbed, false);
boolean exportAllScript = exportAll || exportFormats.contains("script");
boolean exportAs2Script = exportAllScript || exportFormats.contains("script_as2");
boolean exportAs3Script = exportAllScript || exportFormats.contains("script_as3");
if (exportAs2Script || exportAs3Script) {
System.out.println("Exporting scripts...");
String scriptsFolder = Path.combine(outDir, ScriptExportSettings.EXPORT_FOLDER_NAME);
Path.createDirectorySafe(new File(scriptsFolder));
String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension());
try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) {
scriptExportSettings.singleFileWriter = writer;
List as3packs = as3classes.isEmpty() ? null : swf.getScriptPacksByClassNames(as3classes);
exportOK = swf.exportActionScript(handler, scriptsFolder, as3classes.isEmpty() ? null : as3packs, scriptExportSettings, parallel, evl, exportAs2Script, exportAs3Script) != null && exportOK;
}
if (showStat) {
Statistics.print();
Statistics.addToMap(stat);
Statistics.clear();
}
}
if (exportFormats.contains("fla")) {
System.out.println("Exporting FLA...");
exportFla(true, outDir, inFile, swf, multipleExportTypes, formats, handler);
}
if (exportFormats.contains("xfl")) {
System.out.println("Exporting XFL...");
exportFla(false, outDir, inFile, swf, multipleExportTypes, formats, handler);
}
if (!singleFile) {
long stopTimeSwf = System.currentTimeMillis();
long time = stopTimeSwf - startTimeSwf;
System.out.println("Export finished: " + inFile.getName() + " Export time: " + Helper.formatTimeSec(time));
}
swf.clearAllCache();
CancellableWorker.cancelBackgroundThreads();
}
} catch (OutOfMemoryError | Exception ex) {
System.err.print("FAIL: Exporting Failed on Exception - ");
logger.log(Level.SEVERE, null, ex);
System.exit(1);
}
if (showStat) {
Statistics.print(stat);
}
long stopTime = System.currentTimeMillis();
long time = stopTime - startTime;
System.out.println("Export finished. Total export time: " + Helper.formatTimeSec(time));
System.out.println(exportOK ? "OK" : "FAIL");
System.exit(exportOK ? 0 : 1);
}
private static void exportFla(boolean compressed, String outDir, File inFile, SWF swf, boolean multipleExportTypes, Map formats, AbortRetryIgnoreHandler handler) throws IOException, InterruptedException {
String exportFormat = compressed ? "fla" : "xfl";
String format = formats.get(exportFormat);
boolean exportScript = true;
if (format != null && format.endsWith("_noscript")) {
format = format.substring(0, format.length() - 9);
exportScript = false;
}
FLAVersion flaVersion = FLAVersion.fromString(format);
if (flaVersion == null) {
flaVersion = FLAVersion.CS6; //Defaults to CS6
}
String outFile = outDir;
if (multipleExportTypes) {
outFile = Path.combine(outFile, exportFormat);
}
String outFileName = inFile.getName().toLowerCase(Locale.ENGLISH).endsWith(".swf") ? inFile.getName().substring(0, inFile.getName().length() - 3) + exportFormat : inFile.getName();
outFile = Path.combine(outFile, outFileName);
XFLExportSettings settings = new XFLExportSettings();
settings.compressed = compressed;
settings.exportScript = exportScript;
try {
if (Configuration.setFFDecVersionInExportedFont.get()) {
swf.exportXfl(handler, outFile, inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), flaVersion, settings, null);
} else {
swf.exportXfl(handler, outFile, inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.APPLICATION_NAME, "1.0.0", Configuration.parallelSpeedUp.get(), flaVersion, settings, null);
}
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error during XFL/FLA export", ex);
}
}
private static void parseDeobfuscate(Stack args, String charset) {
if (args.size() < 3) {
badArguments("deobfuscate");
}
String mode = args.pop();
DeobfuscationLevel lev;
switch (mode) {
case "controlflow":
case "3":
System.err.println("WARNING: Control flow level(3) is not implemented - it is the same as remove traps (2) level.");
lev = DeobfuscationLevel.LEVEL_REMOVE_TRAPS;
break;
case "traps":
case "max":
case "2":
lev = DeobfuscationLevel.LEVEL_REMOVE_TRAPS;
break;
case "deadcode":
case "1":
lev = DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE;
break;
default:
System.err.println("Invalid level, must be one of: controlflow,traps,deadcode or 1,2,3/max");
System.exit(1);
return;
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
File tmpFile = null;
if (inFile.equals(outFile)) {
try {
tmpFile = File.createTempFile("ffdec_deobf_", ".swf");
outFile = tmpFile;
} catch (IOException ex) {
System.err.println("Unable to create temp file");
System.exit(1);
}
}
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile); FileOutputStream fos = new FileOutputStream(outFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
if (!swf.isAS3()) {
System.out.println("Warning: The file is not AS3. Only AS3 deobfuscation from commandline is available.");
System.exit(0);
}
swf.deobfuscate(lev);
swf.saveTo(fos);
if (tmpFile != null) {
inFile.delete();
tmpFile.renameTo(inFile);
tmpFile = null;
System.out.println(inFile + " overwritten.");
}
System.out.println("OK");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
System.exit(1);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error", ex);
System.exit(1);
} finally {
if (tmpFile != null && tmpFile.exists()) {
tmpFile.delete();
}
}
}
private static void parseCompress(Stack args) {
if (args.size() < 2) {
badArguments("compress");
}
boolean result = false;
try {
SWFCompression compression = SWFCompression.ZLIB;
String compressionString = !args.isEmpty() ? args.pop() : null;
if (compressionString != null) {
switch (compressionString.toLowerCase(Locale.ENGLISH)) {
case "zlib":
compression = SWFCompression.ZLIB;
break;
case "lzma":
compression = SWFCompression.LZMA;
break;
default:
System.out.println("Unsupported compression method: " + compressionString);
System.exit(0);
break;
}
}
try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) {
result = SWF.compress(fis, fos, compression);
System.out.println(result ? "OK" : "FAIL");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.exit(result ? 0 : 1);
}
private static void parseEncrypt(Stack args) {
if (args.size() < 2) {
badArguments("encrypt");
}
boolean result = false;
try {
try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) {
result = SWF.encrypt(fis, fos);
System.out.println(result ? "OK" : "FAIL");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.exit(result ? 0 : 1);
}
private static void parseDecrypt(Stack args) {
if (args.size() < 2) {
badArguments("decrypt");
}
boolean result = false;
try {
try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) {
result = SWF.decrypt(fis, fos);
System.out.println(result ? "OK" : "FAIL");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.exit(result ? 0 : 1);
}
private static void parseDecompress(Stack args) {
if (args.size() < 2) {
badArguments("decompress");
}
boolean result = false;
try {
try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) {
result = SWF.decompress(fis, fos);
System.out.println(result ? "OK" : "FAIL");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.exit(result ? 0 : 1);
}
private static void parseSwf2Xml(Stack args, String charset) {
if (args.size() < 2) {
badArguments("swf2xml");
}
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(args.pop())) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
new SwfXmlExporter().exportXml(swf, new File(args.pop()));
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
private static void parseXml2Swf(Stack args, String charset) {
if (args.size() < 2) {
badArguments("xml2swf");
}
try {
SWF swf = new SWF(charset);
try (StdInAwareFileInputStream in = new StdInAwareFileInputStream(args.pop())) {
new SwfXmlImporter().importSwf(swf, in);
}
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(new File(args.pop())))) {
swf.saveTo(fos);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
private static void parseExtract(Stack args) {
if (args.size() < 1) {
badArguments("extract");
}
String fileName = args.pop();
SearchMode mode = SearchMode.ALL;
boolean noCheck = false;
String output = null;
if (args.size() > 0 && args.peek().toLowerCase(Locale.ENGLISH).equals("-o")) {
args.pop();
if (args.size() < 1) {
badArguments("extract");
}
output = args.pop();
}
if (args.size() > 0 && args.peek().toLowerCase(Locale.ENGLISH).equals("nocheck")) {
noCheck = true;
args.pop();
}
if (args.size() > 0) {
String modeStr = args.pop().toLowerCase(Locale.ENGLISH);
switch (modeStr) {
case "biggest":
mode = SearchMode.BIGGEST;
break;
case "smallest":
mode = SearchMode.SMALLEST;
break;
case "first":
mode = SearchMode.FIRST;
break;
case "last":
mode = SearchMode.LAST;
break;
}
}
try {
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, fileName, null);
if (!sourceInfo.isBundle()) {
System.err.println("Error: should be a bundle. (ZIP or non SWF binary file)");
System.exit(1);
}
Bundle bundle = sourceInfo.getBundle(noCheck, mode);
List> streamsToExtract = new ArrayList<>();
for (Map.Entry streamEntry : bundle.getAll().entrySet()) {
InputStream stream = streamEntry.getValue();
stream.reset();
streamsToExtract.add(streamEntry);
}
for (Map.Entry streamEntry : streamsToExtract) {
InputStream stream = streamEntry.getValue();
stream.reset();
String fileNameOut;
if (mode != SearchMode.ALL) {
if (output == null) {
fileNameOut = Path.getFileNameWithoutExtension(new File(fileName)) + ".swf";
} else {
fileNameOut = output;
}
} else {
fileNameOut = streamEntry.getKey() + ".swf";
if (output != null) {
fileNameOut = Path.combine(output, fileNameOut);
}
}
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(fileNameOut))) {
byte[] swfData = new byte[stream.available()];
int cnt = stream.read(swfData);
fos.write(swfData, 0, cnt);
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
private static void parseMemorySearch(Stack args) {
if (args.size() < 1) {
badArguments("memorysearch");
}
if (Platform.isWindows()) {
AtomicInteger cnt = new AtomicInteger();
List procs = new ArrayList<>();
List processList = ProcessTools.listProcesses();
while (args.size() > 0) {
String arg = args.pop();
if (arg.matches("\\d+")) {
int processId = 0;
try {
processId = Integer.parseInt(arg);
} catch (NumberFormatException nfe) {
System.err.println("ProcessId should be integer");
badArguments("memorysearch");
}
boolean found = false;
if (processList != null) {
for (Process process : processList) {
if (process.getPid() == processId) {
if (!procs.contains(process)) {
procs.add(process);
}
found = true;
break; // only 1 process can have this process id
}
}
}
if (!found) {
System.out.println("Process id=" + processId + " was not found.");
}
} else {
boolean found = false;
if (processList != null) {
for (Process process : processList) {
if (process.getFileName().equals(arg)) {
if (!procs.contains(process)) {
procs.add(process);
}
found = true;
}
}
}
if (!found) {
System.out.println("Process name=" + arg + " was not found.");
}
}
}
try {
new SearchInMemory(new SearchInMemoryListener() {
@Override
public void publish(Object... chunks) {
for (Object s : chunks) {
if (s instanceof SwfInMemory) {
SwfInMemory swf = (SwfInMemory) s;
String fileName = cnt.getAndIncrement() + ".swf";
System.out.println("SWF found (" + fileName + "). Version: " + swf.version + ", file size: " + swf.fileSize + ", address: " + swf.address);
Helper.writeFile(fileName, swf.is);
}
}
}
@Override
public void setProgress(int progress) {
// ignore
}
}).search(procs);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
} else {
System.err.println("Memory search is only available on Windows platform.");
}
}
private static void parseRenameInvalidIdentifiers(Stack args) {
if (args.size() < 3) {
badArguments("renameinvalididentifiers");
}
String renameTypeStr = args.pop();
RenameType renameType;
switch (renameTypeStr.toLowerCase(Locale.ENGLISH)) {
case "typenumber":
renameType = RenameType.TYPENUMBER;
break;
case "randomword":
renameType = RenameType.RANDOMWORD;
break;
default:
System.err.println("Invalid rename type:" + renameTypeStr);
badArguments("renameinvalididentifiers");
return;
}
boolean result = false;
try {
try (InputStream fis = new BufferedInputStream(new StdInAwareFileInputStream(args.pop())); OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.pop()))) {
result = SWF.renameInvalidIdentifiers(renameType, fis, fos);
System.out.println(result ? "OK" : "FAIL");
} catch (FileNotFoundException ex) {
System.err.println("File not found.");
System.exit(1);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
System.exit(result ? 0 : 1);
}
private static String parseCharset(Stack args) {
if (args.size() < 1) {
badArguments("charset");
}
String charsetName = args.pop();
boolean charsetValid = false;
try {
if (Charset.isSupported(charsetName)) {
charsetValid = true;
}
} catch (Exception ex) {
charsetValid = false;
}
if (!charsetValid) {
System.err.println("Specified charset is not valid");
badArguments("charset");
}
return charsetName;
}
private static Map parseFormat(Stack args) {
if (args.size() < 1) {
badArguments("format");
}
String fmtStr = args.pop();
String[] fmts;
if (fmtStr.contains(",")) {
fmts = fmtStr.split(",");
} else {
fmts = new String[]{fmtStr};
}
Map ret = new HashMap<>();
for (String fmt : fmts) {
if (!fmt.contains(":")) {
badArguments("format");
}
String[] parts = fmt.split(":");
String key = parts[0].toLowerCase(Locale.ENGLISH);
String val = parts[1].toLowerCase(Locale.ENGLISH);
if (val.contains("webp") && !ImageFormat.WEBP.available()) {
System.err.println("WEBP format is not available on this platform");
badArguments("format");
}
ret.put(key, val);
}
return ret;
}
private static void parseFlashPaperToPdf(Selection selection, double zoom, Stack args, String charset) {
if (args.size() < 2) {
badArguments("flashpaper2pdf");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
printHeader();
int aaScale = Configuration.reduceAntialiasConflationByScalingForExport.get() ? Configuration.reduceAntialiasConflationByScalingValueForExport.get() : 1;
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
PDFJob job = null;
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
int totalPages = 0;
for (Tag t : swf.getTags()) {
if (t instanceof DefineSpriteTag) {
DefineSpriteTag ds = (DefineSpriteTag) t;
String exportName = ds.getExportName();
if (exportName != null && exportName.matches("^page[0-9]+$")) {
int pageNum = Integer.parseInt(exportName.substring(4));
if (pageNum > totalPages) {
totalPages = pageNum;
}
}
}
}
PageFormat pf = new PageFormat();
pf.setOrientation(PageFormat.PORTRAIT);
Paper p = new Paper();
int page = 0;
for (Tag t : swf.getTags()) {
if (t instanceof DefineSpriteTag) {
DefineSpriteTag ds = (DefineSpriteTag) t;
if ("page1".equals(ds.getExportName())) {
page = 1;
job = new PDFJob(new BufferedOutputStream(new FileOutputStream(outFile)));
} else if (page > 0) {
page++;
}
if (("page" + page).equals(ds.getExportName())) {
if (!selection.contains(page)) {
continue;
}
System.out.print("Page " + page + "/" + totalPages + "...");
Timeline tim = ds.getTimeline();
RECT rect = tim.displayRect;
double w = (rect.getWidth() * zoom / SWF.unitDivisor);
double h = (rect.getHeight() * zoom / SWF.unitDivisor);
p.setSize(w, h);
pf.setPaper(p);
Matrix m = new Matrix();
m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom);
m.scale(zoom);
Matrix transformation = m;
Map existingFonts = new HashMap<>();
int fframe = 0;
final Graphics2D g = (Graphics2D) job.getGraphics(pf);
SerializableImage image = new SerializableImage((int) w + 1, (int) h + 1, SerializableImage.TYPE_INT_ARGB_PRE) {
private Graphics2D compositeGraphics;
@Override
public Graphics getGraphics() {
if (compositeGraphics != null) {
return compositeGraphics;
}
final Graphics2D parentGraphics = (Graphics2D) super.getGraphics();
compositeGraphics = new DualPdfGraphics2D(parentGraphics, (PDFGraphics) g, existingFonts);
return compositeGraphics;
}
@Override
public void fillTransparent() {
}
};
int imgWidth = (int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1;
int imgHeight = (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1;
RenderContext renderContext = new RenderContext();
renderContext.cursorPosition = new Point(-1, -1);
renderContext.mouseButton = 0;
renderContext.stateUnderCursor = new ArrayList<>();
try {
tim.toImage(fframe, fframe, renderContext, image, image, false, m, new Matrix(), m, null, zoom, true, new ExportRectangle(rect), new ExportRectangle(rect), m, true, Timeline.DRAW_MODE_ALL, 0, true, new ArrayList<>(), aaScale);
} catch (Exception ex) {
ex.printStackTrace();
}
g.dispose();
if (Thread.currentThread().isInterrupted()) {
return;
}
System.out.println("OK");
}
}
}
if (job == null) {
System.err.println("No pages found. Maybe it is not a FlashPaper file");
System.exit(2);
}
job.end();
} catch (FileNotFoundException ex) {
System.err.println("File not found");
System.exit(1);
} catch (IOException | InterruptedException ex) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseReplace(Stack args, String charset, boolean air) {
if (args.size() < 3) {
badArguments("replace");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
if (args.size() == 1) {
System.out.println("Replacing - only single argument passed, taking it as file to load replacements from");
try {
List lines = Files.readAllLines(Paths.get(args.pop()), StandardCharsets.UTF_8);
Collections.reverse(lines);
args.clear();
args.addAll(lines);
} catch (IOException e) {
System.err.println("I/O Error during reading replacements file");
System.exit(1);
}
if (args.isEmpty()) {
System.err.println("Replacements file is empty.");
System.exit(1);
}
}
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
while (true) {
String objectToReplace = args.pop();
if (objectToReplace.matches("\\d+")) {
// replace character tag
int characterId = 0;
try {
characterId = Integer.parseInt(objectToReplace);
} catch (NumberFormatException nfe) {
System.err.println("CharacterId should be integer");
badArguments("replace");
}
SoundStreamHeadTypeTag soundStreamHead = null;
CharacterTag characterTag = null;
if (characterId == -1) {
//replacing soundstreamhead on main timeline
} else if (swf.getCharacters(false).containsKey(characterId)) {
characterTag = swf.getCharacter(characterId);
} else {
System.err.println("CharacterId does not exist");
System.exit(1);
}
String repFile = args.pop();
byte[] data = Helper.readFile(repFile);
String ext = Path.getExtension(repFile);
int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN;
if (".mp3".equals(ext)) {
soundFormat = SoundFormat.FORMAT_MP3;
}
if ((characterTag == null || (characterTag instanceof DefineSpriteTag)) && (".wav".equals(ext) || ".mp3".equals(ext))) {
ReadOnlyTagList tags = (characterTag == null) ? swf.getTags() : ((DefineSpriteTag) characterTag).getTags();
for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) {
soundStreamHead = (SoundStreamHeadTypeTag) t;
break;
}
}
if (soundStreamHead == null) {
System.err.println((characterTag == null ? "Main timeline " : "DefineSprite(" + characterId + ")") + " does not contain any SoundStreamHead");
System.exit(1);
}
boolean ok = false;
try {
ok = new SoundImporter().importSoundStream(soundStreamHead, new ByteArrayInputStream(data), soundFormat);
} catch (UnsupportedSamplingRateException usre) {
List supportedRatesStr = new ArrayList<>();
for (int i : usre.getSupportedRates()) {
supportedRatesStr.add("" + i);
}
System.err.println("Import FAILED. Input file has unsupported sampling rate (" + usre.getSoundRate() + "). Supported rates for this sound format: " + String.join(", ", supportedRatesStr) + ".");
System.exit(2);
}
if (!ok) {
System.err.println("Import FAILED. Maybe unsupported media type? Only MP3 and uncompressed WAV are available.");
System.exit(1);
}
} else if (characterTag instanceof DefineBinaryDataTag) {
DefineBinaryDataTag defineBinaryData = (DefineBinaryDataTag) characterTag;
new BinaryDataImporter().importData(defineBinaryData, data);
} else if (characterTag instanceof ImageTag) {
int format = parseImageFormat(args);
ImageTag imageTag = (ImageTag) characterTag;
new ImageImporter().importImage(imageTag, data, format);
} else if (characterTag instanceof ShapeTag) {
boolean fill = true;
if (!args.isEmpty() && args.peek().equals("nofill")) {
args.pop();
fill = false;
}
ShapeTag shapeTag = (ShapeTag) characterTag;
if (".svg".equals(ext)) {
String svgText = Helper.readTextFile(repFile);
new SvgImporter().importSvg(shapeTag, svgText);
} else {
int format = parseImageFormat(args);
new ShapeImporter().importImage(shapeTag, data, format, fill);
}
} else if (characterTag instanceof TextTag) {
TextTag textTag = (TextTag) characterTag;
new TextImporter(new MissingCharacterHandler(), new TextImportErrorHandler() {
@Override
public boolean handle(TextTag textTag) {
String msg = "Error during text import.";
logger.log(Level.SEVERE, msg);
return false;
}
@Override
public boolean handle(TextTag textTag, String message, long line) {
String msg = "Error during text import: %text% on line %line%".replace("%text%", message).replace("%line%", Long.toString(line));
logger.log(Level.SEVERE, msg);
return false;
}
}).importText(textTag, new String(data, Utf8Helper.charset));
} else if (characterTag instanceof FontTag) {
FontTag fontTag = (FontTag) characterTag;
Set selChars = new HashSet<>();
Font font;
try {
File fontFile = new File(repFile);
font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
FontTag.addCustomFont(font, fontFile);
List required = Arrays.asList((char) 0x01, (char) 0x00, (char) 0x0D, (char) 0x20);
for (char c = 0; c < Character.MAX_VALUE; c++) {
if (required.contains(c)) {
continue;
}
if (!font.canDisplay(c)) {
continue;
}
if (Utf8Helper.charToCodePoint(c, fontTag.getCodesCharset()) == -1) {
continue;
}
selChars.add(c);
}
} catch (FontFormatException | IOException e) {
System.err.println("replace font tag fail: " + e.getMessage());
System.exit(1);
return;
}
if (font.getSize() != 1024) {
font = font.deriveFont(fontTag.getFontStyle(), 1024);
}
for (char c : selChars) {
if (!fontTag.addCharacter(c, font)) {
break;
}
}
} else if (characterTag instanceof DefineFont4Tag) {
DefineFont4Tag font4 = (DefineFont4Tag) characterTag;
font4.fontData = new ByteArrayRange(data);
font4.setModified(true);
} else if (characterTag instanceof SoundTag) {
SoundTag st = (SoundTag) characterTag;
Integer startFrame = null;
if (!args.isEmpty()) {
if (args.peek().toLowerCase(Locale.ENGLISH).equals("-startframe")) {
args.pop();
if (args.isEmpty()) {
System.err.println("Frame number must be specified");
badArguments("replace");
}
try {
startFrame = Integer.parseInt(args.pop());
} catch (NumberFormatException nfe) {
System.err.println("Frame number should be integer");
badArguments("replace");
}
}
}
boolean ok = false;
SoundImporter soundImporter = new SoundImporter();
try {
ok = soundImporter.importSound(st, new ByteArrayInputStream(data), soundFormat, startFrame);
} catch (UnsupportedSamplingRateException usre) {
List supportedRatesStr = new ArrayList<>();
for (int i : usre.getSupportedRates()) {
supportedRatesStr.add("" + i);
}
System.err.println("Import FAILED. Input file has unsupported sampling rate (" + usre.getSoundRate() + "). Supported rates for this sound format: " + String.join(", ", supportedRatesStr) + ".");
System.exit(2);
} catch (SoundImportException sie) {
ok = false;
}
if (!ok) {
System.err.println("Import FAILED. Maybe unsupported media type? Only MP3 and uncompressed WAV are available.");
System.exit(1);
}
} else if (characterTag instanceof DefineVideoStreamTag) {
DefineVideoStreamTag movie = (DefineVideoStreamTag) characterTag;
try {
new MovieImporter().importMovie(movie, data);
} catch (IOException iex) {
System.err.println("Import FAILED: " + iex.getMessage());
System.exit(1);
}
} else if (characterTag instanceof DefineSpriteTag) {
DefineSpriteTag sprite = (DefineSpriteTag) characterTag;
try {
new SpriteImporter().importSprite(sprite, new ByteArrayInputStream(data));
} catch (IOException iex) {
System.err.println("Import FAILED: " + iex.getMessage());
System.exit(1);
}
} else {
System.err.println("The specified tag type is not supported for import");
System.exit(1);
}
} else {
Map asms = swf.getASMs(true);
boolean found = false;
if (asms.containsKey(objectToReplace)) {
found = true;
// replace AS1/2
String repFile = args.pop();
String repText = Helper.readTextFile(repFile);
ASMSource src = asms.get(objectToReplace);
if (Path.getExtension(repFile).equals(".as")) {
replaceAS2(repText, src);
} else {
replaceAS2PCode(repText, src);
}
} else {
List packs = swf.getAS3Packs();
for (ScriptPack entry : packs) {
if (entry.getClassPath().toString().equals(objectToReplace)) {
found = true;
// replace AS3
String repFile = args.pop();
String repText = Helper.readTextFile(repFile);
ScriptPack pack = entry;
if (Path.getExtension(repFile).equals(".as")) {
replaceAS3(repFile, repText, pack, air);
} else {
// todo: get traits
if (args.isEmpty()) {
badArguments("replace");
}
int bodyIndex = Integer.parseInt(args.pop());
ABC abc = pack.abc;
List resultTraits = abc.getMethodIndexing().findMethodTraits(pack, bodyIndex);
//int classIndex = 0;
//int traitId = 0;
Trait trait = null; //abc.findTraitByTraitId(classIndex, traitId);
if (resultTraits.size() == 1) {
trait = resultTraits.get(0);
}
replaceAS3PCode(repText, abc, bodyIndex, trait);
}
}
}
}
if (!found) {
System.err.println(objectToReplace + " is not recognized as a CharacterId or a script name.");
System.exit(1);
}
}
if (args.isEmpty() || args.peek().startsWith("-")) {
break;
}
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static int parseImageFormat(Stack args) {
if (args.isEmpty()) {
return 0;
}
int res = ImageImporter.getImageTagType(args.peek().toLowerCase(Locale.ENGLISH));
if (res != 0) {
args.pop();
}
return res;
}
private static void parseReplaceAlpha(Stack args, String charset) {
if (args.size() < 4) {
badArguments("replacealpha");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
while (true) {
String objectToReplace = args.pop();
int imageId = 0;
try {
imageId = Integer.parseInt(objectToReplace);
} catch (NumberFormatException nfe) {
System.err.println("ImageId should be integer");
badArguments("replacealpha");
}
if (!swf.getCharacters(false).containsKey(imageId)) {
System.err.println("ImageId does not exist");
System.exit(1);
}
CharacterTag characterTag = swf.getCharacter(imageId);
String repFile = args.pop();
byte[] data = Helper.readFile(repFile);
if (characterTag instanceof HasSeparateAlphaChannel) {
ImageTag imageTag = (ImageTag) characterTag;
new ImageImporter().importImageAlpha(imageTag, data);
} else {
System.err.println("The specified tag type is not supported for alpha channel import");
badArguments("replacealpha");
}
if (args.isEmpty() || args.peek().startsWith("-")) {
break;
}
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseReplaceCharacter(Stack args, String charset) {
if (args.size() < 4) {
badArguments("replacecharacter");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
while (true) {
String objectToReplace = args.pop();
int characterId = 0;
try {
characterId = Integer.parseInt(objectToReplace);
} catch (NumberFormatException nfe) {
System.err.println("CharacterId should be integer");
badArguments("replacecharacter");
}
if (!swf.getCharacters(false).containsKey(characterId)) {
System.err.println("CharacterId does not exist");
System.exit(1);
}
CharacterTag characterTag = swf.getCharacter(characterId);
String newCharacterIdStr = args.pop();
int newCharacterId = 0;
try {
newCharacterId = Integer.parseInt(newCharacterIdStr);
} catch (NumberFormatException nfe) {
System.err.println("NewCharacterId should be integer");
badArguments("replacecharacter");
}
if (!swf.getCharacters(false).containsKey(newCharacterId)) {
System.err.println("NewCharacterId does not exist");
System.exit(1);
}
swf.replaceCharacterTags(characterTag, newCharacterId);
if (args.isEmpty() || args.peek().startsWith("-")) {
break;
}
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseReplaceCharacterId(Stack args, String charset) {
if (args.size() < 3) {
badArguments("replacecharacterid");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
String arg = args.pop().toLowerCase(Locale.ENGLISH);
if (arg.equals("pack")) {
swf.packCharacterIds();
} else if (arg.equals("sort")) {
swf.sortCharacterIds();
} else {
String[] characterIdsStr = arg.split(",");
if (characterIdsStr.length % 2 != 0) {
System.err.println("CharacterId count should be an even number");
badArguments("replacecharacterid");
}
List characterIds = new ArrayList<>();
for (int i = 0; i < characterIdsStr.length; i++) {
int characterId;
try {
characterId = Integer.parseInt(characterIdsStr[i]);
characterIds.add(characterId);
} catch (NumberFormatException nfe) {
System.err.println("CharacterId should be integer");
badArguments("replacecharacterid");
}
}
for (int i = 0; i < characterIds.size(); i += 2) {
int oldCharacterId = characterIds.get(i);
int newCharacterId = characterIds.get(i + 1);
swf.replaceCharacter(oldCharacterId, newCharacterId);
}
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseConvert(Stack args, String charset) {
if (args.size() < 4) {
badArguments("convert");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
String objectToConvert = args.pop();
int characterId = 0;
try {
characterId = Integer.parseInt(objectToConvert);
} catch (NumberFormatException nfe) {
System.err.println("CharacterId should be integer");
badArguments("convert");
}
if (!swf.getCharacters(false).containsKey(characterId)) {
System.err.println("CharacterId does not exist");
System.exit(1);
}
CharacterTag characterTag = swf.getCharacter(characterId);
String targetType = args.pop().toLowerCase(Locale.ENGLISH);
if (characterTag instanceof ImageTag) {
int format = ImageImporter.getImageTagType(targetType);
ImageTag imageTag = (ImageTag) characterTag;
new ImageImporter().convertImage(imageTag, format);
} else if (characterTag instanceof ShapeTag) {
int format = ShapeImporter.getShapeTagType(targetType);
ShapeTag shapeTag = (ShapeTag) characterTag;
System.err.println("Converting shape tag is currently not supported");
} else if (characterTag instanceof MorphShapeTag) {
int format = MorphShapeImporter.getMorphShapeTagType(targetType);
MorphShapeTag morphShapeTag = (MorphShapeTag) characterTag;
System.err.println("Converting morph shape tag is currently not supported");
} else if (characterTag instanceof FontTag) {
int format = FontImporter.getFontTagType(targetType);
FontTag fontTag = (FontTag) characterTag;
System.err.println("Converting font tag is currently not supported");
} else if (characterTag instanceof TextTag) {
int format = TextImporter.getTextTagType(targetType);
TextTag textTag = (TextTag) characterTag;
System.err.println("Converting text tag is currently not supported");
} else {
System.err.println("The specified tag type is not supported for import");
System.exit(1);
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseRemove(Stack args, String charset) {
if (args.size() < 3) {
badArguments("remove");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
List tagNumbersToRemove = new ArrayList<>();
while (true) {
String tagNoToRemoveStr = args.pop();
int tagNo;
try {
tagNo = Integer.parseInt(tagNoToRemoveStr);
} catch (NumberFormatException nfe) {
System.err.println("Tag number should be integer");
System.exit(1);
return;
}
if (tagNo < 0 || tagNo >= swf.getTags().size()) {
System.err.println("Tag number does not exist. Tag number should be between 0 and " + (swf.getTags().size() - 1));
System.exit(1);
}
if (!tagNumbersToRemove.contains(tagNo)) {
tagNumbersToRemove.add(tagNo);
}
if (args.isEmpty() || args.peek().startsWith("-")) {
break;
}
}
Collections.sort(tagNumbersToRemove);
for (int i = tagNumbersToRemove.size() - 1; i >= 0; i--) {
swf.removeTag((int) tagNumbersToRemove.get(i));
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseDoc(Stack args) {
String type = null;
String format = null;
String out = null;
String locale = null;
boolean nightMode = false;
while (!args.isEmpty()) {
String arg = args.pop();
switch (arg) {
case "-night":
nightMode = true;
break;
case "-out":
if (args.isEmpty() || out != null) {
badArguments("doc");
}
out = args.pop();
break;
case "-type":
if (args.isEmpty() || type != null) {
badArguments("doc");
}
type = args.pop();
break;
case "-format":
if (args.isEmpty() || format != null) {
badArguments("doc");
}
format = args.pop();
break;
case "-locale":
if (args.isEmpty() || locale != null) {
badArguments("doc");
}
locale = args.pop();
break;
}
}
if (format == null) {
format = "html";
}
if (!format.equals("html")) {
badArguments("doc");
}
if (locale != null) {
Locale.setDefault(Locale.forLanguageTag(locale));
}
String doc = "";
if (type == null) {
badArguments("doc");
} else if (type.equals("as3.pcode.instructions")) {
doc = As3PCodeDocs.getAllInstructionDocs(nightMode);
} else if (type.equals("as12.pcode.instructions")) {
doc = As12PCodeDocs.getAllInstructionDocs(nightMode);
} else {
badArguments("doc");
}
PrintStream outStream;
if (out == null) {
outStream = System.out;
} else {
try {
outStream = new PrintStream(out, "UTF-8");
} catch (UnsupportedEncodingException | FileNotFoundException ex) {
Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage());
System.exit(1);
return;
}
}
outStream.print(doc);
}
private static void parseRemoveCharacter(Stack args, boolean removeDependencies, String charset) {
if (args.size() < 3) {
badArguments("removecharacter");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
while (true) {
String objectToRemove = args.pop();
int characterId = 0;
try {
characterId = Integer.parseInt(objectToRemove);
} catch (NumberFormatException nfe) {
System.err.println("CharacterId should be integer");
badArguments("removecharacter");
}
if (!swf.getCharacters(false).containsKey(characterId)) {
System.err.println("CharacterId does not exist");
System.exit(1);
}
CharacterTag characterTag = swf.getCharacter(characterId);
swf.removeTag(characterTag, removeDependencies, null);
if (args.isEmpty() || args.peek().startsWith("-")) {
break;
}
}
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseImportSymbolClass(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importsymbolclass");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
String selFile = args.pop();
File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME));
SymbolClassImporter importer = new SymbolClassImporter();
if (!importFile.exists()) {
System.err.println("Symbol class file " + importFile + " does not exist.");
System.exit(1);
}
importer.importSymbolClasses(importFile, swf);
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportShapes(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importshapes");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
boolean noFill = false;
if (args.size() > 1) {
if (args.pop().equals("nofill")) {
noFill = true;
} else {
badArguments("importshapes");
}
}
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
System.out.println("Source file opened");
String selFile = args.pop();
File shapesDir = new File(Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME));
if (shapesDir.exists()) {
System.out.println("Using the directory: " + shapesDir.getAbsolutePath());
} else {
shapesDir = new File(selFile);
}
if (!shapesDir.exists()) {
System.err.println("Shapes directory does not exist: " + shapesDir.getAbsolutePath());
System.exit(1);
}
ShapeImporter shapeImporter = new ShapeImporter();
int shapeCount = shapeImporter.bulkImport(shapesDir, swf, noFill, true);
System.out.println("Writing outfile");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
System.out.println("" + shapeCount + " shapes successfully imported");
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportMovies(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importmovies");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
System.out.println("Source file opened");
String selFile = args.pop();
File moviesDir = new File(Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME));
if (moviesDir.exists()) {
System.out.println("Using the directory: " + moviesDir.getAbsolutePath());
} else {
moviesDir = new File(selFile);
}
if (!moviesDir.exists()) {
System.err.println("Movies directory does not exist: " + moviesDir.getAbsolutePath());
System.exit(1);
}
MovieImporter movieImporter = new MovieImporter();
int movieCount = movieImporter.bulkImport(moviesDir, swf, true);
System.out.println("Writing outfile");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
System.out.println("" + movieCount + " movies successfully imported");
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportSounds(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importsounds");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
System.out.println("Source file opened");
String selFile = args.pop();
File soundsDir = new File(Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME));
if (soundsDir.exists()) {
System.out.println("Using the directory: " + soundsDir.getAbsolutePath());
} else {
soundsDir = new File(selFile);
}
if (!soundsDir.exists()) {
System.err.println("Sounds directory does not exist: " + soundsDir.getAbsolutePath());
System.exit(1);
}
SoundImporter soundImporter = new SoundImporter();
int soundCount = soundImporter.bulkImport(soundsDir, swf, true);
System.out.println("Writing outfile");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
System.out.println("" + soundCount + " sounds successfully imported");
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportImages(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importimages");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
System.out.println("Source file opened");
String selFile = args.pop();
File imagesDir = new File(Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME));
if (imagesDir.exists()) {
System.out.println("Using the directory: " + imagesDir.getAbsolutePath());
} else {
imagesDir = new File(selFile);
}
if (!imagesDir.exists()) {
System.err.println("Images directory does not exist: " + imagesDir.getAbsolutePath());
System.exit(1);
}
ImageImporter imageImporter = new ImageImporter();
int imageCount = imageImporter.bulkImport(imagesDir, swf, true);
System.out.println("Writing outfile");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
System.out.println("" + imageCount + " images successfully imported");
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportSprites(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importsprites");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
System.out.println("Source file opened");
String selFile = args.pop();
File spritesDir = new File(Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME));
if (spritesDir.exists()) {
System.out.println("Using the directory: " + spritesDir.getAbsolutePath());
} else {
spritesDir = new File(selFile);
}
if (!spritesDir.exists()) {
System.err.println("Sprites directory does not exist: " + spritesDir.getAbsolutePath());
System.exit(1);
}
SpriteImporter spriteImporter = new SpriteImporter();
int spriteCount = spriteImporter.bulkImport(spritesDir, swf, true);
System.out.println("Writing outfile");
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
System.out.println("" + spriteCount + " sprites successfully imported");
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportText(Stack args, String charset) {
if (args.size() < 3) {
badArguments("importtext");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
String selFile = args.pop();
boolean textsFolderExists = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).exists();
File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED));
if (textsFolderExists) {
System.out.println("Using the directory: " + new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME)).getAbsolutePath());
} else {
textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED));
}
TextImporter textImporter = new TextImporter(new MissingCharacterHandler() {
@Override
public boolean getIgnoreMissingCharacters() {
return true;
}
@Override
public boolean handle(TextTag textTag, final FontTag font, final char character) {
String fontName = font.getSwf().sourceFontNamesMap.get(font.getCharacterId());
if (fontName == null) {
fontName = font.getFontName();
}
final Font f = FontTag.getInstalledFontsByName().get(fontName);
if (f == null || !f.canDisplay(character)) {
String msg = AppStrings.translate("error.font.nocharacter").replace("%char%", "" + character);
logger.log(Level.SEVERE, "{0} FontId: {1} TextId: {2}", new Object[]{msg, font.getCharacterId(), textTag.getCharacterId()});
return false;
}
return font.addCharacter(character, f);
}
}, new TextImportErrorHandler() {
private String getTextTagInfo(TextTag textTag) {
StringBuilder ret = new StringBuilder();
if (textTag != null) {
ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")");
}
return ret.toString();
}
@Override
public boolean handle(TextTag textTag) {
String msg = AppStrings.translate("error.text.import");
logger.log(Level.SEVERE, "{0}{1}", new Object[]{msg, getTextTagInfo(textTag)});
return true;
}
@Override
public boolean handle(TextTag textTag, String message, long line) {
String msg = AppStrings.translate("error.text.invalid.continue").replace("%text%", message).replace("%line%", Long.toString(line));
logger.log(Level.SEVERE, "{0}{1}", new Object[]{msg, getTextTagInfo(textTag)});
return true;
}
});
// try to import formatted texts
if (textsFile.exists()) {
textImporter.importTextsSingleFileFormatted(textsFile, swf);
} else {
textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN));
if (!textsFolderExists) {
textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FILENAME_PLAIN));
}
// try to import plain texts
if (textsFile.exists()) {
textImporter.importTextsSingleFile(textsFile, swf);
} else {
textImporter.importTextsMultipleFiles(selFile, swf);
}
}
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
private static void parseImportScript(Stack args, String charset, boolean air, AbortRetryIgnoreHandler errorHandler) {
String flexLocation = Configuration.flexSdkLocation.get();
if (Configuration.useFlexAs3Compiler.get() && (flexLocation.isEmpty() || (!new File(flexLocation).exists()))) {
System.err.println("Flex AS3 compiler enabled but Flex SDK path not set");
System.exit(1);
}
if (args.size() < 3) {
badArguments("importscript");
}
File inFile = new File(args.pop());
File outFile = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
String baseFolder = args.pop();
String scriptsFolder = Path.combine(baseFolder, ScriptExportSettings.EXPORT_FOLDER_NAME);
if (new File(scriptsFolder).exists()) {
System.out.println("Using the directory: " + new File(scriptsFolder).getAbsolutePath());
} else {
scriptsFolder = baseFolder;
}
ScriptImporterProgressListener listener = new ScriptImporterProgressListener() {
@Override
public void scriptImported() {
}
@Override
public void scriptImportError() {
if (errorHandler == null || ((ConsoleAbortRetryIgnoreHandler) errorHandler).errorMode == AbortRetryIgnoreHandler.ABORT) {
System.exit(1);
}
}
};
new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true), listener);
new AS3ScriptImporter().importScripts(As3ScriptReplacerFactory.createByConfig(air), scriptsFolder, swf.getAS3Packs(), listener, Main.getDependencies(swf));
try {
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(outFile))) {
swf.saveTo(fos);
}
} catch (IOException e) {
System.err.println("I/O error during writing");
System.exit(2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseCustom(Stack args) {
String[] customParameters = new String[args.size()];
for (int i = 0; i < customParameters.length; i++) {
customParameters[i] = args.pop();
}
SWFDecompilerPlugin.customParameters = customParameters;
}
private static void loadFiles(String[] fileNames) {
boolean result = true;
for (String fileName : fileNames) {
try {
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, fileName, null);
Main.parseOpenable(sourceInfo);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
result = false;
}
}
System.exit(result ? 0 : 1);
}
private static void replaceAS2PCode(String text, ASMSource src) throws IOException, InterruptedException {
System.out.println("Replace AS1/2 PCode");
if (text.trim().startsWith(Helper.hexData)) {
src.setActionBytes(Helper.getBytesFromHexaText(text));
} else {
try {
src.setActions(ASMParser.parse(0, true, text, src.getSwf().version, false, src.getSwf().getCharset()));
} catch (ActionParseException ex) {
System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)));
System.exit(1);
}
}
src.setModified();
}
private static void replaceAS2(String as, ASMSource src) throws IOException, InterruptedException {
System.out.println("Replace AS1/2");
System.out.println("Warning: This feature is EXPERIMENTAL");
ActionScript2Parser par = new ActionScript2Parser(src.getSwf(), src);
try {
src.setActions(par.actionsFromString(as, src.getSwf().getCharset()));
} catch (ValueTooLargeException ex) {
System.err.println("Script or some of its functions are too large");
System.exit(1);
} catch (ActionParseException ex) {
System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)));
System.exit(1);
} catch (CompilationException ex) {
System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)));
System.exit(1);
}
src.setModified();
}
private static void replaceAS3PCode(String text, ABC abc, int bodyIndex, Trait trait) throws IOException, InterruptedException {
System.out.println("Replace AS3 PCode");
if (text.trim().startsWith(Helper.hexData)) {
byte[] data = Helper.getBytesFromHexaText(text);
MethodBody mb = abc.bodies.get(bodyIndex);
mb.setCodeBytes(data);
} else {
try {
AVM2Code acode = ASM3Parser.parse(abc, new StringReader(text), trait, new MissingSymbolHandler() {
//no longer ask for adding new constants
@Override
public boolean missingString(String value) {
return true;
}
@Override
public boolean missingInt(long value) {
return true;
}
@Override
public boolean missingUInt(long value) {
return true;
}
@Override
public boolean missingDouble(double value) {
return true;
}
@Override
public boolean missingDecimal(Decimal128 value) {
return true;
}
@Override
public boolean missingFloat(float value) {
return true;
}
@Override
public boolean missingFloat4(Float4 value) {
return true;
}
}, abc.bodies.get(bodyIndex), abc.method_info.get(abc.bodies.get(bodyIndex).method_info));
//acode.getBytes(abc.bodies.get(bodyIndex).getCodeBytes());
abc.bodies.get(bodyIndex).setCode(acode);
} catch (AVM2ParseException ex) {
System.err.println("%error% on line %line%".replace("%error%", ex.text).replace("%line%", Long.toString(ex.line)));
System.exit(1);
}
}
((Tag) abc.parentTag).setModified(true);
}
private static void replaceAS3(String asp, String as, ScriptPack pack, boolean air) throws IOException, InterruptedException {
System.out.println("Replacing: " + asp);
System.out.println("Warning: This feature is EXPERIMENTAL");
As3ScriptReplacerInterface scriptReplacer = As3ScriptReplacerFactory.createByConfig(air);
if (!scriptReplacer.isAvailable()) {
System.err.println("Current script replacer is not available.");
if (scriptReplacer instanceof FFDecAs3ScriptReplacer) {
System.err.println("Current replacer: FFDec");
final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html";
System.err.println("For ActionScript 3 direct editation, a library called \"PlayerGlobal.swc\" needs to be downloaded from Adobe homepage:");
System.err.println(adobePage);
System.err.println("Download the library called PlayerGlobal(.swc), and place it to directory");
System.err.println(Configuration.getFlashLibPath().getAbsolutePath());
} else if (scriptReplacer instanceof MxmlcAs3ScriptReplacer) {
System.err.println("Current replacer: Flex SDK");
final String flexPage = "http://www.adobe.com/devnet/flex/flex-sdk-download.html";
System.err.println("For ActionScript 3 direct editation, Flex SDK needs to be download");
System.err.println(flexPage);
System.err.println("Download FLEX Sdk, unzip it to some directory and set its directory path in the configuration");
}
System.exit(1);
}
try {
pack.abc.replaceScriptPack(scriptReplacer, pack, as, Main.getDependencies(pack.abc.getSwf()));
} catch (As3ScriptReplaceException asre) {
for (As3ScriptReplaceExceptionItem item : asre.getExceptionItems()) {
String r = "%error% on line %line%, column %col%, file: %file%".replace("%error%", "" + item.getMessage());
r = r.replace("%line%", Long.toString(item.getLine()));
r = r.replace("%file%", "" + item.getFile());
r = r.replace("%col%", "" + item.getCol());
logger.log(Level.SEVERE, r);
}
System.exit(1);
}
}
private static void parseInfo(Stack args, String charset) throws FileNotFoundException {
File out;
PrintWriter pw = new PrintWriter(System.out);
boolean found = false;
boolean detectBundle = true;
while (!args.isEmpty()) {
String a = args.pop();
switch (a) {
case "-out":
if (args.isEmpty()) {
badArguments("info");
}
out = new File(args.pop());
if (out.isDirectory()) {
logger.log(Level.SEVERE, "File is a directory");
System.exit(1);
} else {
pw = new PrintWriter(out);
}
break;
case "-nobundle":
detectBundle = false;
break;
default:
Bundle bundle;
String sfile = a;
File file = new File(sfile);
try {
OpenableSourceInfo sourceInfo = new OpenableSourceInfo(null, sfile, sfile, detectBundle);
bundle = sourceInfo.getBundle(false, SearchMode.ALL);
logger.log(Level.INFO, "Load file: {0}", sourceInfo.getFile());
if (bundle != null) {
int subcnt = 0;
for (Entry streamEntry : bundle.getAll().entrySet()) {
InputStream stream = streamEntry.getValue();
stream.reset();
CancellableWorker worker = new CancellableWorker("parseInfoBundle") {
@Override
public SWF doInBackground() throws Exception {
final CancellableWorker worker = this;
SWF swf = new SWF(stream, null, streamEntry.getKey(), new ProgressListener() {
@Override
public void progress(int p) {
//...
}
@Override
public void status(String status) {
}
}, Configuration.parallelSpeedUp.get(), charset);
return swf;
}
};
//loadingDialog.setWorker(worker);
worker.execute();
subcnt++;
try {
processInfoSWF(file, worker.get(), pw);
} catch (CancellationException | ExecutionException | InterruptedException ex) {
logger.log(Level.WARNING, "Loading SWF {0} was cancelled.", streamEntry.getKey());
}
}
if (subcnt > 0) {
found = true;
} else {
System.err.println("No SWFs found in \"" + file + "\"");
}
} else {
StdInAwareFileInputStream fis = new StdInAwareFileInputStream(file);
BufferedInputStream inputStream = new BufferedInputStream(fis);
InputStream fInputStream = inputStream;
CancellableWorker worker = new CancellableWorker("parseInfo") {
@Override
public SWF doInBackground() throws Exception {
final CancellableWorker worker = this;
SWF swf = new SWF(fInputStream, sourceInfo.getFile(), sourceInfo.getFileTitle(), new ProgressListener() {
@Override
public void progress(int p) {
//startWork(AppStrings.translate("work.reading.swf"), p, worker);
}
@Override
public void status(String status) {
}
}, Configuration.parallelSpeedUp.get(), charset);
return swf;
}
};
worker.execute();
try {
processInfoSWF(null, worker.get(), pw);
} catch (CancellationException | ExecutionException | InterruptedException ex) {
logger.log(Level.WARNING, "Loading SWF was cancelled.");
}
found = true;
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Cannot read.");
System.exit(1);
}
}
}
if (!found) {
System.exit(1);
}
}
private static String doubleToString(double d) {
String ds = "" + d;
if (ds.endsWith(".0")) {
ds = ds.substring(0, ds.length() - 2);
}
return ds;
}
private static void processInfoSWF(File bundle, SWF swf, PrintWriter pw) throws IOException {
pw.println("[swf]");
pw.println("file=" + (bundle == null ? swf.getFile() : bundle + ":" + swf.getFileTitle()));
pw.println("fileSize=" + swf.fileSize);
pw.println("version=" + swf.version);
pw.println("compression=" + swf.compression);
pw.println("encrypted=" + swf.encrypted);
pw.println("gfx=" + swf.gfx);
pw.println("width=" + doubleToString(swf.displayRect.getWidth() / SWF.unitDivisor));
pw.println("height=" + doubleToString(swf.displayRect.getHeight() / SWF.unitDivisor));
pw.println("frameCount=" + swf.frameCount);
pw.println("frameRate=" + doubleToString(swf.frameRate));
for (Tag t : swf.getTags()) {
if (t instanceof SetBackgroundColorTag) {
pw.println("backgroundColor=" + ((SetBackgroundColorTag) t).backgroundColor.toHexRGB());
}
if (t instanceof ScriptLimitsTag) {
pw.println("maxRecursionDepth=" + ((ScriptLimitsTag) t).maxRecursionDepth);
pw.println("scriptTimeoutSeconds=" + ((ScriptLimitsTag) t).scriptTimeoutSeconds);
}
}
pw.println();
pw.println("[tags]");
pw.println("tagCount=" + swf.getTags().size());
pw.println("hasEndTag=" + swf.hasEndTag);
pw.println("characterCount=" + (swf.getCharacters(true).size()));
pw.println("maxCharacterId=" + (swf.getNextCharacterId() - 1));
pw.println();
FileAttributesTag fa = swf.getFileAttributes();
if (fa != null) {
pw.println("[attributes]");
pw.println("actionScript3=" + fa.actionScript3);
pw.println("hasMetadata=" + fa.hasMetadata);
pw.println("noCrossDomainCache=" + fa.noCrossDomainCache);
pw.println("swfRelativeUrls=" + fa.swfRelativeUrls);
pw.println("useDirectBlit=" + fa.useDirectBlit);
pw.println("useGPU=" + fa.useGPU);
pw.println("useNetwork=" + fa.useNetwork);
pw.println();
}
pw.println("[as2]");
pw.println("scriptsCount=" + swf.getASMs(true).size());
pw.println();
pw.println("[as3]");
pw.println("ABCtagCount=" + swf.getAbcList().size());
pw.println("packsCount=" + swf.getAS3Packs().size());
String dcs = swf.getDocumentClass();
if (dcs != null) {
if (dcs.contains(".")) {
DottedChain dc = DottedChain.parseWithSuffix(dcs);
pw.println("documentClass=" + dc.toPrintableString(new LinkedHashSet<>(), swf, true));
} else {
pw.println("documentClass=" + IdentifiersDeobfuscation.printIdentifier(swf, new LinkedHashSet<>(), true, dcs));
}
} else {
pw.println("documentClass=");
}
pw.println();
pw.flush();
}
private static void parseDumpSwf(Stack args) {
if (args.isEmpty()) {
badArguments("dumpswf");
}
Configuration.dumpTags.set(true);
Configuration.parallelSpeedUp.set(false);
try (InputStream is = new FileInputStream(args.pop())) {
SWF swf = new SWF(is, false, false);
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
System.exit(1);
}
}
private static void parseDumpAS2(Stack args, String charset) {
if (args.isEmpty()) {
badArguments("dumpas2");
}
boolean useExportNames = false;
String exportNamesParam = args.pop();
if (exportNamesParam.toLowerCase(Locale.ENGLISH).equals("-exportnames")) {
useExportNames = true;
} else {
args.push(exportNamesParam);
}
if (args.isEmpty()) {
badArguments("dumpas2");
}
File file = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
Map asms = swf.getASMs(useExportNames);
for (String as2 : asms.keySet()) {
System.out.println(as2);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseEnableDebugging(Stack args, String charset) {
if (args.size() < 2) {
badArguments("enabledebugging");
}
boolean injectas3 = false;
boolean doPCode = false;
boolean generateSwd = false;
String file = args.pop();
if (file.equals("-generateswd")) {
if (args.size() < 2) {
badArguments("enabledebugging");
}
file = args.pop();
generateSwd = true;
}
if (file.equals("-injectas3")) {
if (args.size() < 2) {
badArguments("enabledebugging");
}
file = args.pop();
injectas3 = true;
}
if (file.equals("-pcode")) {
if (args.size() < 2) {
badArguments("enabledebugging");
}
doPCode = true;
file = args.pop();
}
String outfile = args.pop();
try {
System.out.print("Working...");
StdInAwareFileInputStream fis = new StdInAwareFileInputStream(file);
SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get(), charset);
fis.close();
if (swf.isAS3()) {
swf.enableDebugging(injectas3, new File(outfile).getParentFile(), doPCode);
} else {
swf.enableDebugging();
}
FileOutputStream fos = new FileOutputStream(outfile);
swf.saveTo(fos);
fos.close();
if (!swf.isAS3()) {
if (generateSwd) {
fis = new StdInAwareFileInputStream(outfile);
swf = new SWF(fis, Configuration.parallelSpeedUp.get(), charset);
fis.close();
String outSwd = outfile;
if (outSwd.toLowerCase(Locale.ENGLISH).endsWith(".swf")) {
outSwd = outSwd.substring(0, outSwd.length() - 4) + ".swd";
} else {
outSwd = outSwd + ".swd";
}
if (doPCode) {
if (!swf.generatePCodeSwdFile(new File(outSwd), new HashMap<>())) {
System.err.println("Generating SWD failed");
}
} else if (!swf.generateSwdFile(new File(outSwd), new HashMap<>())) {
System.err.println("Generating SWD failed");
}
}
} else if (generateSwd) {
System.err.println("WARNING: Cannot generate SWD for AS3 file");
}
System.out.println("OK");
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Cannot read {0}", file);
System.exit(1);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Reading error {0}", file);
System.exit(2);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, "Cancelled {0}", file);
System.exit(3);
}
System.out.println("Finished");
}
private static void parseDumpAS3(Stack args, String charset) {
if (args.isEmpty()) {
badArguments("dumpas3");
}
File file = new File(args.pop());
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
List packs = swf.getAS3Packs();
for (ScriptPack entry : packs) {
System.out.println(entry.getClassPath().toString() + " " + entry.scriptIndex);
}
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
}
private static void parseHeader(Stack args) {
if (args.isEmpty()) {
badArguments("header");
}
Float frameRate = null;
Integer frameCount = null;
Integer width = null;
Integer height = null;
Integer x = null;
Integer y = null;
Boolean gfx = null;
Integer version = null;
Pattern displayRectPattern = Pattern.compile("\\[(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?,(?-?[0-9.]+)(?px)?\\]");
boolean printOnly = args.size() == 1;
while (!args.isEmpty()) {
String arg = args.pop();
if (arg.equals("-set")) {
if (args.size() < 3) {
badArguments("header");
}
String key = args.pop();
String value = args.pop();
switch (key.toLowerCase()) {
case "version":
try {
version = Integer.valueOf(value);
} catch (NumberFormatException nfe) {
badArguments("header");
}
break;
case "gfx":
gfx = parseBooleanConfigValue(value);
if (gfx == null) {
badArguments("header");
}
break;
case "displayrect":
Matcher displayRectMatcher = displayRectPattern.matcher(value);
if (!displayRectMatcher.matches()) {
badArguments("header");
}
if (displayRectMatcher.group("x1px") != null) {
x = (int) Math.round(Float.parseFloat(displayRectMatcher.group("x1")) * SWF.unitDivisor);
} else {
try {
x = Integer.valueOf(displayRectMatcher.group("x1"));
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
if (displayRectMatcher.group("y1px") != null) {
y = (int) Math.round(Float.parseFloat(displayRectMatcher.group("y1")) * SWF.unitDivisor);
} else {
try {
y = Integer.valueOf(displayRectMatcher.group("y1"));
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
int x2 = 0;
int y2 = 0;
if (displayRectMatcher.group("x2px") != null) {
x2 = (int) Math.round(Float.parseFloat(displayRectMatcher.group("x2")) * SWF.unitDivisor);
} else {
try {
x2 = Integer.parseInt(displayRectMatcher.group("x2"));
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
if (displayRectMatcher.group("y2px") != null) {
y2 = (int) Math.round(Float.parseFloat(displayRectMatcher.group("y2")) * SWF.unitDivisor);
} else {
try {
y2 = Integer.parseInt(displayRectMatcher.group("y2"));
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
width = x2 - x;
height = y2 - y;
break;
case "width":
if (value.endsWith("px")) {
value = value.substring(0, value.length() - 2).trim();
try {
width = (int) Math.round(Float.parseFloat(value) * SWF.unitDivisor);
} catch (NumberFormatException nfe) {
badArguments("header");
}
} else {
try {
width = Integer.valueOf(value);
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
break;
case "height":
if (value.endsWith("px")) {
value = value.substring(0, value.length() - 2).trim();
try {
height = (int) Math.round(Float.parseFloat(value) * SWF.unitDivisor);
} catch (NumberFormatException nfe) {
badArguments("header");
}
} else {
try {
height = Integer.valueOf(value);
} catch (NumberFormatException nfe) {
badArguments("header");
}
}
break;
case "framecount":
try {
frameCount = Integer.valueOf(value);
} catch (NumberFormatException nfe) {
badArguments("header");
}
break;
case "framerate":
try {
frameRate = Float.valueOf(value);
} catch (NumberFormatException nfe) {
badArguments("header");
}
break;
default:
badArguments("header");
}
} else {
args.push(arg);
break;
}
}
if (!printOnly && args.size() != 2) {
badArguments("header");
}
File file = new File(args.pop());
File outfile = printOnly ? null : new File(args.pop());
PrintWriter pw = new PrintWriter(System.out);
try {
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(file)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), true);
if (printOnly) {
pw.println("[header]");
pw.println("fileSize=" + swf.fileSize);
pw.println("version=" + swf.version);
pw.println("compression=" + swf.compression);
pw.println("encrypted=" + swf.encrypted);
pw.println("gfx=" + swf.gfx);
pw.println("displayRect=[" + swf.displayRect.Xmin + ", " + swf.displayRect.Ymin + ", " + swf.displayRect.Xmax + ", " + swf.displayRect.Ymax + "]");
pw.println("width=" + swf.displayRect.getWidth());
pw.println("widthPx=" + doubleToString(swf.displayRect.getWidth() / SWF.unitDivisor));
pw.println("height=" + swf.displayRect.getHeight());
pw.println("heightPx=" + doubleToString(swf.displayRect.getHeight() / SWF.unitDivisor));
pw.println("frameCount=" + swf.frameCount);
pw.println("frameRate=" + doubleToString(swf.frameRate));
pw.flush();
return;
}
if (version != null) {
swf.version = version;
pw.println("version set to " + version);
}
if (gfx != null) {
swf.gfx = gfx;
pw.println("gfx set to " + gfx);
}
if (x != null) {
swf.displayRect.Xmin = x;
pw.println("displayrect.x1 set to " + x);
}
if (y != null) {
swf.displayRect.Ymin = y;
pw.println("displayrect.y1 set to " + y);
}
if (width != null) {
swf.displayRect.Xmax = swf.displayRect.Xmin + width;
pw.println("displayrect.x2 set to " + swf.displayRect.Xmax);
}
if (height != null) {
swf.displayRect.Ymax = swf.displayRect.Ymin + height;
pw.println("displayrect.y2 set to " + swf.displayRect.Ymax);
}
if (frameCount != null) {
swf.frameCount = frameCount;
pw.println("framecount set to " + frameCount);
}
if (frameRate != null) {
swf.frameRate = frameRate;
pw.println("framerate set to " + frameRate);
}
try (FileOutputStream fos = new FileOutputStream(outfile)) {
swf.saveTo(fos);
}
pw.println("Successfully saved");
}
} catch (IOException | InterruptedException e) {
System.err.println("I/O error during reading");
System.exit(2);
}
pw.flush();
}
private static FilenameFilter getSwfFilter() {
return (File dir, String name) -> name.toLowerCase(Locale.ENGLISH).endsWith(".swf");
}
private static E enumFromStr(String str, Class cls) {
E[] vals = cls.getEnumConstants();
if (str == null) {
return vals[0];
}
for (E e : vals) {
if (e.toString().toLowerCase(Locale.ENGLISH).replace("_", "").equals(str.toLowerCase(Locale.ENGLISH).replace("_", ""))) {
return e;
}
}
return vals[0];
}
private static interface SwfAction {
public void swfAction(SWF swf, OutputStream stdout) throws IOException;
}
private static interface AbcAction {
public void abcAction(ABC abc, OutputStream stdout) throws IOException;
}
private static void processReadSWF(File inFile, File stdOutFile, SwfAction action, String charset) {
OutputStream stdout = null;
try {
if (stdOutFile != null) {
try {
stdout = new FileOutputStream(stdOutFile);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
}
} else {
stdout = System.out;
}
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
action.swfAction(swf, stdout);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
System.exit(1);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error", ex);
System.exit(1);
}
} finally {
if (stdOutFile != null) {
if (stdout != null) {
try {
stdout.close();
} catch (IOException ex) {
//ignore
}
}
}
}
}
private static void processModifySWF(File inFile, File outFile, File stdOutFile, SwfAction action, String charset) {
OutputStream stdout = null;
try {
if (stdOutFile != null) {
try {
stdout = new FileOutputStream(stdOutFile);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
}
} else {
stdout = System.out;
}
File tmpFile = null;
if (inFile.equals(outFile)) {
try {
tmpFile = File.createTempFile("ffdec_modify_", ".swf");
outFile = tmpFile;
} catch (IOException ex) {
System.err.println("Unable to create temp file");
System.exit(1);
}
}
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile); FileOutputStream fos = new FileOutputStream(outFile)) {
SWF swf = new SWF(is, Configuration.parallelSpeedUp.get(), charset);
action.swfAction(swf, stdout);
swf.saveTo(fos);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, null, ex);
System.exit(1);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error", ex);
System.exit(1);
}
if (tmpFile != null) {
try {
if (!inFile.delete()) {
System.err.println("Cannot overwrite original file");
System.exit(1);
}
if (!tmpFile.renameTo(inFile)) {
System.err.println("Cannot rename tempfile to original file");
System.exit(1);
}
tmpFile = null;
System.out.println(inFile + " overwritten.");
} finally {
if (tmpFile != null && tmpFile.exists()) {
tmpFile.delete();
}
}
}
} finally {
if (stdOutFile != null) {
if (stdout != null) {
try {
stdout.close();
} catch (IOException ex) {
//ignore
}
}
}
}
}
private static void processModifyAbc(File inFile, File outFile, File stdOutFile, AbcAction action, String charset) {
//It does not have .abc extension, assuming its SWF - process all its ABC tags
if (!inFile.getAbsolutePath().toLowerCase().endsWith(".abc")) {
SwfAction swfAction = new SwfAction() {
@Override
public void swfAction(SWF swf, OutputStream stdout) throws IOException {
List abcList = swf.getAbcList();
for (ABCContainerTag cnt : abcList) {
action.abcAction(cnt.getABC(), stdout);
}
}
};
processModifySWF(inFile, outFile, stdOutFile, swfAction, charset);
return;
}
OutputStream stdout = null;
try {
if (stdOutFile != null) {
try {
stdout = new FileOutputStream(stdOutFile);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
}
} else {
stdout = System.out;
}
File tmpFile = null;
if (inFile.equals(outFile)) {
try {
tmpFile = File.createTempFile("ffdec_modify_", ".abc");
outFile = tmpFile;
} catch (IOException ex) {
System.err.println("Unable to create temp file");
System.exit(1);
}
}
try (StdInAwareFileInputStream is = new StdInAwareFileInputStream(inFile); FileOutputStream fos = new FileOutputStream(outFile)) {
SWF dummySwf = new SWF();
dummySwf.setFileTitle(inFile.getName());
FileAttributesTag fileAttributes = new FileAttributesTag(dummySwf);
fileAttributes.actionScript3 = true;
dummySwf.addTag(fileAttributes);
fileAttributes.setTimelined(dummySwf);
DoABC2Tag doABC2Tag = new DoABC2Tag(dummySwf);
dummySwf.addTag(doABC2Tag);
doABC2Tag.setTimelined(dummySwf);
dummySwf.clearModified();
ABC abc = new ABC(new ABCInputStream(new MemoryInputStream(Helper.readStream(is))), dummySwf, doABC2Tag, inFile.getName(), null);
doABC2Tag.setABC(abc);
action.abcAction(abc, stdout);
abc.saveTo(fos);
} catch (FileNotFoundException ex) {
System.err.println("File not found: " + ex.getMessage());
System.exit(1);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error", ex);
System.exit(1);
}
if (tmpFile != null) {
try {
if (!inFile.delete()) {
System.err.println("Cannot overwrite original file");
System.exit(1);
}
if (!tmpFile.renameTo(inFile)) {
System.err.println("Cannot rename tempfile to original file");
System.exit(1);
}
tmpFile = null;
System.out.println(inFile + " overwritten.");
} finally {
if (tmpFile != null && tmpFile.exists()) {
tmpFile.delete();
}
}
}
} finally {
if (stdOutFile != null) {
if (stdout != null) {
try {
stdout.close();
} catch (IOException ex) {
//ignore
}
}
}
}
}
}