/*
* Copyright (C) 2010-2023 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.EventListener;
import com.jpexs.decompiler.flash.IdentifiersDeobfuscation;
import com.jpexs.decompiler.flash.ReadOnlyTagList;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFCompression;
import com.jpexs.decompiler.flash.OpenableSourceInfo;
import com.jpexs.decompiler.flash.SearchMode;
import com.jpexs.decompiler.flash.SwfOpenException;
import com.jpexs.decompiler.flash.ValueTooLargeException;
import com.jpexs.decompiler.flash.abc.ABC;
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.Decimal;
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.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.docs.As3PCodeDocs;
import com.jpexs.decompiler.flash.exporters.BinaryDataExporter;
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.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.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.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.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.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.ShapeImporter;
import com.jpexs.decompiler.flash.importers.SwfXmlImporter;
import com.jpexs.decompiler.flash.importers.TextImporter;
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer;
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException;
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.DefineSpriteTag;
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.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.ShapeTag;
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.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.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.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 com.sun.jna.platform.win32.Kernel32;
import gnu.jpdf.PDFJob;
import java.awt.Graphics;
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.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 com.jpexs.decompiler.flash.Bundle;
import com.jpexs.decompiler.flash.exporters.DualPdfGraphics2D;
import com.jpexs.decompiler.flash.exporters.Font4Exporter;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.modes.Font4ExportMode;
import com.jpexs.decompiler.flash.exporters.settings.Font4ExportSettings;
import com.jpexs.decompiler.flash.gui.translator.Translator;
import com.jpexs.decompiler.flash.importers.MovieImporter;
import com.jpexs.decompiler.flash.importers.SoundImporter;
import com.jpexs.decompiler.flash.importers.SymbolClassImporter;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.base.HasSeparateAlphaChannel;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.SoundImportException;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.UnsupportedSamplingRateException;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.helpers.SerializableImage;
import gnu.jpdf.PDFGraphics;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
/**
*
* @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();
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(System.out, webHelp, filter);
}
public static void printCmdLineUsage(PrintStream out, boolean webHelp, String filter) {
int cnt = 1;
out.println("Commandline arguments:");
if (filter == null) {
out.println(" " + (cnt++) + ") -help | --help | /?");
out.println(" ...shows commandline arguments (this help)");
out.println(" " + (cnt++) + ") [ ...]");
out.println(" ...opens SWF file(s) with the decompiler GUI");
}
if (filter == null || filter.equals("proxy")) {
out.println(" " + (cnt++) + ") -proxy [-P]");
out.println(" ...auto start proxy in the tray. Optional parameter -P specifies port for proxy. Defaults to 55555. ");
}
if (filter == null || filter.equals("export")) {
out.println(" " + (cnt++) + ") -export ");
out.println(" ...export sources to .");
out.println(" Exports all files from when it is a folder.");
out.println(" Values for parameter:");
out.println(" script - Scripts (Default format: ActionScript source)");
out.println(" image - Images (Default format: PNG/JPEG)");
out.println(" shape - Shapes (Default format: SVG)");
out.println(" morphshape - MorphShapes (Default format: SVG)");
out.println(" movie - Movies (Default format: FLV without sound)");
out.println(" font - Fonts (Default format: TTF)");
out.println(" font4 - DefineFont4 (Default format: CFF)");
out.println(" frame - Frames (Default format: PNG)");
out.println(" sprite - Sprites (Default format: PNG)");
out.println(" button - Buttons (Default format: PNG)");
out.println(" sound - Sounds (Default format: MP3/WAV/FLV only sound)");
out.println(" binaryData - Binary data (Default format: Raw data)");
out.println(" symbolClass - Symbol-Class mapping (Default format: CSV)");
out.println(" text - Texts (Default format: Plain text)");
out.println(" all - Every resource (but not FLA and XFL)");
out.println(" fla - Everything to FLA compressed format");
out.println(" xfl - Everything to uncompressed FLA format (XFL)");
out.println(" You can export multiple types of items by using colon \",\"");
out.println(" DO NOT PUT space between comma (,) and next value.");
out.println();
}
if (filter == null || filter.equals("format")) {
out.println(" " + (cnt++) + ") -format ");
out.println(" ...sets output formats for export");
out.println(" Values for parameter:");
out.println(" script:as - ActionScript source");
out.println(" script:pcode - ActionScript P-code");
out.println(" script:pcodehex - ActionScript P-code with hex");
out.println(" script:hex - ActionScript Hex only");
out.println(" shape:svg - SVG format for Shapes");
out.println(" shape:png - PNG format for Shapes");
out.println(" shape:canvas - HTML5 Canvas format for Shapes");
out.println(" shape:bmp - BMP format for Shapes");
out.println(" morphshape:svg - SVG format for MorphShapes");
out.println(" morphshape:canvas - HTML5 Canvas format for MorphShapes");
out.println(" frame:png - PNG format for Frames");
out.println(" frame:gif - GIF format for Frames");
out.println(" frame:avi - AVI format for Frames");
out.println(" frame:svg - SVG format for Frames");
out.println(" frame:canvas - HTML5 Canvas format for Frames");
out.println(" frame:pdf - PDF format for Frames");
out.println(" frame:bmp - BMP format for Frames");
out.println(" sprite:png - PNG format for Sprites");
out.println(" sprite:gif - GIF format for Sprites");
out.println(" sprite:avi - AVI format for Sprites");
out.println(" sprite:svg - SVG format for Sprites");
out.println(" sprite:canvas - HTML5 Canvas format for Sprites");
out.println(" sprite:pdf - PDF format for Sprites");
out.println(" sprite:bmp - BMP format for Sprites");
out.println(" button:png - PNG format for Buttons");
out.println(" button:svg - SVG format for Buttons");
out.println(" button:bmp - BMP format for Buttons");
out.println(" image:png_gif_jpeg - PNG/GIF/JPEG format for Images");
out.println(" image:png - PNG format for Images");
out.println(" image:jpeg - JPEG format for Images");
out.println(" image:bmp - BMP format for Images");
out.println(" image:png_gif_jpeg_alpha - PNG/GIF/JPEG+ALPHA format for Images");
out.println(" text:plain - Plain text format for Texts");
out.println(" text:formatted - Formatted text format for Texts");
out.println(" text:svg - SVG format for Texts");
out.println(" sound:mp3_wav_flv - MP3/WAV/FLV format for Sounds");
out.println(" sound:mp3_wav - MP3/WAV format for Sounds");
out.println(" sound:wav - WAV format for Sounds");
out.println(" sound:flv - FLV format for Sounds");
out.println(" font:ttf - TTF format for Fonts");
out.println(" font:woff - WOFF format for Fonts");
out.println(" font4:cff - CFF format for DefineFont4");
out.println(" fla: or xfl: - Specify FLA format version");
out.println(" - values for : cs5,cs5.5,cs6,cc");
out.println(" You can set multiple formats at once using comma (,)");
out.println(" DO NOT PUT space between comma (,) and next value.");
out.println(" The prefix with colon (:) is neccessary.");
}
if (filter == null || filter.equals("cli")) {
out.println(" " + (cnt++) + ") -cli");
out.println(" ...Command line mode. Parses the SWFs without opening the GUI");
}
if (filter == null || filter.equals("select")) {
out.println(" " + (cnt++) + ") -select ");
out.println(" ...selects frames/pages for export");
out.println(" Example formats:");
out.println(" 1-5");
out.println(" 2,3");
out.println(" 2-5,7,9-");
out.println(" DO NOT PUT space between comma (,) and next ramge.");
out.println(" " + (cnt++) + ") -selectid ");
out.println(" ...selects characters for export by character id");
out.println(" format is same as in -select");
}
if (filter == null || filter.equals("selectclass")) {
out.println(" " + (cnt++) + ") -selectclass ");
out.println(" ...selects scripts to export by class name (ActionScript 3 ONLY)");
out.println(" format:");
out.println(" com.example.MyClass");
out.println(" com.example.+ (all classes in package \"com.example\")");
out.println(" com.++,net.company.MyClass (all classes in package \"com\" and all subpackages, class net.company.MyClass)");
out.println(" DO NOT PUT space between comma (,) and next class.");
}
if (filter == null || filter.equals("exportembed")) {
out.println(" " + (cnt++) + ") -exportembed");
out.println(" ...Allows exporting embedded assets via [Embed tag]");
out.println(" Use in combination with -export -format script:as");
}
if (filter == null || filter.equals("dumpswf")) {
out.println(" " + (cnt++) + ") -dumpSWF ");
out.println(" ...dumps list of SWF tags to console");
}
if (filter == null || filter.equals("dumpas2")) {
out.println(" " + (cnt++) + ") -dumpAS2 ");
out.println(" ...dumps list of AS1/2 scripts to console");
}
if (filter == null || filter.equals("dumpas3")) {
out.println(" " + (cnt++) + ") -dumpAS3 ");
out.println(" ...dumps list of AS3 scripts to console");
}
if (filter == null || filter.equals("compress")) {
out.println(" " + (cnt++) + ") -compress [(zlib|lzma)]");
out.println(" ...Compress SWF and save it to . If is already compressed, it will be re-compressed. Default compression method is ZLIB");
}
if (filter == null || filter.equals("decompress")) {
out.println(" " + (cnt++) + ") -decompress ");
out.println(" ...Decompress and save it to ");
}
if (filter == null || filter.equals("decrypt")) {
out.println(" " + (cnt++) + ") -decrypt ");
out.println(" ...Decrypts HARMAN Air encrypted file and save it to ");
}
if (filter == null || filter.equals("swf2xml")) {
out.println(" " + (cnt++) + ") -swf2xml ");
out.println(" ...Converts the SWF to XML file");
}
if (filter == null || filter.equals("xml2swf")) {
out.println(" " + (cnt++) + ") -xml2swf ");
out.println(" ...Converts the XML to SWF file");
}
if (filter == null || filter.equals("extract")) {
out.println(" " + (cnt++) + ") -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]");
out.println(" ...Extracts SWF files from ZIP or other binary files");
out.println(" ...-o parameter should contain a file path when \"biggest\" or \"first\" parameter is specified");
out.println(" ...-o parameter should contain a folder path when no extaction mode or \"all\" parameter is specified");
}
if (filter == null || filter.equals("memorysearch")) {
out.println(" " + (cnt++) + ") -memorySearch (|) (|)...");
out.println(" ...Search SWF files in the memory");
}
if (filter == null || filter.equals("renameinvalididentifiers")) {
out.println(" " + (cnt++) + ") -renameInvalidIdentifiers (typeNumber|randomWord) ");
out.println(" ...Renames the invalid identifiers in and save it to ");
}
if (filter == null || filter.equals("config")) {
out.println(" " + (cnt++) + ") -config key=value[,key2=value2][,key3=value3...] [other parameters]");
out.print(" ...Sets configuration values. Use -listconfigs command to list the available configuration settings.");
out.println();
out.println(" Values are boolean, you can use 0/1, true/false, on/off or yes/no.");
out.println(" If no other parameters passed, configuration is saved. Otherwise it is used only once.");
out.println(" DO NOT PUT space between comma (,) and next value.");
}
if (filter == null || filter.equals("onerror")) {
out.println(" " + (cnt++) + ") -onerror (abort|retryN|ignore)");
out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file");
}
if (filter == null || filter.equals("timeout")) {
out.println(" " + (cnt++) + ") -timeout ");
out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds");
}
if (filter == null || filter.equals("exporttimeout")) {
out.println(" " + (cnt++) + ") -exportTimeout ");
out.println(" ...total export timeout in seconds");
}
if (filter == null || filter.equals("exportfiletimeout")) {
out.println(" " + (cnt++) + ") -exportFileTimeout ");
out.println(" ...export timeout for a single AS3 class in seconds");
}
if (filter == null || filter.equals("stat")) {
out.println(" " + (cnt++) + ") -stat");
out.println(" ...show export performance statistics");
}
if (filter == null || filter.equals("flashpaper2pdf")) {
out.println(" " + (cnt++) + ") -flashpaper2pdf ");
out.println(" ...converts FlashPaper SWF file to PDF . Use -zoom parameter to specify image quality.");
}
if (filter == null || filter.equals("zoom")) {
out.println(" " + (cnt++) + ") -zoom ");
out.println(" ...apply zoom during export");
}
if (filter == null || filter.equals("replace")) {
out.println(" " + (cnt++) + ") -replace (|) [nofill] ([][]) [(|) [nofill] ([][])]...");
out.println(" ...replaces the data of the specified BinaryData, Image, Shape, Text, Sound tag or Script");
out.println(" ...nofill parameter can be specified only for shape replace");
out.println(" ... parameter can be specified for Image and Shape tags");
out.println(" ...valid formats: lossless, lossless2, jpeg2, jpeg3, jpeg4");
out.println(" ... parameter should be specified if and only if the imported entity is an AS3 P-Code");
out.println(" ...use -1 as characterId to replace main timeline SoundStreamHead");
out.println(" " + (cnt++) + ") -replace ");
out.println(" ... same as -replace command, but the rest of arguments is read as lines from a text file ");
}
if (filter == null || filter.equals("replacealpha")) {
out.println(" " + (cnt++) + ") -replaceAlpha [ ]...");
out.println(" ...replaces the alpha channel of the specified JPEG3 or JPEG4 tag");
}
if (filter == null || filter.equals("replacecharacter")) {
out.println(" " + (cnt++) + ") -replaceCharacter [ ]...");
out.println(" ...replaces a character tag with another character tag from the same SWF");
}
if (filter == null || filter.equals("replacecharacterid")) {
out.println(" " + (cnt++) + ") -replaceCharacterId ,,,... or");
out.println(" " + (cnt++) + ") -replaceCharacterId (pack|sort)");
out.println(" ...replaces the character id with ");
out.println(" ...pack: removes the spaces between the character ids (1,4,3 => 1,3,2)");
out.println(" ...sort: assigns increasing IDs to the character tags + pack (1,4,3 => 1,2,3)");
out.println(" DO NOT PUT space between comma (,) and next value.");
}
if (filter == null || filter.equals("remove")) {
out.println(" " + (cnt++) + ") -remove []...");
out.println(" ...removes a tag from the SWF");
}
if (filter == null || filter.equals("removecharacter")) {
out.println(" " + (cnt++) + ") -removeCharacter[WithDependencies] []...");
out.println(" ...removes a character tag from the SWF");
}
if (filter == null || filter.equals("importsymbolclass")) {
out.println(" " + (cnt++) + ") -importSymbolClass ");
out.println(" ...imports Symbol-Class mapping to and saves the result to ");
}
if (filter == null || filter.equals("importmovies")) {
out.println(" " + (cnt++) + ") -importMovies ");
out.println(" ...imports movies to and saves the result to ");
}
if (filter == null || filter.equals("importsounds")) {
out.println(" " + (cnt++) + ") -importSounds ");
out.println(" ...imports sounds to and saves the result to ");
}
if (filter == null || filter.equals("importshapes")) {
out.println(" " + (cnt++) + ") -importShapes [nofill] ");
out.println(" ...imports shapes to and saves the result to ");
}
if (filter == null || filter.equals("importimages")) {
out.println(" " + (cnt++) + ") -importImages ");
out.println(" ...imports images to and saves the result to ");
}
if (filter == null || filter.equals("importtext")) {
out.println(" " + (cnt++) + ") -importText ");
out.println(" ...imports texts to and saves the result to ");
}
if (filter == null || filter.equals("importscript")) {
out.println(" " + (cnt++) + ") -importScript ");
out.println(" ...imports scripts to and saves the result to ");
}
if (filter == null || filter.equals("deobfuscate")) {
out.println(" " + (cnt++) + ") -deobfuscate ");
out.println(" ...Deobfuscates AS3 P-code in and saves result to ");
out.println(" ... can be one of: traps/2/max, deadcode/1");
out.println(" ...WARNING: The deobfuscation result is still probably far enough to be openable by other decompilers.");
}
if (filter == null || filter.equals("enabledebugging")) {
out.println(" " + (cnt++) + ") -enabledebugging [-injectas3|-generateswd] [-pcode] ");
out.println(" ...Enables debugging for and saves result to ");
out.println(" ...-injectas3 (optional) causes debugfile and debugline instructions to be injected into the code to match decompiled/pcode source.");
out.println(" ...-generateswd (optional) parameter creates SWD file needed for AS1/2 debugging. for , is generated");
out.println(" ...-pcode (optional) parameter specified after -injectas3 or -generateswd causes lines to be handled as lines in P-code => All P-code lines are injected, etc.");
out.println(" ...WARNING: Injected/SWD script filenames may be different than from standard compiler");
}
if (filter == null || filter.equals("custom")) {
out.println(" " + (cnt++) + ") -custom []...");
out.println(" ...Forwards all parameters after the -custom parameter to the plugins");
}
if (filter == null || filter.equals("doc")) {
out.println(" " + (cnt++) + ") -doc -type [-out ] [-format ] [-locale ]");
out.println(" ...Generate documentation");
out.println(" ...-type Selects documentation type");
out.println(" ... can be currently only: as3.pcode.instructions for list of ActionScript3 AVM2 instructions");
out.println(" ...-out (optional) If specified, output is written to instead of stdout");
out.println(" ...-format (optional, html is default) Selects output format");
out.println(" ... is currently only html");
out.println(" ...-locale (optional) Override default locale");
out.println(" ... is localization identifier, en for english for example");
out.println(" ... is currently only html");
}
if (filter == null || filter.equals("getinstancemetadata")) {
out.println(" " + (cnt++) + ") -getInstanceMetadata -instance [-outputFormat ] [-key ] [-datafile ] ");
out.println(" ...reads instance metadata");
out.println(" ...-instance : name of instance to fetch metadata from");
out.println(" ...-outputFormat (optional): format of output - one of: jslike|raw. Default is jslike.");
out.println(" ...- key (optional): name of subkey to display. When present, only value from subkey is shown, whole object value otherwise.");
out.println(" ...-datafile (optional): File to write the data to. If ommited, stdout is used.");
out.println(" ...: SWF file to read metadata from");
}
if (filter == null || filter.equals("setinstancemetadata")) {
out.println(" " + (cnt++) + ") -setInstanceMetadata -instance [-inputFormat ] [-key ] [-value | -datafile ] [-outfile ] ");
out.println(" ...adds metadata to instance");
out.println(" ...-instance : name of instance to replace data in");
out.println(" ...-inputFormat : format of input data - one of: jslike|raw. Default is jslike.");
out.println(" ...- key (optional): name of subkey to use. When present, the value is set as object property with the name.");
out.println(" Otherwise the value is set directly to the instance without any subkeys.");
out.println(" ...-value (optional): value to set.");
out.println(" ...-datafile (optional): value to set from file.");
out.println(" ...If no -value or -infile parameter present, the value to set is taken from stdin.");
out.println(" ...-outfile (optional): Where to save resulting file. If ommited, original SWF file is overwritten.");
out.println(" ...: SWF file to search instance in");
}
if (filter == null || filter.equals("removeinstancemetadata")) {
out.println(" " + (cnt++) + ") -removeInstanceMetadata -instance [-key ] [-outfile ] ");
out.println(" ...removes metadata from instance");
out.println(" ...-instance : name of instance to remove data from");
out.println(" ...- key (optional): name of subkey to remove. When present, only the value from subkey of the AMF object is removed.");
out.println(" Otherwise all metadata are removed from the instance.");
out.println(" ...-outfile (optional): Where to save resulting file. If ommited, original SWF file is overwritten.");
out.println(" ...: SWF file to search instance in");
}
if (filter == null || filter.equals("linkreport")) {
out.println(" " + (cnt++) + ") -linkReport [-outfile ] ");
out.println(" ...generates linker report for the swffile");
out.println(" ...-outfile (optional): Saves XML report to . When ommited, the report is printed to stdout.");
out.println(" ...: SWF file to search instance in");
}
if (filter == null || filter.equals("swf2swc")) {
out.println(" " + (cnt++) + ") -swf2swc ");
out.println(" ...generates SWC file from SWF");
out.println(" ...: Where to save SWC file");
out.println(" ...: Input SWF file");
}
if (filter == null || filter.equals("abcmerge")) {
out.println(" " + (cnt++) + ") -abcmerge ");
out.println(" ...merge all ABC tags in SWF file to one");
out.println(" ...: Where to save merged file");
out.println(" ...: Input SWF file");
}
if (filter == null || filter.equals("swf2exe")) {
out.println(" " + (cnt++) + ") -swf2exe ");
out.println(" ...export SWF to executable file");
out.println(" ...: wrapper|projector_win||projector_mac|projector_linux");
}
if (filter == null || filter.equals("charset")) {
out.println(" " + (cnt++) + ") -charset ");
out.println(" ...sets desired character set for reading/writing SWF files with SWF version <= 5");
out.println(" (use in combination with other commands)");
}
if (filter == null || filter.equals("air")) {
out.println(" " + (cnt++) + ") -air");
out.println(" ...use AIR (airglobal.swc) for AS3 compilation instead of playerglobal.swc");
out.println(" (use in combination with other commands)");
}
printCmdLineUsageExamples(out, filter);
System.out.println("You can use special value \"/dev/stdin\" for input files to read data from standard input (even on Windows)");
}
private static void printCmdLineUsageExamples(PrintStream out, String filter) {
out.println();
out.println("Examples:");
final String PREFIX = "java -jar ffdec.jar ";
boolean exampleFound = false;
if (filter == null) {
out.println(PREFIX + "myfile.swf");
exampleFound = true;
}
if (filter == null || filter.equals("proxy")) {
out.println(PREFIX + "-proxy");
out.println(PREFIX + "-proxy -P1234");
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 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");
}
if (!exampleFound) {
out.println("Sorry, no example found for command " + filter + ", Let us know in issue tracker when you need it.");
}
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 {
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;
Map format = new HashMap<>();
double zoom = 1;
String charset = Charset.defaultCharset().name();
boolean cliMode = false;
boolean air = false;
boolean exportEmbed = false;
Selection selection = new Selection();
Selection selectionIds = new Selection();
List selectionClasses = null;
String nextParam = null, nextParamOriginal = null;
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);
break;
case "-select":
selection = parseSelect(args);
break;
case "-selectclass":
selectionClasses = parseSelectClass(args);
break;
case "-exportembed":
exportEmbed = true;
break;
case "-zoom":
zoom = parseZoom(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 "-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;
default:
break OUTER;
}
if (args.isEmpty()) {
return null;
}
}
String command = "";
if (nextParam == null) {
nextParam = "";
}
if (nextParam.startsWith("-")) {
command = nextParam.substring(1);
}
if (command.equals("translator")) {
Translator.main(new String[]{});
} else if (command.equals("swf2exe")) {
parseSwf2Exe(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);
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("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("importtext")) {
parseImportText(args, charset);
System.exit(0);
} else if (command.equals("importscript")) {
parseImportScript(args, charset, air);
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("-help") || nextParam.equals("--help") || nextParam.equals("/?") || nextParam.equals("\\_") /* /? translates as this on windows */) {
printHeader();
printCmdLineUsage(null, false);
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() {
System.out.println(ApplicationInfo.applicationVerName);
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);
for (String c : cfgs) {
String[] cp = c.split("=");
if (cp.length == 1) {
cp = new String[]{cp[0], "1"};
}
Field field = fields.get(cp[0].toLowerCase(Locale.ENGLISH));
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);
}
}
}
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 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 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 | Amf3ParseException | 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