diff --git a/src/com/jpexs/decompiler/flash/ApplicationInfo.java b/src/com/jpexs/decompiler/flash/ApplicationInfo.java index a1f06fa39..d5f83598e 100644 --- a/src/com/jpexs/decompiler/flash/ApplicationInfo.java +++ b/src/com/jpexs/decompiler/flash/ApplicationInfo.java @@ -1,64 +1,64 @@ -/* - * Copyright (C) 2010-2014 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; - -import java.io.IOException; -import java.util.Properties; - -/** - * - * @author JPEXS - */ -public class ApplicationInfo { - - public static final String APPLICATION_NAME = "JPEXS Free Flash Decompiler"; - public static final String SHORT_APPLICATION_NAME = "FFDec"; - public static final String VENDOR = "JPEXS"; - public static String version = ""; - public static String build = ""; - public static boolean nightly = false; - public static String applicationVerName; - public static String shortApplicationVerName; - public static final String PROJECT_PAGE = "http://www.free-decompiler.com/flash"; - public static String updatePageStub = "http://www.free-decompiler.com/flash/update.html?currentVersion="; - public static String updatePage; - - static { - loadProperties(); - } - - private static void loadProperties() { - Properties prop = new Properties(); - try { - prop.load(ApplicationInfo.class.getResourceAsStream("/project.properties")); - version = prop.getProperty("version"); - build = prop.getProperty("build"); - nightly = prop.getProperty("nightly").equals("true"); - if (nightly) { - version = version + " nightly build " + build.substring(0,7); - } - } catch (IOException | NullPointerException ex) { - //ignore - version = "unknown"; - } - - applicationVerName = APPLICATION_NAME + " v." + version; - updatePage = updatePageStub + version; - shortApplicationVerName = SHORT_APPLICATION_NAME + " v." + version; - } - -} +/* + * Copyright (C) 2010-2014 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; + +import java.io.IOException; +import java.util.Properties; + +/** + * + * @author JPEXS + */ +public class ApplicationInfo { + + public static final String APPLICATION_NAME = "JPEXS Free Flash Decompiler"; + public static final String SHORT_APPLICATION_NAME = "FFDec"; + public static final String VENDOR = "JPEXS"; + public static String version = ""; + public static String build = ""; + public static boolean nightly = false; + public static String applicationVerName; + public static String shortApplicationVerName; + public static final String PROJECT_PAGE = "http://www.free-decompiler.com/flash"; + public static String updatePageStub = "http://www.free-decompiler.com/flash/update.html?currentVersion="; + public static String updatePage; + + static { + loadProperties(); + } + + private static void loadProperties() { + Properties prop = new Properties(); + try { + prop.load(ApplicationInfo.class.getResourceAsStream("/project.properties")); + version = prop.getProperty("version"); + build = prop.getProperty("build"); + nightly = prop.getProperty("nightly").equals("true"); + if (nightly) { + version = version + " nightly build " + build.substring(0, 7); + } + } catch (IOException | NullPointerException ex) { + //ignore + version = "unknown"; + } + + applicationVerName = APPLICATION_NAME + " v." + version; + updatePage = updatePageStub + version; + shortApplicationVerName = SHORT_APPLICATION_NAME + " v." + version; + } + +} diff --git a/src/com/jpexs/decompiler/flash/SWF.java b/src/com/jpexs/decompiler/flash/SWF.java index 50be87d18..b667adf3d 100644 --- a/src/com/jpexs/decompiler/flash/SWF.java +++ b/src/com/jpexs/decompiler/flash/SWF.java @@ -1,2986 +1,2930 @@ -/* - * Copyright (C) 2010-2014 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; - -import SevenZip.Compression.LZMA.Encoder; -import com.jpacker.JPacker; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionDeobfuscation; -import com.jpexs.decompiler.flash.action.ActionGraphSource; -import com.jpexs.decompiler.flash.action.ActionLocalData; -import com.jpexs.decompiler.flash.action.model.ConstantPool; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -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.TextExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; -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.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -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.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.timeline.Clip; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.SvgClip; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.AS2PackageNodeItem; -import com.jpexs.decompiler.flash.treeitems.AS3PackageNodeItem; -import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.treenodes.AS2PackageNode; -import com.jpexs.decompiler.flash.treenodes.ContainerNode; -import com.jpexs.decompiler.flash.treenodes.FrameNode; -import com.jpexs.decompiler.flash.treenodes.TagNode; -import com.jpexs.decompiler.flash.treenodes.TreeNode; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.BlendComposite; -import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; -import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; -import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.decompiler.flash.xfl.XFLConverter; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import gnu.jpdf.PDFJob; -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.awt.print.PageFormat; -import java.awt.print.Paper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.InflaterInputStream; -import javax.imageio.ImageIO; -import javax.imageio.stream.FileImageOutputStream; -import javax.imageio.stream.ImageOutputStream; -import javax.xml.bind.DatatypeConverter; -import net.kroo.elliot.GifSequenceWriter; -import org.monte.media.VideoFormatKeys; -import org.monte.media.avi.AVIWriter; - -/** - * Class representing SWF file - * - * @author JPEXS - */ -public final class SWF implements TreeItem, Timelined { - - /** - * Default version of SWF file format - */ - public static final int DEFAULT_VERSION = 10; - /** - * Tags inside of file - */ - public List tags = new ArrayList<>(); - public boolean hasEndTag; - /** - * ExportRectangle for the display - */ - public RECT displayRect; - /** - * Movie frame rate - */ - public int frameRate; - /** - * Number of frames in movie - */ - public int frameCount; - /** - * Version of SWF - */ - public int version; - /** - * Size of the file - */ - public long fileSize; - /** - * Use compression - */ - public boolean compressed = false; - /** - * Use LZMA compression - */ - public boolean lzma = false; - /** - * Compressed size of the file (LZMA) - */ - public long compressedSize; - /** - * LZMA Properties - */ - public byte[] lzmaProperties; - public FileAttributesTag fileAttributes; - /** - * ScaleForm GFx - */ - public boolean gfx = false; - - public SWFList swfList; - public String file; - public String fileTitle; - public boolean readOnly; - public boolean isAS3; - public Map characters = new HashMap<>(); - public List abcList; - public JPEGTablesTag jtt; - public Map sourceFontsMap = new HashMap<>(); - public static final double unitDivisor = 20; - - private Timeline timeline; - - public void updateCharacters() { - parseCharacters(new ArrayList(tags)); - } - - private void parseCharacters(List list) { - for (ContainerItem t : list) { - if (t instanceof CharacterTag) { - characters.put(((CharacterTag) t).getCharacterId(), (CharacterTag) t); - } - if (t instanceof Container) { - parseCharacters(((Container) t).getSubItems()); - } - } - } - - /** - * Unresolve recursive sprites - */ - private void checkInvalidSprites() { - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, new ArrayList())) { - tags.set(i, new Tag(this, t.getId(), "InvalidSprite", t.getOriginalHeaderData(), t.getOriginalData(), t.getPos())); - } - } - } - } - - private boolean isSpriteValid(DefineSpriteTag sprite, List path) { - if (path.contains(sprite.spriteId)) { - return false; - } - path.add(sprite.spriteId); - for (Tag t : sprite.subTags) { - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, path)) { - return false; - } - } - } - path.remove((Integer) sprite.spriteId); - return true; - } - - @Override - public Timeline getTimeline() { - if (timeline == null) { - timeline = new Timeline(this); - } - return timeline; - } - - @Override - public void resetTimeline() { - timeline = null; - } - - /** - * Gets all tags with specified id - * - * @param tagId Identificator of tag type - * @return List of tags - */ - public List getTagData(int tagId) { - List ret = new ArrayList<>(); - for (Tag tag : tags) { - if (tag.getId() == tagId) { - ret.add(tag); - } - } - return ret; - } - - /** - * Saves this SWF into new file - * - * @param os OutputStream to save SWF in - * @throws IOException - */ - public void saveTo(OutputStream os) throws IOException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version); - sos.writeRECT(displayRect); - sos.writeUI8(0); - sos.writeUI8(frameRate); - sos.writeUI16(frameCount); - - sos.writeTags(tags); - if (hasEndTag) { - sos.writeUI16(0); - } - sos.close(); - if (compressed && lzma) { - os.write('Z'); - } else if (compressed) { - os.write('C'); - } else { - if (gfx) { - os.write('G'); - } else { - os.write('F'); - } - } - if (gfx) { - os.write('F'); - os.write('X'); - } else { - os.write('W'); - os.write('S'); - } - os.write(version); - byte[] data = baos.toByteArray(); - sos = new SWFOutputStream(os, version); - sos.writeUI32(data.length + 8); - - if (compressed) { - if (lzma) { - Encoder enc = new Encoder(); - int val = lzmaProperties[0] & 0xFF; - int lc = val % 9; - int remainder = val / 9; - int lp = remainder % 5; - int pb = remainder / 5; - int dictionarySize = 0; - for (int i = 0; i < 4; i++) { - dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); - } - enc.SetDictionarySize(dictionarySize); - enc.SetLcLpPb(lc, lp, pb); - baos = new ByteArrayOutputStream(); - enc.SetEndMarkerMode(true); - enc.Code(new ByteArrayInputStream(data), baos, -1, -1, null); - data = baos.toByteArray(); - byte[] udata = new byte[4]; - udata[0] = (byte) (data.length & 0xFF); - udata[1] = (byte) ((data.length >> 8) & 0xFF); - udata[2] = (byte) ((data.length >> 16) & 0xFF); - udata[3] = (byte) ((data.length >> 24) & 0xFF); - os.write(udata); - os.write(lzmaProperties); - } else { - os = new DeflaterOutputStream(os); - } - } - os.write(data); - } finally { - if (os != null) { - os.close(); - } - } - } - - public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { - this(is, null, parallelRead); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param listener - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { - this(is, listener, parallelRead, false); - } - - /** - * Faster constructor to check SWF only - * - * @param is - * @throws java.io.IOException - */ - public SWF(InputStream is) throws IOException { - byte[] hdr = new byte[3]; - is.read(hdr); - String shdr = new String(hdr, Utf8Helper.charset); - if (!Arrays.asList( - "FWS", //Uncompressed Flash - "CWS", //ZLib compressed Flash - "ZWS", //LZMA compressed Flash - "GFX", //Uncompressed ScaleForm GFx - "CFX" //Compressed ScaleForm GFx - ).contains(shdr)) { - throw new IOException("Invalid SWF file"); - } - version = is.read(); - fileSize = (is.read() + (is.read() << 8) + (is.read() << 16) + (is.read() << 24)) & 0xffffffff; - - if (hdr[0] == 'C') { - is = new InflaterInputStream(is); - } - - if (hdr[0] == 'Z') { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - //outSize - is.read(); - is.read(); - is.read(); - is.read(); - int propertiesSize = 5; - lzmaProperties = new byte[propertiesSize]; - if (is.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { - throw new IOException("LZMA:input .lzma file is too short"); - } - long dictionarySize = 0; - for (int i = 0; i < 4; i++) { - dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); - if (dictionarySize > Runtime.getRuntime().freeMemory()) { - throw new IOException("LZMA: Too large dictionary size"); - } - } - - SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); - if (!decoder.SetDecoderProperties(lzmaProperties)) { - throw new IOException("LZMA:Incorrect stream properties"); - } - - if (!decoder.Code(is, baos, fileSize - 8)) { - throw new IOException("LZMA:Error in data stream"); - } - } else { - long toRead = fileSize - 8; - if (toRead > 0) { - byte[] bytes = new byte[4096]; - while (toRead > 4096) { - int read = is.read(bytes); - if (read == -1) { - throw new IOException("Invalid SWF file"); - } - toRead -= read; - } - while (toRead > 0) { - int read = is.read(bytes, 0, (int) toRead); - if (read == -1) { - throw new IOException("Invalid SWF file"); - } - toRead -= read; - } - } - } - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param listener - * @param parallelRead Use parallel threads? - * @param checkOnly Check only file validity - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException, InterruptedException { - byte[] hdr = new byte[3]; - is.read(hdr); - String shdr = new String(hdr, Utf8Helper.charset); - if (!Arrays.asList( - "FWS", //Uncompressed Flash - "CWS", //ZLib compressed Flash - "ZWS", //LZMA compressed Flash - "GFX", //Uncompressed ScaleForm GFx - "CFX" //Compressed ScaleForm GFx - ).contains(shdr)) { - throw new IOException("Invalid SWF file"); - } - version = is.read(); - SWFInputStream sis = new SWFInputStream(is, version, 4); - fileSize = sis.readUI32(); - - if (hdr[1] == 'F' && hdr[2] == 'X') { - gfx = true; - } - if (hdr[0] == 'C') { - byte[] uncompressedData = Helper.readStream(new InflaterInputStream(is)); - sis = new SWFInputStream(new ByteArrayInputStream(uncompressedData), version, 8); - compressed = true; - } - - if (hdr[0] == 'Z') { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - sis.readUI32(); //outSize - int propertiesSize = 5; - lzmaProperties = new byte[propertiesSize]; - if (sis.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { - throw new IOException("LZMA:input .lzma file is too short"); - } - long dictionarySize = 0; - for (int i = 0; i < 4; i++) { - dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); - if (dictionarySize > Runtime.getRuntime().freeMemory()) { - throw new IOException("LZMA: Too large dictionary size"); - } - } - - SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); - if (!decoder.SetDecoderProperties(lzmaProperties)) { - throw new IOException("LZMA:Incorrect stream properties"); - } - - if (!decoder.Code(sis, baos, fileSize - 8)) { - throw new IOException("LZMA:Error in data stream"); - } - sis = new SWFInputStream(new ByteArrayInputStream(baos.toByteArray()), version, 8); - compressed = true; - lzma = true; - } - - if (listener != null) { - sis.addPercentListener(listener); - } - sis.setPercentMax(fileSize); - displayRect = sis.readRECT(); - // FIXED8 (16 bit fixed point) frameRate - sis.readUI8(); //tmpFirstByetOfFrameRate - frameRate = sis.readUI8(); - frameCount = sis.readUI16(); - List tags = sis.readTagList(this, this, 0, parallelRead, true, !checkOnly, gfx); - if (tags.get(tags.size() - 1).getId() == EndTag.ID) { - hasEndTag = true; - tags.remove(tags.size() - 1); - } - this.tags = tags; - if (!checkOnly) { - checkInvalidSprites(); - updateInnerTagsForShowFrameTags(); - updateCharacters(); - assignExportNamesToSymbols(); - assignClassesToSymbols(); - findFileAttributes(); - findABCTags(); - } else { - boolean hasNonUnknownTag = false; - for (Tag tag : tags) { - if (tag.getOriginalData().length > 0 && Tag.getRequiredTags().contains(tag.getId())) { - hasNonUnknownTag = true; - } - } - if (!hasNonUnknownTag) { - throw new IOException("Invalid SWF file. No known tag found."); - } - } - } - - public void updateInnerTagsForShowFrameTags() { - Map tagMap = new HashMap<>(); - Map> tagPositionsInFrames = new HashMap<>(); - createTagMap(tagMap, tagPositionsInFrames, tags); - addInnerTagsForShowFrameTags(tagPositionsInFrames, tagMap, tags); - } - - private void createTagMap(Map tagMap, Map> tagPositionsInFrames, List tags) { - List tagPositionsInFrame = new ArrayList<>(); - for (Tag tag : tags) { - long pos = tag.getPos(); - tagMap.put(pos, tag); - if (ShowFrameTag.isNestedTagType(tag.getId())) { - tagPositionsInFrame.add(pos); - } else if (tag.getId() == ShowFrameTag.ID) { - tagPositionsInFrames.put(pos, tagPositionsInFrame); - tagPositionsInFrame = new ArrayList<>(); - } - - if (tag instanceof DefineSpriteTag) { - createTagMap(tagMap, tagPositionsInFrames, tag.getSubTags()); - } - } - } - - private void addInnerTagsForShowFrameTags(Map> tagPositionsInFrames, Map tagMap, List tags) { - for (Tag tag : tags) { - if (tag instanceof ShowFrameTag) { - ShowFrameTag showFrameTag = (ShowFrameTag) tag; - List tagPositions = tagPositionsInFrames.get(tag.getPos()); - if (tagPositions != null) { - List innerTags = new ArrayList<>(); - for (long tagPos : tagPositions) { - innerTags.add(tagMap.get(tagPos)); - } - showFrameTag.innerTags = innerTags; - } - } else if (tag instanceof DefineSpriteTag) { - addInnerTagsForShowFrameTags(tagPositionsInFrames, tagMap, tag.getSubTags()); - } - } - } - - @Override - public SWF getSwf() { - return this; - } - - /** - * Get title of the file - * - * @return file title - */ - public String getFileTitle() { - if (fileTitle != null) { - return fileTitle; - } - return file; - } - - public String getShortFileName() { - String title = getFileTitle(); - if (title == null) { - return ""; - } - return new File(title).getName(); - } - - private void findABCTags(){ - List objs = new ArrayList<>(); - objs.addAll(tags); - - ArrayList newAbcList = new ArrayList<>(); - getABCTags(objs, newAbcList); - this.abcList = newAbcList; - } - - - private static void getABCTags(List list, List actionScripts) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getABCTags(((Container) t).getSubItems(), actionScripts); - } - if (t instanceof ABCContainerTag) { - actionScripts.add((ABCContainerTag) t); - } - } - } - - private void findFileAttributes() { - for (Tag t : tags) { - if (t instanceof FileAttributesTag) { - fileAttributes = (FileAttributesTag) t; - break; - } - } - } - - private void assignExportNamesToSymbols() { - HashMap exportNames = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof ExportAssetsTag) { - ExportAssetsTag eat = (ExportAssetsTag) t; - for (int i = 0; i < eat.tags.size(); i++) { - if ((!exportNames.containsKey(eat.tags.get(i))) && (!exportNames.containsValue(eat.names.get(i)))) { - exportNames.put(eat.tags.get(i), eat.names.get(i)); - } - } - } - } - for (Tag t : tags) { - if (t instanceof CharacterIdTag) { - CharacterIdTag ct = (CharacterIdTag) t; - if (exportNames.containsKey(ct.getCharacterId())) { - ct.setExportName(exportNames.get(ct.getCharacterId())); - } - } - } - } - - public void assignClassesToSymbols() { - HashMap classes = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sct = (SymbolClassTag) t; - for (int i = 0; i < sct.tags.length; i++) { - if ((!classes.containsKey(sct.tags[i])) && (!classes.containsValue(sct.names[i]))) { - classes.put(sct.tags[i], sct.names[i]); - } - } - } - } - for (Tag t : tags) { - if (t instanceof CharacterIdTag) { - CharacterIdTag ct = (CharacterIdTag) t; - if (classes.containsKey(ct.getCharacterId())) { - ct.setClassName(classes.get(ct.getCharacterId())); - } - } - } - } - - /** - * Compress SWF file - * - * @param fis Input stream - * @param fos Output stream - * @return True on success - */ - public static boolean fws2cws(InputStream fis, OutputStream fos) { - try { - byte[] swfHead = new byte[8]; - fis.read(swfHead); - - if (swfHead[0] != 'F') { - fis.close(); - return false; - } - swfHead[0] = 'C'; - fos.write(swfHead); - fos = new DeflaterOutputStream(fos); - int i; - while ((i = fis.read()) != -1) { - fos.write(i); - } - - fis.close(); - fos.close(); - } catch (IOException ex) { - return false; - } - return true; - } - - public static boolean decompress(InputStream fis, OutputStream fos) { - try { - byte[] hdr = new byte[3]; - fis.read(hdr); - String shdr = new String(hdr, Utf8Helper.charset); - switch (shdr) { - case "CWS": { - int version = fis.read(); - SWFInputStream sis = new SWFInputStream(fis, version, 4); - long fileSize = sis.readUI32(); - SWFOutputStream sos = new SWFOutputStream(fos, version); - sos.write(Utf8Helper.getBytes("FWS")); - sos.writeUI8(version); - sos.writeUI32(fileSize); - InflaterInputStream iis = new InflaterInputStream(fis); - int i; - try { - while ((i = iis.read()) != -1) { - fos.write(i); - } - } catch (EOFException ex) { - } - fis.close(); - fos.close(); - break; - } - case "ZWS": { - int version = fis.read(); - SWFInputStream sis = new SWFInputStream(fis, version, 4); - long fileSize = sis.readUI32(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - sis.readUI32(); - int propertiesSize = 5; - byte[] lzmaProperties = new byte[propertiesSize]; - if (sis.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { - throw new IOException("LZMA:input .lzma file is too short"); - } - SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); - if (!decoder.SetDecoderProperties(lzmaProperties)) { - throw new IOException("LZMA:Incorrect stream properties"); - } - if (!decoder.Code(sis, baos, fileSize - 8)) { - throw new IOException("LZMA:Error in data stream"); - } - try (SWFOutputStream sos = new SWFOutputStream(fos, version)) { - sos.write(Utf8Helper.getBytes("FWS")); - sos.write(version); - sos.writeUI32(fileSize); - sos.write(baos.toByteArray()); - } - fis.close(); - fos.close(); - break; - } - default: - return false; - } - } catch (IOException ex) { - return false; - } - return true; - } - - public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { - try { - SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); - int cnt = swf.deobfuscateIdentifiers(renameType); - swf.assignClassesToSymbols(); - System.out.println(cnt + " identifiers renamed."); - swf.saveTo(fos); - } catch (InterruptedException ex) { - return false; - } catch (IOException ex) { - return false; - } - return true; - } - - public boolean exportAS3Class(String className, String outdir, ScriptExportMode exportMode, boolean parallel) throws Exception { - List abcTags = new ArrayList<>(); - - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - ABCContainerTag cnt = (ABCContainerTag) t; - abcTags.add(cnt); - } - } - for (int i = 0; i < abcTags.size(); i++) { - ABC abc = abcTags.get(i).getABC(); - ScriptPack scr = abc.findScriptPackByPath(className); - if (scr != null) { - String cnt = ""; - if (abc.script_info.size() > 1) { - cnt = "script " + (i + 1) + "/" + abc.script_info.size() + " "; - } - String exStr = "Exporting " + "tag " + (i + 1) + "/" + abcTags.size() + " " + cnt + scr.getPath() + " ..."; - informListeners("exporting", exStr); - scr.export(outdir, abcTags, exportMode, parallel); - exStr = "Exported " + "tag " + (i + 1) + "/" + abcTags.size() + " " + cnt + scr.getPath() + " ..."; - informListeners("exported", exStr); - return true; - } - } - return false; - } - - private List> uniqueAS3Packs(List> packs) { - List> ret = new ArrayList<>(); - for (MyEntry item : packs) { - for (MyEntry itemOld : ret) { - if (item.key.equals(itemOld.key)) { - Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Duplicate pack path found (" + itemOld.key + ")!"); - break; - } - } - ret.add(item); - } - return ret; - } - - public List> getAS3Packs() { - List abcTags = new ArrayList<>(); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - abcTags.add((ABCContainerTag) t); - } - } - List> packs = new ArrayList<>(); - for (int i = 0; i < abcTags.size(); i++) { - ABCContainerTag t = abcTags.get(i); - packs.addAll(t.getABC().getScriptPacks()); - } - return uniqueAS3Packs(packs); - } - - @Override - public RECT getRect() { - return displayRect; - } - - private class ExportPackTask implements Callable { - - ScriptPack pack; - String directory; - List abcList; - ScriptExportMode exportMode; - ClassPath path; - AtomicInteger index; - int count; - boolean parallel; - AbortRetryIgnoreHandler handler; - long startTime; - long stopTime; - - public ExportPackTask(AbortRetryIgnoreHandler handler, AtomicInteger index, int count, ClassPath path, ScriptPack pack, String directory, List abcList, ScriptExportMode exportMode, boolean parallel) { - this.pack = pack; - this.directory = directory; - this.abcList = abcList; - this.exportMode = exportMode; - this.path = path; - this.index = index; - this.count = count; - this.parallel = parallel; - this.handler = handler; - } - - @Override - public File call() throws Exception { - RunnableIOExResult rio = new RunnableIOExResult() { - @Override - public void run() throws IOException { - startTime = System.currentTimeMillis(); - this.result = pack.export(directory, abcList, exportMode, parallel); - stopTime = System.currentTimeMillis(); - } - }; - int currentIndex = index.getAndIncrement(); - synchronized (ABC.class) { - long time = stopTime - startTime; - informListeners("exporting", "Exporting script " + currentIndex + "/" + count + " " + path); - } - new RetryTask(rio, handler).run(); - synchronized (ABC.class) { - long time = stopTime - startTime; - informListeners("exported", "Exported script " + currentIndex + "/" + count + " " + path + ", " + Helper.formatTimeSec(time)); - } - return rio.result; - } - } - - public List exportActionScript2(AbortRetryIgnoreHandler handler, String outdir, ScriptExportMode exportMode, boolean parallel, EventListener evl) throws IOException { - List ret = new ArrayList<>(); - List list2 = new ArrayList<>(); - list2.addAll(tags); - List list = createASTagList(list2, null); - - TagNode.setExport(list, true); - if (!outdir.endsWith(File.separator)) { - outdir += File.separator; - } - outdir += "scripts" + File.separator; - ret.addAll(TagNode.exportNodeAS(handler, list, outdir, exportMode, evl)); - return ret; - } - - public List exportActionScript3(final AbortRetryIgnoreHandler handler, final String outdir, final ScriptExportMode exportMode, final boolean parallel) { - final AtomicInteger cnt = new AtomicInteger(1); - final List abcTags = new ArrayList<>(); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - abcTags.add((ABCContainerTag) t); - } - } - - final List ret = new ArrayList<>(); - final List> packs = getAS3Packs(); - - if (!parallel || packs.size() < 2) { - try { - CancellableWorker.call(new Callable() { - @Override - public Void call() throws Exception { - for (MyEntry item : packs) { - ExportPackTask task = new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel); - ret.add(task.call()); - } - return null; - } - }, Configuration.exportTimeout.get(), TimeUnit.SECONDS); - } catch (TimeoutException ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached", ex); - } catch (Exception ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); - } - } else { - ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); - List> futureResults = new ArrayList<>(); - for (MyEntry item : packs) { - Future future = executor.submit(new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel)); - futureResults.add(future); - } - - try { - executor.shutdown(); - if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached"); - } - } catch (InterruptedException ex) { - } finally { - executor.shutdownNow(); - } - - for (int f = 0; f < futureResults.size(); f++) { - try { - if (futureResults.get(f).isDone()) { - ret.add(futureResults.get(f).get()); - } - } catch (InterruptedException ex) { - } catch (ExecutionException ex) { - Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); - } - } - } - - return ret; - } - - public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportMode exportMode, boolean parallel) throws Exception { - boolean asV3Found = false; - List ret = new ArrayList<>(); - final EventListener evl = new EventListener() { - @Override - public void handleEvent(String event, Object data) { - if (event.equals("exporting") || event.equals("exported")) { - informListeners(event, data); - } - } - }; - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - asV3Found = true; - } - } - - if (asV3Found) { - ret.addAll(exportActionScript3(handler, outdir, exportMode, parallel)); - } else { - ret.addAll(exportActionScript2(handler, outdir, exportMode, parallel, evl)); - } - return ret; - } - - public static List createASTagList(List list, Tag parent) { - List ret = new ArrayList<>(); - int frame = 1; - List frames = new ArrayList<>(); - - List exportAssetsTags = new ArrayList<>(); - for (ContainerItem t : list) { - if (t instanceof ExportAssetsTag) { - exportAssetsTags.add((ExportAssetsTag) t); - } - TreeNode addNode = null; - if (t instanceof ShowFrameTag) { - // do not add PlaceObjects (+etc) to script nodes - FrameNode tti = new FrameNode(new FrameNodeItem(t.getSwf(), frame, parent, (ShowFrameTag) t, false), null, true); - - for (int r = ret.size() - 1; r >= 0; r--) { - if (!(ret.get(r).getItem() instanceof DefineSpriteTag)) { - if (!(ret.get(r).getItem() instanceof DefineButtonTag)) { - if (!(ret.get(r).getItem() instanceof DefineButton2Tag)) { - if (!(ret.get(r).getItem() instanceof DoInitActionTag)) { - if (!(ret.get(r).getItem() instanceof AS2PackageNodeItem)) { - tti.subNodes.add(ret.get(r)); - ret.remove(r); - } - } - } - } - } - } - frame++; - frames.add(tti); - } else if (t instanceof ASMSource) { - ContainerNode tti = new ContainerNode(t); - //ret.add(tti); - addNode = tti; - } else if (t instanceof Container) { - if (((Container) t).getItemCount() > 0) { - - ContainerNode tti = new ContainerNode(t); - List subItems = ((Container) t).getSubItems(); - - Tag tag = t instanceof Tag ? (Tag) t : null; - tti.subNodes = createASTagList(subItems, tag); - addNode = tti; - //ret.add(tti); - } - } - if (addNode != null) { - if (addNode.getItem() instanceof CharacterIdTag) { - CharacterIdTag cit = (CharacterIdTag) addNode.getItem(); - String path = cit.getExportName(); - if (path == null) { - path = ""; - } - String[] pathParts; - if (path.contains(".")) { - pathParts = path.split("\\."); - } else { - pathParts = new String[]{path}; - } - List items = ret; - int pos = 0; - TreeNode selNode = null; - do { - if (pos == pathParts.length - 1) { - break; - } - selNode = null; - for (TreeNode node : items) { - if (node.getItem() instanceof AS2PackageNodeItem) { - AS2PackageNodeItem pkg = (AS2PackageNodeItem) node.getItem(); - if (pkg.packageName.equals(pathParts[pos])) { - selNode = node; - break; - } - } - } - int pkgCount = 0; - for (; pkgCount < items.size(); pkgCount++) { - if (items.get(pkgCount).getItem() instanceof AS3PackageNodeItem) { - AS2PackageNodeItem pkg = (AS2PackageNodeItem) items.get(pkgCount).getItem(); - if (pkg.packageName.compareTo(pathParts[pos]) > 0) { - break; - } - } else { - break; - } - } - if (selNode == null) { - items.add(pkgCount, selNode = new AS2PackageNode(new AS2PackageNodeItem(pathParts[pos], t.getSwf()))); - } - pos++; - if (selNode != null) { - items = selNode.subNodes; - } - - } while (selNode != null); - - int clsCount = 0; - for (; clsCount < items.size(); clsCount++) { - if (items.get(clsCount).getItem() instanceof CharacterIdTag) { - CharacterIdTag ct = (CharacterIdTag) items.get(clsCount).getItem(); - String expName = ct.getExportName(); - if (expName == null) { - expName = ""; - } - if (expName.contains(".")) { - expName = expName.substring(expName.lastIndexOf('.') + 1); - } - if ((ct.getClass().getName() + "_" + expName).compareTo(addNode.getItem().getClass().getName() + "_" + pathParts[pos]) > 0) { - break; - } - } - } - items.add(clsCount, addNode); - } else { - ret.add(addNode); - } - } - - } - ret.addAll(frames); - for (int i = ret.size() - 1; i >= 0; i--) { - if (ret.get(i).getItem() instanceof DefineSpriteTag) { - ((DefineSpriteTag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; - } - if (ret.get(i).getItem() instanceof DefineButtonTag) { - ((DefineButtonTag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; - } - if (ret.get(i).getItem() instanceof DefineButton2Tag) { - ((DefineButton2Tag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; - } - /*if (ret.get(i).tag instanceof DoInitActionTag) { - //((DoInitActionTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags; - }*/ - if (ret.get(i).getItem() instanceof ASMSource) { - ASMSource ass = (ASMSource) ret.get(i).getItem(); - if (ass.containsSource()) { - continue; - } - } - if (ret.get(i).subNodes.isEmpty()) { - ret.remove(i); - } - } - return ret; - } - - public static void getTagsFromTreeNodes(List treeNodes, List result) { - for (TreeNode treeNode : treeNodes) { - TreeItem treeItem = treeNode.getItem(); - if (treeItem instanceof Tag) { - result.add((Tag) treeItem); - } - getTagsFromTreeNodes(treeNode.subNodes, result); - } - } - - private final HashSet listeners = new HashSet<>(); - - public final void addEventListener(EventListener listener) { - listeners.add(listener); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).addEventListener(listener); - } - } - } - - public final void removeEventListener(EventListener listener) { - listeners.remove(listener); - for (Tag t : tags) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).removeEventListener(listener); - } - } - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public static boolean hasErrorHeader(byte[] data) { - if (data.length > 4) { - if ((data[0] & 0xff) == 0xff) { - if ((data[1] & 0xff) == 0xd9) { - if ((data[2] & 0xff) == 0xff) { - if ((data[3] & 0xff) == 0xd8) { - return true; - } - } - } - } - } - return false; - } - - public static void populateVideoFrames(int streamId, List tags, HashMap output) { - for (ContainerItem t : tags) { - if (t instanceof VideoFrameTag) { - output.put(((VideoFrameTag) t).frameNum, (VideoFrameTag) t); - } - if (t instanceof Container) { - populateVideoFrames(streamId, ((Container) t).getSubItems(), output); - } - } - } - - - public void exportMovies(AbortRetryIgnoreHandler handler, String outdir, MovieExportSettings settings) throws IOException { - new MovieExporter().exportMovies(handler, outdir, tags, settings); - } - - public void exportSounds(AbortRetryIgnoreHandler handler, String outdir, SoundExportSettings settings) throws IOException { - new SoundExporter().exportSounds(handler, outdir, tags, settings); - } - - public void exportFonts(AbortRetryIgnoreHandler handler, String outdir, FontExportSettings settings) throws IOException { - new FontExporter().exportFonts(handler, outdir, tags, settings); - } - - private static void writeLE(OutputStream os, long val, int size) throws IOException { - for (int i = 0; i < size; i++) { - os.write((int) (val & 0xff)); - val >>= 8; - } - } - - public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { - ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); - int audioFormat = 1; //PCM - writeLE(subChunk1Data, audioFormat, 2); - int numChannels = soundType ? 2 : 1; - writeLE(subChunk1Data, numChannels, 2); - int[] rateMap = {5512, 11025, 22050, 44100}; - int sampleRate = soundRateHz;//rateMap[soundRate]; - writeLE(subChunk1Data, sampleRate, 4); - int bitsPerSample = soundSize ? 16 : 8; - int byteRate = sampleRate * numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, byteRate, 4); - int blockAlign = numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, blockAlign, 2); - writeLE(subChunk1Data, bitsPerSample, 2); - - ByteArrayOutputStream chunks = new ByteArrayOutputStream(); - chunks.write(Utf8Helper.getBytes("fmt ")); - byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); - writeLE(chunks, subChunk1DataBytes.length, 4); - chunks.write(subChunk1DataBytes); - - chunks.write(Utf8Helper.getBytes("data")); - writeLE(chunks, data.length, 4); - chunks.write(data); - - fos.write(Utf8Helper.getBytes("RIFF")); - byte[] chunkBytes = chunks.toByteArray(); - writeLE(fos, 4 + chunkBytes.length, 4); - fos.write(Utf8Helper.getBytes("WAVE")); - fos.write(chunkBytes); - } - - private static void makeAVI(List images, int frameRate, File file) throws IOException { - if (images.isEmpty()) { - return; - } - AVIWriter out = new AVIWriter(file); - out.addVideoTrack(VideoFormatKeys.ENCODING_AVI_PNG, 1, frameRate, images.get(0).getWidth(), images.get(0).getHeight(), 0, 0); - try { - for (BufferedImage img : images) { - out.write(0, img, 1); - } - } finally { - out.close(); - } - - } - - private static void makeGIF(List images, int frameRate, File file) throws IOException { - if (images.isEmpty()) { - return; - } - try (ImageOutputStream output = new FileImageOutputStream(file)) { - GifSequenceWriter writer = new GifSequenceWriter(output, images.get(0).getType(), 1000 / frameRate, true); - - for (BufferedImage img : images) { - writer.writeToSequence(img); - } - - writer.close(); - } - } - - private static String getTypePrefix(CharacterTag c) { - if (c instanceof ShapeTag) { - return "shape"; - } - if (c instanceof MorphShapeTag) { - return "morphshape"; - } - if (c instanceof DefineSpriteTag) { - return "sprite"; - } - if (c instanceof TextTag) { - return "text"; - } - if (c instanceof ButtonTag) { - return "button"; - } - if (c instanceof FontTag) { - return "font"; - } - if (c instanceof ImageTag) { - return "image"; - } - return "character"; - } - - - public static void writeLibrary(SWF fswf,Set library,OutputStream fos) throws IOException{ - for (int c : library) { - CharacterTag ch = fswf.characters.get(c); - if(ch instanceof FontTag){ - fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ch,textColor){\r\n")); - fos.write(Utf8Helper.getBytes(((FontTag) ch).toHtmlCanvas(1))); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - } - else if (ch instanceof ImageTag) { - ImageTag image = (ImageTag) ch; - String format = image.getImageFormat(); - InputStream imageStream = image.getImageData(); - byte[] imageData; - if (imageStream != null) { - imageData = Helper.readStream(image.getImageData()); - } else { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - ImageIO.write(image.getImage().getBufferedImage(), format.toUpperCase(Locale.ENGLISH), baos); - } catch (IOException ex) { - } - imageData = baos.toByteArray(); - } - String base64ImgData = DatatypeConverter.printBase64Binary(imageData); - fos.write(Utf8Helper.getBytes("var image" + c + " = document.createElement(\"img\");\r\nimage" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); - } else { - fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); - if (ch instanceof DrawableTag) { - fos.write(Utf8Helper.getBytes(((DrawableTag) ch).toHtmlCanvas(1))); - } - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - } - } - } - - public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, int containerId, List frames, final FramesExportSettings settings) throws IOException { - final List ret = new ArrayList<>(); - if (tags.isEmpty()) { - return ret; - } - Timeline tim = null; - String path = ""; - if (containerId == 0) { - tim = getTimeline(); - } else { - tim = ((Timelined) characters.get(containerId)).getTimeline(); - path = File.separator + Helper.makeFileName(characters.get(containerId).getExportFileName()); - } - if (frames == null) { - int frameCnt = tim.frames.size(); - frames = new ArrayList<>(); - for (int i = 0; i < frameCnt; i++) { - frames.add(i); - } - } - - final File foutdir = new File(outdir + path); - if (!foutdir.exists()) { - if (!foutdir.mkdirs()) { - if (!foutdir.exists()) { - throw new IOException("Cannot create directory " + outdir); - } - } - } - - final List fframes = frames; - - Color backgroundColor = null; - if (settings.mode == FramesExportMode.AVI) { - for (Tag t : tags) { - if (t instanceof SetBackgroundColorTag) { - SetBackgroundColorTag sb = (SetBackgroundColorTag) t; - backgroundColor = sb.backgroundColor.toColor(); - } - } - } - - if (settings.mode == FramesExportMode.SVG) { - for (int i = 0; i < frames.size(); i++) { - final int fi = i; - final Timeline ftim = tim; - final Color fbackgroundColor = backgroundColor; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - int frame = fframes.get(fi); - File f = new File(foutdir + File.separator + frame + ".svg"); - try (FileOutputStream fos = new FileOutputStream(f)) { - SVGExporter exporter = new SVGExporter(new ExportRectangle(ftim.displayRect)); - if (fbackgroundColor != null) { - exporter.setBackGroundColor(fbackgroundColor); - } - frameToSvg(ftim, frame, 0, null, 0, exporter, new ColorTransform(), 0); - fos.write(Utf8Helper.getBytes(exporter.getSVG())); - } - ret.add(f); - } - }, handler).run(); - } - return ret; - } - - if (settings.mode == FramesExportMode.CANVAS) { - final Timeline ftim = tim; - final Color fbackgroundColor = backgroundColor; - final SWF fswf = this; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - File fcanvas = new File(foutdir + File.separator + "canvas.js"); - Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); - ret.add(fcanvas); - - File f = new File(foutdir + File.separator + "frames.js"); - File fmin = new File(foutdir + File.separator + "frames.min.js"); - int width = (int) (ftim.displayRect.getWidth() / SWF.unitDivisor); - int height = (int) (ftim.displayRect.getHeight() / SWF.unitDivisor); - try (FileOutputStream fos = new FileOutputStream(f)) { - fos.write(Utf8Helper.getBytes("\r\n")); - Set library = new HashSet<>(); - getNeededCharacters(ftim, fframes, library); - - writeLibrary(fswf,library,fos); - - String currentName = ftim.id == 0 ? "main" : getTypePrefix(fswf.characters.get(ftim.id)) + ftim.id; - - fos.write(Utf8Helper.getBytes("function " + currentName + "(ctx,ctrans,frame,ratio,time){\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.transform(1,0,0,1,"+(-ftim.displayRect.Xmin/unitDivisor)+","+(-ftim.displayRect.Ymin/unitDivisor)+");\r\n")); - fos.write(Utf8Helper.getBytes(framesToHtmlCanvas(unitDivisor, ftim, fframes, 0, null, 0, ftim.displayRect, new ColorTransform(), fbackgroundColor))); - fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - - fos.write(Utf8Helper.getBytes("var frame = -1;\r\n")); - fos.write(Utf8Helper.getBytes("var time = 0;\r\n")); - fos.write(Utf8Helper.getBytes("var frames = [];\r\n")); - for (int i : fframes) { - fos.write(Utf8Helper.getBytes("frames.push(" + i + ");\r\n")); - } - fos.write(Utf8Helper.getBytes("\r\n")); - RGB backgroundColor = new RGB(255,255,255); - for(Tag t:fswf.tags){ - if(t instanceof SetBackgroundColorTag){ - SetBackgroundColorTag sb=(SetBackgroundColorTag)t; - backgroundColor = sb.backgroundColor; - } - } - - fos.write(Utf8Helper.getBytes("var backgroundColor = \""+backgroundColor.toHexRGB()+"\";\r\n")); - fos.write(Utf8Helper.getBytes("var originalWidth = "+width+";\r\n")); - fos.write(Utf8Helper.getBytes("var originalHeight= "+height+";\r\n")); - fos.write(Utf8Helper.getBytes("function nextFrame(ctx,ctrans){\r\n")); - fos.write(Utf8Helper.getBytes("\tvar oldframe = frame;\r\n")); - fos.write(Utf8Helper.getBytes("\tframe = (frame+1)%frames.length;\r\n")); - fos.write(Utf8Helper.getBytes("\tif(frame==oldframe){time++;}else{time=0;};\r\n")); - fos.write(Utf8Helper.getBytes("\tdrawFrame();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - - fos.write(Utf8Helper.getBytes("function drawFrame(){\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.fillStyle = backgroundColor;\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.fillRect(0,0,canvas.width,canvas.height);\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n")); - fos.write(Utf8Helper.getBytes("\t" + currentName + "(ctx,ctrans,frames[frame],0,time);\r\n")); - fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - fos.write(Utf8Helper.getBytes("window.setInterval(function(){nextFrame(ctx,ctrans);}," + (int) (1000.0 / ftim.swf.frameRate) + ");\r\n")); - } - - if (Configuration.packJavaScripts.get()) { - try{ - JPacker.main(new String[]{"-q", "-b", "62", "-o", fmin.getAbsolutePath(), f.getAbsolutePath()}); - f.delete(); - }catch(Exception|Error e){ //Something wrong in the packer - Logger.getLogger(SWF.class.getName()).log(Level.WARNING, "JPacker: Cannot minimize script"); - f.renameTo(fmin); - } - } else { - f.renameTo(fmin); - } - - File fh = new File(foutdir + File.separator + "frames.html"); - try (FileOutputStream fos = new FileOutputStream(fh); FileInputStream fis = new FileInputStream(fmin)) { - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlPrefix(width, height))); - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsPrefix())); - byte buf[] = new byte[1000]; - int cnt; - while ((cnt = fis.read(buf)) > 0) { - fos.write(buf, 0, cnt); - } - if (Configuration.packJavaScripts.get()) { - fos.write(Utf8Helper.getBytes(";")); - } - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsSuffix())); - fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlSuffix())); - } - fmin.delete(); - - ret.add(f); - } - }, handler).run(); - - return ret; - } - - final List frameImages = new ArrayList<>(); - for (int frame : frames) { - frameImages.add(frameToImageGet(tim, frame, 0, null, 0, tim.displayRect, new Matrix(), new ColorTransform(), backgroundColor).getBufferedImage()); - } - switch (settings.mode) { - case GIF: - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - File f = new File(foutdir + File.separator + "frames.gif"); - makeGIF(frameImages, frameRate, f); - ret.add(f); - } - }, handler).run(); - break; - case PNG: - for (int i = 0; i < frameImages.size(); i++) { - final int fi = i; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - File f = new File(foutdir + File.separator + fframes.get(fi) + ".png"); - ImageIO.write(frameImages.get(fi), "PNG", f); - ret.add(f); - } - }, handler).run(); - } - break; - case PDF: - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - File f = new File(foutdir + File.separator + "frames.pdf"); - PDFJob job=new PDFJob(new FileOutputStream(f)); - PageFormat pf=new PageFormat(); - pf.setOrientation(PageFormat.PORTRAIT); - Paper p = new Paper(); - - p.setSize(frameImages.get(0).getWidth()+10,frameImages.get(0).getHeight()+10); - pf.setPaper(p); - - for(int i=0;i deobfuscated = new HashMap<>(); - private List> allVariableNames = new ArrayList<>(); - private List allFunctions = new ArrayList<>(); - private HashMap allStrings = new HashMap<>(); - private final HashMap usageTypes = new HashMap<>(); - private final ActionDeobfuscation deobfuscation = new ActionDeobfuscation(); - - private static void getVariables(ConstantPool constantPool, BaseLocalData localData, Stack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { - boolean debugMode = false; - while ((ip > -1) && ip < code.size()) { - if (visited.contains(ip)) { - break; - } - GraphSourceItem ins = code.get(ip); - - if (debugMode) { - System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), code.version, ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); - } - if (ins.isExit()) { - break; - } - if (ins.isIgnored()) { - ip++; - continue; - } - - String usageType = "name"; - GraphTargetItem name = null; - if ((ins instanceof ActionGetVariable) - || (ins instanceof ActionGetMember) - || (ins instanceof ActionDefineLocal2) - || (ins instanceof ActionNewMethod) - || (ins instanceof ActionNewObject) - || (ins instanceof ActionCallMethod) - || (ins instanceof ActionCallFunction)) { - if (stack.isEmpty()) { - break; - } - name = stack.peek(); - } - - if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { - usageType = "variable"; - } - if (ins instanceof ActionGetMember) { - usageType = "member"; - } - if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { - usageType = "class"; - } - if (ins instanceof ActionCallMethod) { - usageType = "function"; //can there be method? - } - if (ins instanceof ActionCallFunction) { - usageType = "function"; - } - - if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { - functions.add(ins); - } - - if (ins instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; - List cntSizes = cnt.getContainerSizes(); - long addr = code.pos2adr(ip + 1); - ip = code.adr2pos(addr); - String cntName = cnt.getName(); - for (Long size : cntSizes) { - if (size == 0) { - continue; - } - ip = code.adr2pos(addr); - addr += size; - int nextip = code.adr2pos(addr); - getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap(), new HashMap(), new HashMap()), 0, path + (cntName == null ? "" : "/" + cntName)); - ip = nextip; - } - List> r = new ArrayList<>(); - r.add(new ArrayList()); - r.add(new ArrayList()); - r.add(new ArrayList()); - try { - ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); - } catch (EmptyStackException ex) { - } - //ip++; - continue; - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { - if (stack.size() < 2) { - break; - } - name = stack.get(stack.size() - 2); - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { - usageType = "variable"; - } - - if (ins instanceof ActionSetMember) { - usageType = "member"; - } - - if (name instanceof DirectValueActionItem) { - variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); - usageTypes.put((DirectValueActionItem) name, usageType); - } - - //for..in return - if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { - stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); - } - - if (ins instanceof ActionConstantPool) { - constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - - try { - ins.translate(localData, stack, output, staticOperation, path); - } catch (EmptyStackException ex) { - // probably obfucated code, never executed branch - break; - } - if (ins.isExit()) { - break; - } - - if (ins instanceof ActionPush) { - if (!stack.isEmpty()) { - GraphTargetItem top = stack.peek(); - if (top instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) top; - if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { - if (constantPool == null) { - constantPool = new ConstantPool(dvt.constants); - } - strings.put(dvt, constantPool); - } - } - } - } - - if (ins.isBranch() || ins.isJump()) { - if (ins instanceof ActionIf) { - if (stack.isEmpty()) { - break; - } - stack.pop(); - } - visited.add(ip); - List branches = ins.getBranches(code); - for (int b : branches) { - @SuppressWarnings("unchecked") - Stack brStack = (Stack) stack.clone(); - if (b >= 0) { - getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); - } else { - if (debugMode) { - System.out.println("Negative branch:" + b); - } - } - } - // } - break; - } - ip++; - }; - } - - private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageType, ActionGraphSource code, int addr, String path) throws InterruptedException { - ActionLocalData localData = new ActionLocalData(); - getVariables(null, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList(), usageType, path); - } - - private List> getVariables(List> variables, List functions, HashMap strings, HashMap usageType, ASMSource src, String path) throws InterruptedException { - List> ret = new ArrayList<>(); - List actions = src.getActions(); - actionsMap.put(src, actions); - getVariables(variables, functions, strings, usageType, new ActionGraphSource(actions, version, new HashMap(), new HashMap(), new HashMap()), 0, path); - return ret; - } - private HashMap> actionsMap = new HashMap<>(); - - private void getVariables(List objs, String path) throws InterruptedException { - List processed = new ArrayList<>(); - for (ContainerItem o : objs) { - if (o instanceof ASMSource) { - String infPath = path + "/" + o.toString(); - int pos = 1; - String infPath2 = infPath; - while (processed.contains(infPath2)) { - pos++; - infPath2 = infPath + "[" + pos + "]"; - } - processed.add(infPath2); - informListeners("getVariables", infPath2); - getVariables(allVariableNames, allFunctions, allStrings, usageTypes, (ASMSource) o, path); - } - if (o instanceof Container) { - getVariables(((Container) o).getSubItems(), path + "/" + o.toString()); - } - } - } - - public int deobfuscateAS3Identifiers(RenameType renameType) { - for (Tag tag : tags) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); - tag.setModified(true); - } - } - for (Tag tag : tags) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); - tag.setModified(true); - } - } - for (Tag tag : tags) { - if (tag instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) tag; - for (int i = 0; i < sc.names.length; i++) { - String newname = deobfuscation.deobfuscateNameWithPackage(sc.names[i], deobfuscated, renameType, deobfuscated); - if (newname != null) { - sc.names[i] = newname; - } - } - sc.setModified(true); - } - } - deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, new HashMap()); - return deobfuscated.size(); - } - - public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { - findFileAttributes(); - if (fileAttributes == null) { - int cnt = 0; - cnt += deobfuscateAS2Identifiers(renameType); - cnt += deobfuscateAS3Identifiers(renameType); - return cnt; - } else { - if (fileAttributes.actionScript3) { - return deobfuscateAS3Identifiers(renameType); - } else { - return deobfuscateAS2Identifiers(renameType); - } - } - } - - public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { - Map selected = new HashMap<>(); - selected.put(identifier, newname); - renameAS2Identifiers(null, selected); - } - - private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { - return renameAS2Identifiers(renameType, null); - } - - private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { - actionsMap = new HashMap<>(); - allFunctions = new ArrayList<>(); - allVariableNames = new ArrayList<>(); - allStrings = new HashMap<>(); - - List objs = new ArrayList<>(); - int ret = 0; - objs.addAll(tags); - getVariables(objs, ""); - informListeners("rename", ""); - int fc = 0; - for (MyEntry it : allVariableNames) { - String name = it.key.toStringNoH(it.value); - deobfuscation.allVariableNamesStr.add(name); - } - - informListeners("rename", "classes"); - int classCount = 0; - for (Tag t : tags) { - if (t instanceof DoInitActionTag) { - classCount++; - } - } - int cnt = 0; - for (Tag t : tags) { - if (t instanceof DoInitActionTag) { - cnt++; - informListeners("rename", "class " + cnt + "/" + classCount); - DoInitActionTag dia = (DoInitActionTag) t; - String exportName = dia.getExportName(); - final String pkgPrefix = "__Packages."; - String[] classNameParts = null; - if ((exportName != null) && exportName.startsWith(pkgPrefix)) { - String className = exportName.substring(pkgPrefix.length()); - if (className.contains(".")) { - classNameParts = className.split("\\."); - } else { - classNameParts = new String[]{className}; - } - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List dec; - try { - dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); - } catch (EmptyStackException ex) { - continue; - } - GraphTargetItem name = null; - for (GraphTargetItem it : dec) { - if (it instanceof ClassActionItem) { - ClassActionItem cti = (ClassActionItem) it; - List methods = new ArrayList<>(); - methods.addAll(cti.functions); - methods.addAll(cti.staticFunctions); - - for (GraphTargetItem gti : methods) { - if (gti instanceof FunctionActionItem) { - FunctionActionItem fun = (FunctionActionItem) gti; - if (fun.calculatedFunctionName instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; - String fname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(fname, false, "method", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(fname, changed); - } - } - } - } - - List vars = new ArrayList<>(); - for (MyEntry item : cti.vars) { - vars.add(item.key); - } - for (MyEntry item : cti.staticVars) { - vars.add(item.key); - } - for (GraphTargetItem gti : vars) { - if (gti instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) gti; - String vname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(vname, false, "attribute", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(vname, changed); - } - } - } - - name = cti.className; - break; - } - if (it instanceof InterfaceActionItem) { - InterfaceActionItem ift = (InterfaceActionItem) it; - name = ift.name; - } - } - - if (name != null) { - int pos = 0; - while (name instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) name; - GraphTargetItem memberName = mem.memberName; - if (memberName instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) memberName; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(nameStr, changedNameStr); - pos++; - } - name = mem.object; - } - if (name instanceof GetVariableActionItem) { - GetVariableActionItem var = (GetVariableActionItem) name; - if (var.name instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) var.name; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(nameStr, changedNameStr); - pos++; - } - } - } - t.setModified(true); - } - } - - for (GraphSourceItem fun : allFunctions) { - fc++; - informListeners("rename", "function " + fc + "/" + allFunctions.size()); - if (fun instanceof ActionDefineFunction) { - ActionDefineFunction f = (ActionDefineFunction) fun; - if (f.functionName.isEmpty()) { //anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - if (fun instanceof ActionDefineFunction2) { - ActionDefineFunction2 f = (ActionDefineFunction2) fun; - if (f.functionName.isEmpty()) { //anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - } - - HashSet stringsNoVarH = new HashSet<>(); - List allVariableNamesDv = new ArrayList<>(); - for (MyEntry it : allVariableNames) { - allVariableNamesDv.add(it.key); - } - for (DirectValueActionItem ti : allStrings.keySet()) { - if (!allVariableNamesDv.contains(ti)) { - stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); - } - } - - int vc = 0; - for (MyEntry it : allVariableNames) { - vc++; - String name = it.key.toStringNoH(it.value); - String changed = deobfuscation.deobfuscateName(name, false, usageTypes.get(it.key), deobfuscated, renameType, selected); - if (changed != null) { - boolean addNew = false; - String h = System.identityHashCode(it.key) + "_" + name; - if (stringsNoVarH.contains(h)) { - addNew = true; - } - ActionPush pu = (ActionPush) it.key.src; - if (pu.replacement == null) { - pu.replacement = new ArrayList<>(); - pu.replacement.addAll(pu.values); - } - if (pu.replacement.get(it.key.pos) instanceof ConstantIndex) { - ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.key.pos); - ConstantPool pool = it.value; - if (pool == null) { - continue; - } - if (pool.constants == null) { - continue; - } - if (addNew) { - pool.constants.add(changed); - ci.index = pool.constants.size() - 1; - } else { - pool.constants.set(ci.index, changed); - } - } else { - pu.replacement.set(it.key.pos, changed); - } - ret++; - } - } - for (ASMSource src : actionsMap.keySet()) { - actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, 0, ""/*FIXME path*/)); - src.setActions(actionsMap.get(src)); - src.setModified(); - } - deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, selected); - return ret; - } - - public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { - XFLConverter.convertSWF(handler, this, swfName, outfile, true, generator, generatorVerName, generatorVersion, parallel, version); - } - - public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { - XFLConverter.convertSWF(handler, this, swfName, outfile, false, generator, generatorVerName, generatorVersion, parallel, version); - } - - public static AffineTransform matrixToTransform(MATRIX mat) { - return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), - mat.getRotateSkew1Float(), mat.getScaleYFloat(), - mat.translateX, mat.translateY); - } - - private static Cache frameCache = Cache.getInstance(false); - - public static SerializableImage getFromCache(String key) { - if (frameCache.contains(key)) { - return (SerializableImage) SWF.frameCache.get(key); - } - return null; - } - - public static void putToCache(String key, SerializableImage img) { - if (Configuration.useFrameCache.get()) { - frameCache.put(key, img); - } - } - - public static void clearImageCache() { - frameCache.clear(); - } - - public static RECT fixRect(RECT rect) { - RECT ret = new RECT(); - ret.Xmin = rect.Xmin; - ret.Xmax = rect.Xmax; - ret.Ymin = rect.Ymin; - ret.Ymax = rect.Ymax; - - if (ret.Xmax <= 0) { - ret.Xmax = ret.getWidth(); - ret.Xmin = 0; - } - if (ret.Ymax <= 0) { - ret.Ymax = ret.getHeight(); - ret.Ymin = 0; - } - if (ret.Xmin < 0) { - ret.Xmax += (-ret.Xmin); - ret.Xmin = 0; - } - if (ret.Ymin < 0) { - ret.Ymax += (-ret.Ymin); - ret.Ymin = 0; - } - - if (ret.getWidth() < 1 || ret.getHeight() < 1) { - ret.Xmin = 0; - ret.Ymin = 0; - ret.Xmax = 20; - ret.Ymax = 20; - } - return ret; - } - - public static void getNeededCharacters(SWF swf,int id,Set usedCharacters){ - if(!swf.characters.containsKey(id)){ //maybe imported? - return; - } - CharacterTag character = swf.characters.get(id); - usedCharacters.add(id); - Set needed = character.getNeededCharacters(); - for(int n:needed){ - getNeededCharacters(swf, n, usedCharacters); - } - } - - private static void getNeededCharacters(Timeline timeline, List frames, Set usedCharacters) { - if (frames == null) { - frames = new ArrayList<>(); - for (int i = 0; i < timeline.getFrameCount(); i++) { - frames.add(i); - } - } - for (int frame = 0; frame < frames.size(); frame++) { - Frame frameObj = timeline.frames.get(frame); - for (int depth : frameObj.layers.keySet()) { - DepthState layer = frameObj.layers.get(depth); - if (!timeline.swf.characters.containsKey(layer.characterId)) { - continue; - } - getNeededCharacters(timeline.swf,layer.characterId,usedCharacters); - } - } - - } - - private static String jsArrColor(RGB rgb){ - return "["+rgb.red+","+rgb.green+","+rgb.blue+","+((rgb instanceof RGBA)?((RGBA)rgb).getAlphaFloat():1)+"]"; - } - - public static String framesToHtmlCanvas(double unitDivisor, Timeline timeline, List frames, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, ColorTransform colorTransform, Color backGroundColor) { - StringBuilder sb = new StringBuilder(); - if (frames == null) { - frames = new ArrayList<>(); - for (int i = 0; i < timeline.getFrameCount(); i++) { - frames.add(i); - } - } - sb.append("\tvar clips = [];\r\n"); - sb.append("\tvar frame_cnt = ").append(timeline.getFrameCount()).append(";\r\n"); - sb.append("\tframe = frame % frame_cnt;\r\n"); - sb.append("\tswitch(frame){\r\n"); - int maxDepth = timeline.getMaxDepth(); - Stack clipDepths=new Stack<>(); - for (int frame = 0; frame < frames.size(); frame++) { - sb.append("\t\tcase ").append(frame).append(":\r\n"); - Frame frameObj = timeline.frames.get(frame); - for (int i = 1; i <= maxDepth+1; i++) { - while(!clipDepths.isEmpty() && clipDepths.peek()<=i){ - clipDepths.pop(); - sb.append("\t\t\tvar o = clips.pop();\r\n"); - sb.append("\t\t\tctx.globalCompositeOperation = \"destination-in\";\r\n"); - sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\tctx.drawImage(o.clipCanvas,0,0);\r\n"); - sb.append("\t\t\tvar ms=o.ctx._matrices.slice();\r\n"); - sb.append("\t\t\to.ctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\to.ctx.globalCompositeOperation = \"source-over\";\r\n"); - sb.append("\t\t\to.ctx.drawImage(canvas,0,0);\r\n"); - sb.append("\t\t\to.ctx.applyTransforms(ms);\r\n"); - sb.append("\t\t\tctx = o.ctx;\r\n"); - sb.append("\t\t\tcanvas = o.canvas;\r\n"); - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.characters.containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - CharacterTag character = timeline.swf.characters.get(layer.characterId); - - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - Matrix placeMatrix = new Matrix(layer.matrix); - placeMatrix.scaleX /= unitDivisor; - placeMatrix.scaleY /= unitDivisor; - placeMatrix.rotateSkew0 /= unitDivisor; - placeMatrix.rotateSkew1 /= unitDivisor; - placeMatrix.translateX /= unitDivisor; - placeMatrix.translateY /= unitDivisor; - - - int f = 0; - String fstr = "0"; - if (character instanceof DefineSpriteTag) { - DefineSpriteTag sp = (DefineSpriteTag) character; - Timeline tim = sp.getTimeline(); - if(tim.getFrameCount()>0){ - f = layer.time % tim.getFrameCount(); - fstr = "("+f+"+time)%" + tim.getFrameCount(); - } - } - - if(layer.clipDepth!=-1){ - clipDepths.push(layer.clipDepth); - sb.append("\t\t\tclips.push({ctx:ctx,canvas:canvas});\r\n"); - sb.append("\t\t\tvar ccanvas = createCanvas(canvas.width,canvas.height);\r\n"); - sb.append("\t\t\tvar cctx = ccanvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(cctx);\r\n"); - sb.append("\t\t\tcctx.applyTransforms(ctx._matrices);\r\n"); - sb.append("\t\t\tcanvas = ccanvas;\r\n"); - sb.append("\t\t\tctx = cctx;\r\n"); - } - - if(layer.filters!=null){ - sb.append("\t\t\tvar oldctx = ctx;\r\n"); - sb.append("\t\t\tvar fcanvas = createCanvas(canvas.width,canvas.height);"); - sb.append("\t\t\tvar fctx = fcanvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(fctx);\r\n"); - sb.append("\t\t\tfctx.applyTransforms(ctx._matrices);\r\n"); - sb.append("\t\t\tctx = fctx;\r\n"); - } - - ColorTransform ctrans =layer.colorTransForm; - String ctrans_str = "ctrans"; - if(ctrans==null){ - ctrans = new ColorTransform(); - }else{ - ctrans_str = "ctrans.merge(new cxform("+ - ctrans.getRedAdd()+","+ctrans.getGreenAdd()+","+ctrans.getBlueAdd()+","+ctrans.getAlphaAdd()+","+ - ctrans.getRedMulti()+","+ctrans.getGreenMulti()+","+ctrans.getBlueMulti()+","+ctrans.getAlphaMulti() - +"))"; - } - sb.append("\t\t\tplace(\"").append(getTypePrefix(character)).append(layer.characterId).append("\",canvas,ctx,[").append(placeMatrix.scaleX).append(",") - .append(placeMatrix.rotateSkew0).append(",") - .append(placeMatrix.rotateSkew1).append(",") - .append(placeMatrix.scaleY).append(",") - .append(placeMatrix.translateX).append(",") - .append(placeMatrix.translateY).append("],").append(ctrans_str).append(",").append(""+(layer.blendMode<1?1:layer.blendMode)).append(",").append(fstr).append(",").append(layer.ratio < 0 ? 0 : layer.ratio).append(",time").append(");\r\n"); - - if(layer.filters!=null){ - for(FILTER filter:layer.filters){ - if(filter instanceof COLORMATRIXFILTER){ - COLORMATRIXFILTER cmf = (COLORMATRIXFILTER)filter; - String mat = "["; - for(int k=0;k0){ - mat+=","; - } - mat += cmf.matrix[k]; - } - mat+="]"; - sb.append("\t\t\tfcanvas = Filters.colorMatrix(fcanvas,fcanvas.getContext(\"2d\"),"+mat+");\r\n"); - } - - if(filter instanceof CONVOLUTIONFILTER){ - CONVOLUTIONFILTER cf=(CONVOLUTIONFILTER)filter; - int height = cf.matrix.length; - int width = cf.matrix[0].length; - float[] matrix2 = new float[width * height]; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - matrix2[y * width + x] = cf.matrix[x][y] * cf.divisor + cf.bias; - } - } - String mat = "["; - for(int k=0;k0){ - mat+=","; - } - mat += matrix2[k]; - } - mat+="]"; - sb.append("\t\t\tfcanvas = Filters.convolution(fcanvas,fcanvas.getContext(\"2d\"),"+mat+",false);\r\n"); - } - - if(filter instanceof GLOWFILTER){ - GLOWFILTER gf = (GLOWFILTER)filter; - sb.append("\t\t\tfcanvas = Filters.glow(fcanvas,fcanvas.getContext(\"2d\"),"+gf.blurX+","+gf.blurY+","+gf.strength+","+jsArrColor(gf.glowColor)+","+(gf.innerGlow?"true":"false")+","+(gf.knockout?"true":"false")+","+gf.passes+");\r\n"); - } - - if(filter instanceof DROPSHADOWFILTER){ - DROPSHADOWFILTER ds=(DROPSHADOWFILTER)filter; - sb.append("\t\t\tfcanvas = Filters.dropShadow(fcanvas,fcanvas.getContext(\"2d\"),"+ds.blurX+","+ds.blurY+","+(int) (ds.angle * 180 / Math.PI)+","+ds.distance+","+jsArrColor(ds.dropShadowColor)+","+(ds.innerShadow?"true":"false")+","+ds.passes+","+ds.strength+","+(ds.knockout?"true":"false")+");\r\n"); - } - if(filter instanceof BEVELFILTER){ - BEVELFILTER bv=(BEVELFILTER)filter; - String type = "Filters.INNER"; - if (bv.onTop && !bv.innerShadow) { - type = "Filters.FULL"; - } else if (!bv.innerShadow) { - type = "Filters.OUTER"; - } - sb.append("\t\t\tfcanvas = Filters.bevel(fcanvas,fcanvas.getContext(\"2d\"),"+bv.blurX+","+bv.blurY+","+bv.strength+","+type+","+jsArrColor(bv.highlightColor)+","+jsArrColor(bv.shadowColor)+","+(int) (bv.angle * 180 / Math.PI)+","+bv.distance+","+(bv.knockout?"true":"false")+","+bv.passes+");\r\n"); - } - - if(filter instanceof GRADIENTBEVELFILTER){ - GRADIENTBEVELFILTER gbf = (GRADIENTBEVELFILTER)filter; - String colArr = "["; - String ratArr = "["; - for(int k=0;k0){ - colArr+=","; - ratArr+=","; - } - colArr+=jsArrColor(gbf.gradientColors[k]); - ratArr+=gbf.gradientRatio[k] / 255f; - } - colArr+="]"; - ratArr+="]"; - String type = "Filters.INNER"; - if (gbf.onTop && !gbf.innerShadow) { - type = "Filters.FULL"; - } else if (!gbf.innerShadow) { - type = "Filters.OUTER"; - } - - sb.append("\t\t\tfcanvas = Filters.gradientBevel(fcanvas,fcanvas.getContext(\"2d\"),"+colArr+","+ratArr+","+gbf.blurX+","+gbf.blurY+","+gbf.strength+","+type+","+(int) (gbf.angle * 180 / Math.PI)+","+gbf.distance+","+(gbf.knockout?"true":"false")+","+gbf.passes+");\r\n"); - } - - if(filter instanceof GRADIENTGLOWFILTER){ - GRADIENTGLOWFILTER ggf = (GRADIENTGLOWFILTER)filter; - String colArr = "["; - String ratArr = "["; - for(int k=0;k0){ - colArr+=","; - ratArr+=","; - } - colArr+=jsArrColor(ggf.gradientColors[k]); - ratArr+=ggf.gradientRatio[k] / 255f; - } - colArr+="]"; - ratArr+="]"; - String type = "Filters.INNER"; - if (ggf.onTop && !ggf.innerShadow) { - type = "Filters.FULL"; - } else if (!ggf.innerShadow) { - type = "Filters.OUTER"; - } - - sb.append("\t\t\tfcanvas = Filters.gradientGlow(fcanvas,fcanvas.getContext(\"2d\"),"+ggf.blurX+","+ggf.blurY+","+(int) (ggf.angle * 180 / Math.PI)+","+ggf.distance+","+colArr+","+ratArr+","+type+","+ggf.passes+","+ggf.strength+","+(ggf.knockout?"true":"false")+");\r\n"); - } - } - sb.append("\t\t\tctx = oldctx;\r\n"); - sb.append("\t\t\tvar ms=ctx._matrices;\r\n"); - sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); - sb.append("\t\t\tctx.drawImage(fcanvas,0,0);\r\n"); - sb.append("\t\t\tctx.applyTransforms(ms);\r\n"); - - } - if(layer.clipDepth!=-1){ - sb.append("\t\t\tclips[clips.length-1].clipCanvas = canvas;\r\n"); - sb.append("\t\t\tcanvas = createCanvas(canvas.width,canvas.height);\r\n"); - sb.append("\t\t\tvar nctx = canvas.getContext(\"2d\");\r\n"); - sb.append("\t\t\tenhanceContext(nctx);\r\n"); - sb.append("\t\t\tnctx.applyTransforms(ctx._matrices);\r\n"); - sb.append("\t\t\tctx = nctx;\r\n"); - } - } - sb.append("\t\t\tbreak;\r\n"); - } - sb.append("\t}\r\n"); - return sb.toString(); - } - - public static void frameToSvg(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level) throws IOException { - if (timeline.frames.size() <= frame) { - return; - } - Frame frameObj = timeline.frames.get(frame); - List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); - - int maxDepth = timeline.getMaxDepth(); - for (int i = 1; i <= maxDepth; i++) { - for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - exporter.setClip(prevClips.get(c)); - prevClips.remove(c); - clips.remove(c); - } - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.characters.containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - CharacterTag character = timeline.swf.characters.get(layer.characterId); - - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - - ColorTransform clrTrans = Helper.deepCopy(colorTransform); - if (layer.colorTransForm != null && layer.blendMode <= 1) { //Normal blend mode - clrTrans = colorTransform.merge(layer.colorTransForm); - } - - if (character instanceof DrawableTag) { - DrawableTag drawable = (DrawableTag) character; - - String assetName; - Tag drawableTag = (Tag) drawable; - if (exporter.exportedTags.containsKey(drawableTag)) { - assetName = exporter.exportedTags.get(drawableTag); - } else { - assetName = getTagIdPrefix(drawableTag, exporter); - exporter.exportedTags.put(drawableTag, assetName); - exporter.createDefGroup(new ExportRectangle(drawable.getRect()), assetName); - drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1); - exporter.endGroup(); - } - RECT boundRect = drawable.getRect(); - ExportRectangle rect = new ExportRectangle(boundRect); - - // TODO: if (layer.filters != null) - // TODO: if (layer.blendMode > 1) - if (layer.clipDepth > -1) { - String clipName = exporter.getUniqueId("clipPath"); - exporter.createClipPath(new Matrix(), clipName); - SvgClip clip = new SvgClip(clipName, layer.clipDepth); - clips.add(clip); - prevClips.add(exporter.getClip()); - Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName); - exporter.setClip(clip.shape); - exporter.endGroup(); - } else { - Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); - exporter.addUse(mat, boundRect, assetName); - } - } - } - } - - private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { - if (tag instanceof ShapeTag) { - return exporter.getUniqueId("shape"); - } - if (tag instanceof MorphShapeTag) { - return exporter.getUniqueId("morphshape"); - } - if (tag instanceof DefineSpriteTag) { - return exporter.getUniqueId("sprite"); - } - if (tag instanceof TextTag) { - return exporter.getUniqueId("text"); - } - if (tag instanceof ButtonTag) { - return exporter.getUniqueId("button"); - } - return exporter.getUniqueId("tag"); - } - - public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor) { - String key = "frame_" + frame + "_" + timeline.id + "_" + timeline.swf.hashCode(); - SerializableImage image = getFromCache(key); - if (image != null) { - return image; - } - - if (timeline.frames.isEmpty()) { - return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB); - } - - RECT rect = displayRect; - image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, - (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); - if (backGroundColor == null) { - image.fillTransparent(); - } else { - Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); - g.setComposite(AlphaComposite.Src); - g.setColor(backGroundColor); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - } - Matrix m = transformation.clone(); - m.translate(-rect.Xmin, -rect.Ymin); - frameToImage(timeline, frame, time, stateUnderCursor, mouseButton, image, m, colorTransform); - putToCache(key, image); - return image; - } - - public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, DepthState stateUnderCursor, int mouseButton, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, ColorTransform colorTransform) { - RECT rect = displayRect; - for (int f = 0; f < timeline.frames.size(); f++) { - SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, - (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); - image.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - frameToImage(timeline, f, 0, stateUnderCursor, mouseButton, image, m, colorTransform); - ret.add(image); - } - } - - public static void frameToImage(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - float unzoom = (float) SWF.unitDivisor; - if (timeline.frames.size() <= frame) { - return; - } - Frame frameObj = timeline.frames.get(frame); - Graphics2D g = (Graphics2D) image.getGraphics(); - g.setPaint(frameObj.backgroundColor.toColor()); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - g.setTransform(transformation.toTransform()); - List clips = new ArrayList<>(); - List prevClips = new ArrayList<>(); - - int maxDepth = timeline.getMaxDepth(); - for (int i = 1; i <= maxDepth; i++) { - for (int c = 0; c < clips.size(); c++) { - if (clips.get(c).depth == i) { - g.setClip(prevClips.get(c)); - prevClips.remove(c); - clips.remove(c); - } - } - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!timeline.swf.characters.containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - CharacterTag character = timeline.swf.characters.get(layer.characterId); - Matrix mat = new Matrix(layer.matrix); - mat = mat.preConcatenate(transformation); - - if (colorTransform == null) { - colorTransform = new ColorTransform(); - } - - ColorTransform clrTrans = Helper.deepCopy(colorTransform); - if (layer.colorTransForm != null && layer.blendMode <= 1) { //Normal blend mode - clrTrans = colorTransform.merge(layer.colorTransForm); - } - - boolean showPlaceholder = false; - if (character instanceof DrawableTag) { - DrawableTag drawable = (DrawableTag) character; - SerializableImage img; - Matrix drawMatrix = new Matrix(); - int drawableFrameCount = drawable.getNumFrames(); - if (drawableFrameCount == 0) { - drawableFrameCount = 1; - } - int dframe = (time + layer.time) % drawableFrameCount; - if (character instanceof ButtonTag) { - ButtonTag bt = (ButtonTag) character; - dframe = ButtonTag.FRAME_UP; - if (stateUnderCursor == layer) { - if (mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - } - } - - RECT boundRect = drawable.getRect(); - ExportRectangle rect = new ExportRectangle(boundRect); - rect = mat.transform(rect); - Matrix m = mat.clone(); - if (layer.filters != null) { - // calculate size after applying the filters - double deltaXMax = 0; - double deltaYMax = 0; - for (FILTER filter : layer.filters) { - double x = filter.getDeltaX(); - double y = filter.getDeltaY(); - deltaXMax = Math.max(x, deltaXMax); - deltaYMax = Math.max(y, deltaYMax); - } - rect.xMin -= deltaXMax * SWF.unitDivisor; - rect.xMax += deltaXMax * SWF.unitDivisor; - rect.yMin -= deltaYMax * SWF.unitDivisor; - rect.yMax += deltaYMax * SWF.unitDivisor; - } - - rect.xMin = Math.max(0, rect.xMin); - rect.yMin = Math.max(0, rect.yMin); - - int newWidth = (int) (rect.getWidth() / SWF.unitDivisor); - int newHeight = (int) (rect.getHeight() / SWF.unitDivisor); - int deltaX = (int) (rect.xMin / SWF.unitDivisor); - int deltaY = (int) (rect.yMin / SWF.unitDivisor); - newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1; - newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1; - - if (newWidth <= 0 || newHeight <= 0) { - continue; - } - - img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); - img.fillTransparent(); - - m.translate(-rect.xMin, -rect.yMin); - drawMatrix.translate(rect.xMin, rect.yMin); - - drawable.toImage(dframe, layer.time + time, layer.ratio, stateUnderCursor, mouseButton, img, m, clrTrans); - //if(stateUnderCursor == layer){ - /* if(true){ - Graphics2D gg = (Graphics2D)img.getGraphics(); - gg.setStroke(new BasicStroke(3)); - gg.setPaint(Color.red); - gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - gg.draw(SHAPERECORD.twipToPixelShape(drawable.getOutline(frame, layer.ratio, stateUnderCursor, mouseButton, m))); - }*/ - - if (layer.filters != null) { - for (FILTER filter : layer.filters) { - img = filter.apply(img); - } - } - if (layer.blendMode > 1) { - if (layer.colorTransForm != null) { - img = layer.colorTransForm.apply(img); - } - } - - drawMatrix.translateX /= unzoom; - drawMatrix.translateY /= unzoom; - AffineTransform trans = drawMatrix.toTransform(); - - switch (layer.blendMode) { - case 0: - case 1: - g.setComposite(AlphaComposite.SrcOver); - break; - case 2://Layer - g.setComposite(AlphaComposite.SrcOver); - break; - case 3: - g.setComposite(BlendComposite.Multiply); - break; - case 4: - g.setComposite(BlendComposite.Screen); - break; - case 5: - g.setComposite(BlendComposite.Lighten); - break; - case 6: - g.setComposite(BlendComposite.Darken); - break; - case 7: - g.setComposite(BlendComposite.Difference); - break; - case 8: - g.setComposite(BlendComposite.Add); - break; - case 9: - g.setComposite(BlendComposite.Subtract); - break; - case 10: - g.setComposite(BlendComposite.Invert); - break; - case 11: - g.setComposite(BlendComposite.Alpha); - break; - case 12: - g.setComposite(BlendComposite.Erase); - break; - case 13: - g.setComposite(BlendComposite.Overlay); - break; - case 14: - g.setComposite(BlendComposite.HardLight); - break; - default: //Not implemented - g.setComposite(AlphaComposite.SrcOver); - break; - } - - if (layer.clipDepth > -1) { - BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); - Graphics2D gm = (Graphics2D) mask.getGraphics(); - gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - gm.setComposite(AlphaComposite.Src); - gm.setColor(new Color(0, 0, 0, 0f)); - gm.fillRect(0, 0, image.getWidth(), image.getHeight()); - gm.setTransform(trans); - gm.drawImage(img.getBufferedImage(), 0, 0, null); - Clip clip = new Clip(Helper.imageToShape(mask), layer.clipDepth); //Maybe we can get current outline instead converting from image (?) - clips.add(clip); - prevClips.add(g.getClip()); - g.setTransform(AffineTransform.getTranslateInstance(0, 0)); - g.setClip(clip.shape); - } else { - g.setTransform(trans); - g.drawImage(img.getBufferedImage(), 0, 0, null); - } - } else if (character instanceof BoundedTag) { - showPlaceholder = true; - } - - if (showPlaceholder) { - mat.translateX /= unzoom; - mat.translateY /= unzoom; - AffineTransform trans = mat.toTransform(); - g.setTransform(trans); - BoundedTag b = (BoundedTag) character; - g.setPaint(new Color(255, 255, 255, 128)); - g.setComposite(BlendComposite.Invert); - RECT r = b.getRect(); - int div = (int) unzoom; - g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15); - g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div)); - g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div); - g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div); - g.setComposite(AlphaComposite.Dst); - } - } - - g.setTransform(AffineTransform.getScaleInstance(1, 1)); - } - - public void removeTagFromTimeline(Tag toRemove, List timeline) { - int characterId = 0; - if (toRemove instanceof CharacterTag) { - characterId = ((CharacterTag) toRemove).getCharacterId(); - } - Map stage = new HashMap<>(); - - Set dependingChars = new HashSet<>(); - if (characterId != 0) { - dependingChars.add(characterId); - for (int i = 0; i < timeline.size(); i++) { - Tag t = timeline.get(i); - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - Set needed = t.getNeededCharacters(); - if (needed.contains(characterId)) { - dependingChars.add(c.getCharacterId()); - } - } - - } - } - - for (int i = 0; i < timeline.size(); i++) { - Tag t = timeline.get(i); - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - int currentCharId = stage.get(rt.getDepth()); - stage.remove(rt.getDepth()); - if (dependingChars.contains(currentCharId)) { - timeline.remove(i); - i--; - continue; - } - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - int placeCharId = po.getCharacterId(); - int placeDepth = po.getDepth(); - if (placeCharId != 0) { - stage.put(placeDepth, placeCharId); - } - int currentCharId = stage.get(placeDepth); - if (dependingChars.contains(currentCharId)) { - timeline.remove(i); - i--; - continue; - } - } - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - if (dependingChars.contains(c.getCharacterId())) { - timeline.remove(i); - i--; - continue; - } - } - Set needed = t.getNeededCharacters(); - for (int dep : dependingChars) { - if (needed.contains(dep)) { - timeline.remove(i); - i--; - continue; - } - } - if (t == toRemove) { - timeline.remove(i); - i--; - continue; - } - if (t instanceof DefineSpriteTag) { - DefineSpriteTag spr = (DefineSpriteTag) t; - removeTagFromTimeline(toRemove, spr.subTags); - } - - } - } - - public void removeTag(Tag t) { - List tags; - Timelined timelined = t.getTimelined(); - if (timelined instanceof DefineSpriteTag) { - tags = ((DefineSpriteTag) timelined).getSubTags(); - } else { - tags = this.tags; - } - if (t instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(t.getId())) { - tags.remove(t); - timelined.resetTimeline(); - } else { - removeTagFromTimeline(t, tags); - } - clearImageCache(); - } -} +/* + * Copyright (C) 2010-2014 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; + +import SevenZip.Compression.LZMA.Encoder; +import com.jpacker.JPacker; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionDeobfuscation; +import com.jpexs.decompiler.flash.action.ActionGraphSource; +import com.jpexs.decompiler.flash.action.ActionLocalData; +import com.jpexs.decompiler.flash.action.model.ConstantPool; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +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.TextExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; +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.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +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.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.timeline.Clip; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.SvgClip; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.AS2PackageNodeItem; +import com.jpexs.decompiler.flash.treeitems.AS3PackageNodeItem; +import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.treenodes.AS2PackageNode; +import com.jpexs.decompiler.flash.treenodes.ContainerNode; +import com.jpexs.decompiler.flash.treenodes.FrameNode; +import com.jpexs.decompiler.flash.treenodes.TagNode; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.BlendComposite; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.decompiler.flash.xfl.XFLConverter; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import gnu.jpdf.PDFJob; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; +import javax.imageio.ImageIO; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.xml.bind.DatatypeConverter; +import net.kroo.elliot.GifSequenceWriter; +import org.monte.media.VideoFormatKeys; +import org.monte.media.avi.AVIWriter; + +/** + * Class representing SWF file + * + * @author JPEXS + */ +public final class SWF implements TreeItem, Timelined { + + /** + * Default version of SWF file format + */ + public static final int DEFAULT_VERSION = 10; + /** + * Tags inside of file + */ + public List tags = new ArrayList<>(); + public boolean hasEndTag; + /** + * ExportRectangle for the display + */ + public RECT displayRect; + /** + * Movie frame rate + */ + public int frameRate; + /** + * Number of frames in movie + */ + public int frameCount; + /** + * Version of SWF + */ + public int version; + /** + * Size of the file + */ + public long fileSize; + /** + * Use compression + */ + public boolean compressed = false; + /** + * Use LZMA compression + */ + public boolean lzma = false; + /** + * Compressed size of the file (LZMA) + */ + public long compressedSize; + /** + * LZMA Properties + */ + public byte[] lzmaProperties; + public FileAttributesTag fileAttributes; + /** + * ScaleForm GFx + */ + public boolean gfx = false; + + public SWFList swfList; + public String file; + public String fileTitle; + public boolean readOnly; + public boolean isAS3; + public Map characters = new HashMap<>(); + public List abcList; + public JPEGTablesTag jtt; + public Map sourceFontsMap = new HashMap<>(); + public static final double unitDivisor = 20; + + private Timeline timeline; + + public void updateCharacters() { + parseCharacters(new ArrayList(tags)); + } + + private void parseCharacters(List list) { + for (ContainerItem t : list) { + if (t instanceof CharacterTag) { + characters.put(((CharacterTag) t).getCharacterId(), (CharacterTag) t); + } + if (t instanceof Container) { + parseCharacters(((Container) t).getSubItems()); + } + } + } + + /** + * Unresolve recursive sprites + */ + private void checkInvalidSprites() { + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, new ArrayList())) { + tags.set(i, new Tag(this, t.getId(), "InvalidSprite", t.getOriginalHeaderData(), t.getOriginalData(), t.getPos())); + } + } + } + } + + private boolean isSpriteValid(DefineSpriteTag sprite, List path) { + if (path.contains(sprite.spriteId)) { + return false; + } + path.add(sprite.spriteId); + for (Tag t : sprite.subTags) { + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, path)) { + return false; + } + } + } + path.remove((Integer) sprite.spriteId); + return true; + } + + @Override + public Timeline getTimeline() { + if (timeline == null) { + timeline = new Timeline(this); + } + return timeline; + } + + @Override + public void resetTimeline() { + timeline = null; + } + + /** + * Gets all tags with specified id + * + * @param tagId Identificator of tag type + * @return List of tags + */ + public List getTagData(int tagId) { + List ret = new ArrayList<>(); + for (Tag tag : tags) { + if (tag.getId() == tagId) { + ret.add(tag); + } + } + return ret; + } + + /** + * Saves this SWF into new file + * + * @param os OutputStream to save SWF in + * @throws IOException + */ + public void saveTo(OutputStream os) throws IOException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, version); + sos.writeRECT(displayRect); + sos.writeUI8(0); + sos.writeUI8(frameRate); + sos.writeUI16(frameCount); + + sos.writeTags(tags); + if (hasEndTag) { + sos.writeUI16(0); + } + sos.close(); + if (compressed && lzma) { + os.write('Z'); + } else if (compressed) { + os.write('C'); + } else { + if (gfx) { + os.write('G'); + } else { + os.write('F'); + } + } + if (gfx) { + os.write('F'); + os.write('X'); + } else { + os.write('W'); + os.write('S'); + } + os.write(version); + byte[] data = baos.toByteArray(); + sos = new SWFOutputStream(os, version); + sos.writeUI32(data.length + 8); + + if (compressed) { + if (lzma) { + Encoder enc = new Encoder(); + int val = lzmaProperties[0] & 0xFF; + int lc = val % 9; + int remainder = val / 9; + int lp = remainder % 5; + int pb = remainder / 5; + int dictionarySize = 0; + for (int i = 0; i < 4; i++) { + dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); + } + enc.SetDictionarySize(dictionarySize); + enc.SetLcLpPb(lc, lp, pb); + baos = new ByteArrayOutputStream(); + enc.SetEndMarkerMode(true); + enc.Code(new ByteArrayInputStream(data), baos, -1, -1, null); + data = baos.toByteArray(); + byte[] udata = new byte[4]; + udata[0] = (byte) (data.length & 0xFF); + udata[1] = (byte) ((data.length >> 8) & 0xFF); + udata[2] = (byte) ((data.length >> 16) & 0xFF); + udata[3] = (byte) ((data.length >> 24) & 0xFF); + os.write(udata); + os.write(lzmaProperties); + } else { + os = new DeflaterOutputStream(os); + } + } + os.write(data); + } finally { + if (os != null) { + os.close(); + } + } + } + + public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { + this(is, null, parallelRead); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param listener + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { + this(is, listener, parallelRead, false); + } + + /** + * Faster constructor to check SWF only + * + * @param is + * @throws java.io.IOException + */ + public SWF(InputStream is) throws IOException { + byte[] hdr = new byte[3]; + is.read(hdr); + String shdr = new String(hdr, Utf8Helper.charset); + if (!Arrays.asList( + "FWS", //Uncompressed Flash + "CWS", //ZLib compressed Flash + "ZWS", //LZMA compressed Flash + "GFX", //Uncompressed ScaleForm GFx + "CFX" //Compressed ScaleForm GFx + ).contains(shdr)) { + throw new IOException("Invalid SWF file"); + } + version = is.read(); + fileSize = (is.read() + (is.read() << 8) + (is.read() << 16) + (is.read() << 24)) & 0xffffffff; + + if (hdr[0] == 'C') { + is = new InflaterInputStream(is); + } + + if (hdr[0] == 'Z') { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + //outSize + is.read(); + is.read(); + is.read(); + is.read(); + int propertiesSize = 5; + lzmaProperties = new byte[propertiesSize]; + if (is.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { + throw new IOException("LZMA:input .lzma file is too short"); + } + long dictionarySize = 0; + for (int i = 0; i < 4; i++) { + dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); + if (dictionarySize > Runtime.getRuntime().freeMemory()) { + throw new IOException("LZMA: Too large dictionary size"); + } + } + + SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); + if (!decoder.SetDecoderProperties(lzmaProperties)) { + throw new IOException("LZMA:Incorrect stream properties"); + } + + if (!decoder.Code(is, baos, fileSize - 8)) { + throw new IOException("LZMA:Error in data stream"); + } + } else { + long toRead = fileSize - 8; + if (toRead > 0) { + byte[] bytes = new byte[4096]; + while (toRead > 4096) { + int read = is.read(bytes); + if (read == -1) { + throw new IOException("Invalid SWF file"); + } + toRead -= read; + } + while (toRead > 0) { + int read = is.read(bytes, 0, (int) toRead); + if (read == -1) { + throw new IOException("Invalid SWF file"); + } + toRead -= read; + } + } + } + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param listener + * @param parallelRead Use parallel threads? + * @param checkOnly Check only file validity + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, ProgressListener listener, boolean parallelRead, boolean checkOnly) throws IOException, InterruptedException { + byte[] hdr = new byte[3]; + is.read(hdr); + String shdr = new String(hdr, Utf8Helper.charset); + if (!Arrays.asList( + "FWS", //Uncompressed Flash + "CWS", //ZLib compressed Flash + "ZWS", //LZMA compressed Flash + "GFX", //Uncompressed ScaleForm GFx + "CFX" //Compressed ScaleForm GFx + ).contains(shdr)) { + throw new IOException("Invalid SWF file"); + } + version = is.read(); + SWFInputStream sis = new SWFInputStream(is, version, 4); + fileSize = sis.readUI32(); + + if (hdr[1] == 'F' && hdr[2] == 'X') { + gfx = true; + } + if (hdr[0] == 'C') { + byte[] uncompressedData = Helper.readStream(new InflaterInputStream(is)); + sis = new SWFInputStream(new ByteArrayInputStream(uncompressedData), version, 8); + compressed = true; + } + + if (hdr[0] == 'Z') { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + sis.readUI32(); //outSize + int propertiesSize = 5; + lzmaProperties = new byte[propertiesSize]; + if (sis.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { + throw new IOException("LZMA:input .lzma file is too short"); + } + long dictionarySize = 0; + for (int i = 0; i < 4; i++) { + dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); + if (dictionarySize > Runtime.getRuntime().freeMemory()) { + throw new IOException("LZMA: Too large dictionary size"); + } + } + + SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); + if (!decoder.SetDecoderProperties(lzmaProperties)) { + throw new IOException("LZMA:Incorrect stream properties"); + } + + if (!decoder.Code(sis, baos, fileSize - 8)) { + throw new IOException("LZMA:Error in data stream"); + } + sis = new SWFInputStream(new ByteArrayInputStream(baos.toByteArray()), version, 8); + compressed = true; + lzma = true; + } + + if (listener != null) { + sis.addPercentListener(listener); + } + sis.setPercentMax(fileSize); + displayRect = sis.readRECT(); + // FIXED8 (16 bit fixed point) frameRate + sis.readUI8(); //tmpFirstByetOfFrameRate + frameRate = sis.readUI8(); + frameCount = sis.readUI16(); + List tags = sis.readTagList(this, this, 0, parallelRead, true, !checkOnly, gfx); + if (tags.get(tags.size() - 1).getId() == EndTag.ID) { + hasEndTag = true; + tags.remove(tags.size() - 1); + } + this.tags = tags; + if (!checkOnly) { + checkInvalidSprites(); + updateCharacters(); + assignExportNamesToSymbols(); + assignClassesToSymbols(); + findFileAttributes(); + findABCTags(); + } else { + boolean hasNonUnknownTag = false; + for (Tag tag : tags) { + if (tag.getOriginalData().length > 0 && Tag.getRequiredTags().contains(tag.getId())) { + hasNonUnknownTag = true; + } + } + if (!hasNonUnknownTag) { + throw new IOException("Invalid SWF file. No known tag found."); + } + } + } + + @Override + public SWF getSwf() { + return this; + } + + /** + * Get title of the file + * + * @return file title + */ + public String getFileTitle() { + if (fileTitle != null) { + return fileTitle; + } + return file; + } + + public String getShortFileName() { + String title = getFileTitle(); + if (title == null) { + return ""; + } + return new File(title).getName(); + } + + private void findABCTags() { + List objs = new ArrayList<>(); + objs.addAll(tags); + + ArrayList newAbcList = new ArrayList<>(); + getABCTags(objs, newAbcList); + this.abcList = newAbcList; + } + + private static void getABCTags(List list, List actionScripts) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getABCTags(((Container) t).getSubItems(), actionScripts); + } + if (t instanceof ABCContainerTag) { + actionScripts.add((ABCContainerTag) t); + } + } + } + + private void findFileAttributes() { + for (Tag t : tags) { + if (t instanceof FileAttributesTag) { + fileAttributes = (FileAttributesTag) t; + break; + } + } + } + + private void assignExportNamesToSymbols() { + HashMap exportNames = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof ExportAssetsTag) { + ExportAssetsTag eat = (ExportAssetsTag) t; + for (int i = 0; i < eat.tags.size(); i++) { + if ((!exportNames.containsKey(eat.tags.get(i))) && (!exportNames.containsValue(eat.names.get(i)))) { + exportNames.put(eat.tags.get(i), eat.names.get(i)); + } + } + } + } + for (Tag t : tags) { + if (t instanceof CharacterIdTag) { + CharacterIdTag ct = (CharacterIdTag) t; + if (exportNames.containsKey(ct.getCharacterId())) { + ct.setExportName(exportNames.get(ct.getCharacterId())); + } + } + } + } + + public void assignClassesToSymbols() { + HashMap classes = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sct = (SymbolClassTag) t; + for (int i = 0; i < sct.tags.length; i++) { + if ((!classes.containsKey(sct.tags[i])) && (!classes.containsValue(sct.names[i]))) { + classes.put(sct.tags[i], sct.names[i]); + } + } + } + } + for (Tag t : tags) { + if (t instanceof CharacterIdTag) { + CharacterIdTag ct = (CharacterIdTag) t; + if (classes.containsKey(ct.getCharacterId())) { + ct.setClassName(classes.get(ct.getCharacterId())); + } + } + } + } + + /** + * Compress SWF file + * + * @param fis Input stream + * @param fos Output stream + * @return True on success + */ + public static boolean fws2cws(InputStream fis, OutputStream fos) { + try { + byte[] swfHead = new byte[8]; + fis.read(swfHead); + + if (swfHead[0] != 'F') { + fis.close(); + return false; + } + swfHead[0] = 'C'; + fos.write(swfHead); + fos = new DeflaterOutputStream(fos); + int i; + while ((i = fis.read()) != -1) { + fos.write(i); + } + + fis.close(); + fos.close(); + } catch (IOException ex) { + return false; + } + return true; + } + + public static boolean decompress(InputStream fis, OutputStream fos) { + try { + byte[] hdr = new byte[3]; + fis.read(hdr); + String shdr = new String(hdr, Utf8Helper.charset); + switch (shdr) { + case "CWS": { + int version = fis.read(); + SWFInputStream sis = new SWFInputStream(fis, version, 4); + long fileSize = sis.readUI32(); + SWFOutputStream sos = new SWFOutputStream(fos, version); + sos.write(Utf8Helper.getBytes("FWS")); + sos.writeUI8(version); + sos.writeUI32(fileSize); + InflaterInputStream iis = new InflaterInputStream(fis); + int i; + try { + while ((i = iis.read()) != -1) { + fos.write(i); + } + } catch (EOFException ex) { + } + fis.close(); + fos.close(); + break; + } + case "ZWS": { + int version = fis.read(); + SWFInputStream sis = new SWFInputStream(fis, version, 4); + long fileSize = sis.readUI32(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + sis.readUI32(); + int propertiesSize = 5; + byte[] lzmaProperties = new byte[propertiesSize]; + if (sis.read(lzmaProperties, 0, propertiesSize) != propertiesSize) { + throw new IOException("LZMA:input .lzma file is too short"); + } + SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder(); + if (!decoder.SetDecoderProperties(lzmaProperties)) { + throw new IOException("LZMA:Incorrect stream properties"); + } + if (!decoder.Code(sis, baos, fileSize - 8)) { + throw new IOException("LZMA:Error in data stream"); + } + try (SWFOutputStream sos = new SWFOutputStream(fos, version)) { + sos.write(Utf8Helper.getBytes("FWS")); + sos.write(version); + sos.writeUI32(fileSize); + sos.write(baos.toByteArray()); + } + fis.close(); + fos.close(); + break; + } + default: + return false; + } + } catch (IOException ex) { + return false; + } + return true; + } + + public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { + try { + SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); + int cnt = swf.deobfuscateIdentifiers(renameType); + swf.assignClassesToSymbols(); + System.out.println(cnt + " identifiers renamed."); + swf.saveTo(fos); + } catch (InterruptedException ex) { + return false; + } catch (IOException ex) { + return false; + } + return true; + } + + public boolean exportAS3Class(String className, String outdir, ScriptExportMode exportMode, boolean parallel) throws Exception { + List abcTags = new ArrayList<>(); + + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + ABCContainerTag cnt = (ABCContainerTag) t; + abcTags.add(cnt); + } + } + for (int i = 0; i < abcTags.size(); i++) { + ABC abc = abcTags.get(i).getABC(); + ScriptPack scr = abc.findScriptPackByPath(className); + if (scr != null) { + String cnt = ""; + if (abc.script_info.size() > 1) { + cnt = "script " + (i + 1) + "/" + abc.script_info.size() + " "; + } + String exStr = "Exporting " + "tag " + (i + 1) + "/" + abcTags.size() + " " + cnt + scr.getPath() + " ..."; + informListeners("exporting", exStr); + scr.export(outdir, abcTags, exportMode, parallel); + exStr = "Exported " + "tag " + (i + 1) + "/" + abcTags.size() + " " + cnt + scr.getPath() + " ..."; + informListeners("exported", exStr); + return true; + } + } + return false; + } + + private List> uniqueAS3Packs(List> packs) { + List> ret = new ArrayList<>(); + for (MyEntry item : packs) { + for (MyEntry itemOld : ret) { + if (item.key.equals(itemOld.key)) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Duplicate pack path found (" + itemOld.key + ")!"); + break; + } + } + ret.add(item); + } + return ret; + } + + public List> getAS3Packs() { + List abcTags = new ArrayList<>(); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + abcTags.add((ABCContainerTag) t); + } + } + List> packs = new ArrayList<>(); + for (int i = 0; i < abcTags.size(); i++) { + ABCContainerTag t = abcTags.get(i); + packs.addAll(t.getABC().getScriptPacks()); + } + return uniqueAS3Packs(packs); + } + + @Override + public RECT getRect() { + return displayRect; + } + + private class ExportPackTask implements Callable { + + ScriptPack pack; + String directory; + List abcList; + ScriptExportMode exportMode; + ClassPath path; + AtomicInteger index; + int count; + boolean parallel; + AbortRetryIgnoreHandler handler; + long startTime; + long stopTime; + + public ExportPackTask(AbortRetryIgnoreHandler handler, AtomicInteger index, int count, ClassPath path, ScriptPack pack, String directory, List abcList, ScriptExportMode exportMode, boolean parallel) { + this.pack = pack; + this.directory = directory; + this.abcList = abcList; + this.exportMode = exportMode; + this.path = path; + this.index = index; + this.count = count; + this.parallel = parallel; + this.handler = handler; + } + + @Override + public File call() throws Exception { + RunnableIOExResult rio = new RunnableIOExResult() { + @Override + public void run() throws IOException { + startTime = System.currentTimeMillis(); + this.result = pack.export(directory, abcList, exportMode, parallel); + stopTime = System.currentTimeMillis(); + } + }; + int currentIndex = index.getAndIncrement(); + synchronized (ABC.class) { + long time = stopTime - startTime; + informListeners("exporting", "Exporting script " + currentIndex + "/" + count + " " + path); + } + new RetryTask(rio, handler).run(); + synchronized (ABC.class) { + long time = stopTime - startTime; + informListeners("exported", "Exported script " + currentIndex + "/" + count + " " + path + ", " + Helper.formatTimeSec(time)); + } + return rio.result; + } + } + + public List exportActionScript2(AbortRetryIgnoreHandler handler, String outdir, ScriptExportMode exportMode, boolean parallel, EventListener evl) throws IOException { + List ret = new ArrayList<>(); + List list2 = new ArrayList<>(); + list2.addAll(tags); + List list = createASTagList(list2, null); + + TagNode.setExport(list, true); + if (!outdir.endsWith(File.separator)) { + outdir += File.separator; + } + outdir += "scripts" + File.separator; + ret.addAll(TagNode.exportNodeAS(handler, list, outdir, exportMode, evl)); + return ret; + } + + public List exportActionScript3(final AbortRetryIgnoreHandler handler, final String outdir, final ScriptExportMode exportMode, final boolean parallel) { + final AtomicInteger cnt = new AtomicInteger(1); + final List abcTags = new ArrayList<>(); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + abcTags.add((ABCContainerTag) t); + } + } + + final List ret = new ArrayList<>(); + final List> packs = getAS3Packs(); + + if (!parallel || packs.size() < 2) { + try { + CancellableWorker.call(new Callable() { + @Override + public Void call() throws Exception { + for (MyEntry item : packs) { + ExportPackTask task = new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel); + ret.add(task.call()); + } + return null; + } + }, Configuration.exportTimeout.get(), TimeUnit.SECONDS); + } catch (TimeoutException ex) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached", ex); + } catch (Exception ex) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); + } + } else { + ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); + List> futureResults = new ArrayList<>(); + for (MyEntry item : packs) { + Future future = executor.submit(new ExportPackTask(handler, cnt, packs.size(), item.key, item.value, outdir, abcTags, exportMode, parallel)); + futureResults.add(future); + } + + try { + executor.shutdown(); + if (!executor.awaitTermination(Configuration.exportTimeout.get(), TimeUnit.SECONDS)) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, Helper.formatTimeToText(Configuration.exportTimeout.get()) + " ActionScript export limit reached"); + } + } catch (InterruptedException ex) { + } finally { + executor.shutdownNow(); + } + + for (int f = 0; f < futureResults.size(); f++) { + try { + if (futureResults.get(f).isDone()) { + ret.add(futureResults.get(f).get()); + } + } catch (InterruptedException ex) { + } catch (ExecutionException ex) { + Logger.getLogger(SWF.class.getName()).log(Level.SEVERE, "Error during ABC export", ex); + } + } + } + + return ret; + } + + public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportMode exportMode, boolean parallel) throws Exception { + boolean asV3Found = false; + List ret = new ArrayList<>(); + final EventListener evl = new EventListener() { + @Override + public void handleEvent(String event, Object data) { + if (event.equals("exporting") || event.equals("exported")) { + informListeners(event, data); + } + } + }; + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + asV3Found = true; + } + } + + if (asV3Found) { + ret.addAll(exportActionScript3(handler, outdir, exportMode, parallel)); + } else { + ret.addAll(exportActionScript2(handler, outdir, exportMode, parallel, evl)); + } + return ret; + } + + public static List createASTagList(List list, Tag parent) { + List ret = new ArrayList<>(); + int frame = 1; + List frames = new ArrayList<>(); + + List exportAssetsTags = new ArrayList<>(); + for (ContainerItem t : list) { + if (t instanceof ExportAssetsTag) { + exportAssetsTags.add((ExportAssetsTag) t); + } + TreeNode addNode = null; + if (t instanceof ShowFrameTag) { + // do not add PlaceObjects (+etc) to script nodes + FrameNode tti = new FrameNode(new FrameNodeItem(t.getSwf(), frame, parent, false), null, true); + + for (int r = ret.size() - 1; r >= 0; r--) { + if (!(ret.get(r).getItem() instanceof DefineSpriteTag)) { + if (!(ret.get(r).getItem() instanceof DefineButtonTag)) { + if (!(ret.get(r).getItem() instanceof DefineButton2Tag)) { + if (!(ret.get(r).getItem() instanceof DoInitActionTag)) { + if (!(ret.get(r).getItem() instanceof AS2PackageNodeItem)) { + tti.subNodes.add(ret.get(r)); + ret.remove(r); + } + } + } + } + } + } + frame++; + frames.add(tti); + } else if (t instanceof ASMSource) { + ContainerNode tti = new ContainerNode(t); + //ret.add(tti); + addNode = tti; + } else if (t instanceof Container) { + if (((Container) t).getItemCount() > 0) { + + ContainerNode tti = new ContainerNode(t); + List subItems = ((Container) t).getSubItems(); + + Tag tag = t instanceof Tag ? (Tag) t : null; + tti.subNodes = createASTagList(subItems, tag); + addNode = tti; + //ret.add(tti); + } + } + if (addNode != null) { + if (addNode.getItem() instanceof CharacterIdTag) { + CharacterIdTag cit = (CharacterIdTag) addNode.getItem(); + String path = cit.getExportName(); + if (path == null) { + path = ""; + } + String[] pathParts; + if (path.contains(".")) { + pathParts = path.split("\\."); + } else { + pathParts = new String[]{path}; + } + List items = ret; + int pos = 0; + TreeNode selNode = null; + do { + if (pos == pathParts.length - 1) { + break; + } + selNode = null; + for (TreeNode node : items) { + if (node.getItem() instanceof AS2PackageNodeItem) { + AS2PackageNodeItem pkg = (AS2PackageNodeItem) node.getItem(); + if (pkg.packageName.equals(pathParts[pos])) { + selNode = node; + break; + } + } + } + int pkgCount = 0; + for (; pkgCount < items.size(); pkgCount++) { + if (items.get(pkgCount).getItem() instanceof AS3PackageNodeItem) { + AS2PackageNodeItem pkg = (AS2PackageNodeItem) items.get(pkgCount).getItem(); + if (pkg.packageName.compareTo(pathParts[pos]) > 0) { + break; + } + } else { + break; + } + } + if (selNode == null) { + items.add(pkgCount, selNode = new AS2PackageNode(new AS2PackageNodeItem(pathParts[pos], t.getSwf()))); + } + pos++; + if (selNode != null) { + items = selNode.subNodes; + } + + } while (selNode != null); + + int clsCount = 0; + for (; clsCount < items.size(); clsCount++) { + if (items.get(clsCount).getItem() instanceof CharacterIdTag) { + CharacterIdTag ct = (CharacterIdTag) items.get(clsCount).getItem(); + String expName = ct.getExportName(); + if (expName == null) { + expName = ""; + } + if (expName.contains(".")) { + expName = expName.substring(expName.lastIndexOf('.') + 1); + } + if ((ct.getClass().getName() + "_" + expName).compareTo(addNode.getItem().getClass().getName() + "_" + pathParts[pos]) > 0) { + break; + } + } + } + items.add(clsCount, addNode); + } else { + ret.add(addNode); + } + } + + } + ret.addAll(frames); + for (int i = ret.size() - 1; i >= 0; i--) { + if (ret.get(i).getItem() instanceof DefineSpriteTag) { + ((DefineSpriteTag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; + } + if (ret.get(i).getItem() instanceof DefineButtonTag) { + ((DefineButtonTag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; + } + if (ret.get(i).getItem() instanceof DefineButton2Tag) { + ((DefineButton2Tag) ret.get(i).getItem()).exportAssetsTags = exportAssetsTags; + } + /*if (ret.get(i).tag instanceof DoInitActionTag) { + //((DoInitActionTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags; + }*/ + if (ret.get(i).getItem() instanceof ASMSource) { + ASMSource ass = (ASMSource) ret.get(i).getItem(); + if (ass.containsSource()) { + continue; + } + } + if (ret.get(i).subNodes.isEmpty()) { + ret.remove(i); + } + } + return ret; + } + + public static void getTagsFromTreeNodes(List treeNodes, List result) { + for (TreeNode treeNode : treeNodes) { + TreeItem treeItem = treeNode.getItem(); + if (treeItem instanceof Tag) { + result.add((Tag) treeItem); + } + getTagsFromTreeNodes(treeNode.subNodes, result); + } + } + + private final HashSet listeners = new HashSet<>(); + + public final void addEventListener(EventListener listener) { + listeners.add(listener); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).addEventListener(listener); + } + } + } + + public final void removeEventListener(EventListener listener) { + listeners.remove(listener); + for (Tag t : tags) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).removeEventListener(listener); + } + } + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public static boolean hasErrorHeader(byte[] data) { + if (data.length > 4) { + if ((data[0] & 0xff) == 0xff) { + if ((data[1] & 0xff) == 0xd9) { + if ((data[2] & 0xff) == 0xff) { + if ((data[3] & 0xff) == 0xd8) { + return true; + } + } + } + } + } + return false; + } + + public static void populateVideoFrames(int streamId, List tags, HashMap output) { + for (ContainerItem t : tags) { + if (t instanceof VideoFrameTag) { + output.put(((VideoFrameTag) t).frameNum, (VideoFrameTag) t); + } + if (t instanceof Container) { + populateVideoFrames(streamId, ((Container) t).getSubItems(), output); + } + } + } + + public void exportMovies(AbortRetryIgnoreHandler handler, String outdir, MovieExportSettings settings) throws IOException { + new MovieExporter().exportMovies(handler, outdir, tags, settings); + } + + public void exportSounds(AbortRetryIgnoreHandler handler, String outdir, SoundExportSettings settings) throws IOException { + new SoundExporter().exportSounds(handler, outdir, tags, settings); + } + + public void exportFonts(AbortRetryIgnoreHandler handler, String outdir, FontExportSettings settings) throws IOException { + new FontExporter().exportFonts(handler, outdir, tags, settings); + } + + private static void writeLE(OutputStream os, long val, int size) throws IOException { + for (int i = 0; i < size; i++) { + os.write((int) (val & 0xff)); + val >>= 8; + } + } + + public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { + ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); + int audioFormat = 1; //PCM + writeLE(subChunk1Data, audioFormat, 2); + int numChannels = soundType ? 2 : 1; + writeLE(subChunk1Data, numChannels, 2); + int[] rateMap = {5512, 11025, 22050, 44100}; + int sampleRate = soundRateHz;//rateMap[soundRate]; + writeLE(subChunk1Data, sampleRate, 4); + int bitsPerSample = soundSize ? 16 : 8; + int byteRate = sampleRate * numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, byteRate, 4); + int blockAlign = numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, blockAlign, 2); + writeLE(subChunk1Data, bitsPerSample, 2); + + ByteArrayOutputStream chunks = new ByteArrayOutputStream(); + chunks.write(Utf8Helper.getBytes("fmt ")); + byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); + writeLE(chunks, subChunk1DataBytes.length, 4); + chunks.write(subChunk1DataBytes); + + chunks.write(Utf8Helper.getBytes("data")); + writeLE(chunks, data.length, 4); + chunks.write(data); + + fos.write(Utf8Helper.getBytes("RIFF")); + byte[] chunkBytes = chunks.toByteArray(); + writeLE(fos, 4 + chunkBytes.length, 4); + fos.write(Utf8Helper.getBytes("WAVE")); + fos.write(chunkBytes); + } + + private static void makeAVI(List images, int frameRate, File file) throws IOException { + if (images.isEmpty()) { + return; + } + AVIWriter out = new AVIWriter(file); + out.addVideoTrack(VideoFormatKeys.ENCODING_AVI_PNG, 1, frameRate, images.get(0).getWidth(), images.get(0).getHeight(), 0, 0); + try { + for (BufferedImage img : images) { + out.write(0, img, 1); + } + } finally { + out.close(); + } + + } + + private static void makeGIF(List images, int frameRate, File file) throws IOException { + if (images.isEmpty()) { + return; + } + try (ImageOutputStream output = new FileImageOutputStream(file)) { + GifSequenceWriter writer = new GifSequenceWriter(output, images.get(0).getType(), 1000 / frameRate, true); + + for (BufferedImage img : images) { + writer.writeToSequence(img); + } + + writer.close(); + } + } + + private static String getTypePrefix(CharacterTag c) { + if (c instanceof ShapeTag) { + return "shape"; + } + if (c instanceof MorphShapeTag) { + return "morphshape"; + } + if (c instanceof DefineSpriteTag) { + return "sprite"; + } + if (c instanceof TextTag) { + return "text"; + } + if (c instanceof ButtonTag) { + return "button"; + } + if (c instanceof FontTag) { + return "font"; + } + if (c instanceof ImageTag) { + return "image"; + } + return "character"; + } + + public static void writeLibrary(SWF fswf, Set library, OutputStream fos) throws IOException { + for (int c : library) { + CharacterTag ch = fswf.characters.get(c); + if (ch instanceof FontTag) { + fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ch,textColor){\r\n")); + fos.write(Utf8Helper.getBytes(((FontTag) ch).toHtmlCanvas(1))); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + } else if (ch instanceof ImageTag) { + ImageTag image = (ImageTag) ch; + String format = image.getImageFormat(); + InputStream imageStream = image.getImageData(); + byte[] imageData; + if (imageStream != null) { + imageData = Helper.readStream(image.getImageData()); + } else { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ImageIO.write(image.getImage().getBufferedImage(), format.toUpperCase(Locale.ENGLISH), baos); + } catch (IOException ex) { + } + imageData = baos.toByteArray(); + } + String base64ImgData = DatatypeConverter.printBase64Binary(imageData); + fos.write(Utf8Helper.getBytes("var image" + c + " = document.createElement(\"img\");\r\nimage" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); + } else { + fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); + if (ch instanceof DrawableTag) { + fos.write(Utf8Helper.getBytes(((DrawableTag) ch).toHtmlCanvas(1))); + } + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + } + } + } + + public List exportFrames(AbortRetryIgnoreHandler handler, String outdir, int containerId, List frames, final FramesExportSettings settings) throws IOException { + final List ret = new ArrayList<>(); + if (tags.isEmpty()) { + return ret; + } + Timeline tim = null; + String path = ""; + if (containerId == 0) { + tim = getTimeline(); + } else { + tim = ((Timelined) characters.get(containerId)).getTimeline(); + path = File.separator + Helper.makeFileName(characters.get(containerId).getExportFileName()); + } + if (frames == null) { + int frameCnt = tim.frames.size(); + frames = new ArrayList<>(); + for (int i = 0; i < frameCnt; i++) { + frames.add(i); + } + } + + final File foutdir = new File(outdir + path); + if (!foutdir.exists()) { + if (!foutdir.mkdirs()) { + if (!foutdir.exists()) { + throw new IOException("Cannot create directory " + outdir); + } + } + } + + final List fframes = frames; + + Color backgroundColor = null; + if (settings.mode == FramesExportMode.AVI) { + for (Tag t : tags) { + if (t instanceof SetBackgroundColorTag) { + SetBackgroundColorTag sb = (SetBackgroundColorTag) t; + backgroundColor = sb.backgroundColor.toColor(); + } + } + } + + if (settings.mode == FramesExportMode.SVG) { + for (int i = 0; i < frames.size(); i++) { + final int fi = i; + final Timeline ftim = tim; + final Color fbackgroundColor = backgroundColor; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + int frame = fframes.get(fi); + File f = new File(foutdir + File.separator + frame + ".svg"); + try (FileOutputStream fos = new FileOutputStream(f)) { + SVGExporter exporter = new SVGExporter(new ExportRectangle(ftim.displayRect)); + if (fbackgroundColor != null) { + exporter.setBackGroundColor(fbackgroundColor); + } + frameToSvg(ftim, frame, 0, null, 0, exporter, new ColorTransform(), 0); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + ret.add(f); + } + }, handler).run(); + } + return ret; + } + + if (settings.mode == FramesExportMode.CANVAS) { + final Timeline ftim = tim; + final Color fbackgroundColor = backgroundColor; + final SWF fswf = this; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File fcanvas = new File(foutdir + File.separator + "canvas.js"); + Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); + ret.add(fcanvas); + + File f = new File(foutdir + File.separator + "frames.js"); + File fmin = new File(foutdir + File.separator + "frames.min.js"); + int width = (int) (ftim.displayRect.getWidth() / SWF.unitDivisor); + int height = (int) (ftim.displayRect.getHeight() / SWF.unitDivisor); + try (FileOutputStream fos = new FileOutputStream(f)) { + fos.write(Utf8Helper.getBytes("\r\n")); + Set library = new HashSet<>(); + getNeededCharacters(ftim, fframes, library); + + writeLibrary(fswf, library, fos); + + String currentName = ftim.id == 0 ? "main" : getTypePrefix(fswf.characters.get(ftim.id)) + ftim.id; + + fos.write(Utf8Helper.getBytes("function " + currentName + "(ctx,ctrans,frame,ratio,time){\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.transform(1,0,0,1," + (-ftim.displayRect.Xmin / unitDivisor) + "," + (-ftim.displayRect.Ymin / unitDivisor) + ");\r\n")); + fos.write(Utf8Helper.getBytes(framesToHtmlCanvas(unitDivisor, ftim, fframes, 0, null, 0, ftim.displayRect, new ColorTransform(), fbackgroundColor))); + fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + + fos.write(Utf8Helper.getBytes("var frame = -1;\r\n")); + fos.write(Utf8Helper.getBytes("var time = 0;\r\n")); + fos.write(Utf8Helper.getBytes("var frames = [];\r\n")); + for (int i : fframes) { + fos.write(Utf8Helper.getBytes("frames.push(" + i + ");\r\n")); + } + fos.write(Utf8Helper.getBytes("\r\n")); + RGB backgroundColor = new RGB(255, 255, 255); + for (Tag t : fswf.tags) { + if (t instanceof SetBackgroundColorTag) { + SetBackgroundColorTag sb = (SetBackgroundColorTag) t; + backgroundColor = sb.backgroundColor; + } + } + + fos.write(Utf8Helper.getBytes("var backgroundColor = \"" + backgroundColor.toHexRGB() + "\";\r\n")); + fos.write(Utf8Helper.getBytes("var originalWidth = " + width + ";\r\n")); + fos.write(Utf8Helper.getBytes("var originalHeight= " + height + ";\r\n")); + fos.write(Utf8Helper.getBytes("function nextFrame(ctx,ctrans){\r\n")); + fos.write(Utf8Helper.getBytes("\tvar oldframe = frame;\r\n")); + fos.write(Utf8Helper.getBytes("\tframe = (frame+1)%frames.length;\r\n")); + fos.write(Utf8Helper.getBytes("\tif(frame==oldframe){time++;}else{time=0;};\r\n")); + fos.write(Utf8Helper.getBytes("\tdrawFrame();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + + fos.write(Utf8Helper.getBytes("function drawFrame(){\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.fillStyle = backgroundColor;\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.fillRect(0,0,canvas.width,canvas.height);\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.save();\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n")); + fos.write(Utf8Helper.getBytes("\t" + currentName + "(ctx,ctrans,frames[frame],0,time);\r\n")); + fos.write(Utf8Helper.getBytes("\tctx.restore();\r\n")); + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + fos.write(Utf8Helper.getBytes("window.setInterval(function(){nextFrame(ctx,ctrans);}," + (int) (1000.0 / ftim.swf.frameRate) + ");\r\n")); + } + + if (Configuration.packJavaScripts.get()) { + try { + JPacker.main(new String[]{"-q", "-b", "62", "-o", fmin.getAbsolutePath(), f.getAbsolutePath()}); + f.delete(); + } catch (Exception | Error e) { //Something wrong in the packer + Logger.getLogger(SWF.class.getName()).log(Level.WARNING, "JPacker: Cannot minimize script"); + f.renameTo(fmin); + } + } else { + f.renameTo(fmin); + } + + File fh = new File(foutdir + File.separator + "frames.html"); + try (FileOutputStream fos = new FileOutputStream(fh); FileInputStream fis = new FileInputStream(fmin)) { + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlPrefix(width, height))); + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsPrefix())); + byte buf[] = new byte[1000]; + int cnt; + while ((cnt = fis.read(buf)) > 0) { + fos.write(buf, 0, cnt); + } + if (Configuration.packJavaScripts.get()) { + fos.write(Utf8Helper.getBytes(";")); + } + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getJsSuffix())); + fos.write(Utf8Helper.getBytes(CanvasShapeExporter.getHtmlSuffix())); + } + fmin.delete(); + + ret.add(f); + } + }, handler).run(); + + return ret; + } + + final List frameImages = new ArrayList<>(); + for (int frame : frames) { + frameImages.add(frameToImageGet(tim, frame, 0, null, 0, tim.displayRect, new Matrix(), new ColorTransform(), backgroundColor).getBufferedImage()); + } + switch (settings.mode) { + case GIF: + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File f = new File(foutdir + File.separator + "frames.gif"); + makeGIF(frameImages, frameRate, f); + ret.add(f); + } + }, handler).run(); + break; + case PNG: + for (int i = 0; i < frameImages.size(); i++) { + final int fi = i; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File f = new File(foutdir + File.separator + fframes.get(fi) + ".png"); + ImageIO.write(frameImages.get(fi), "PNG", f); + ret.add(f); + } + }, handler).run(); + } + break; + case PDF: + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File f = new File(foutdir + File.separator + "frames.pdf"); + PDFJob job = new PDFJob(new FileOutputStream(f)); + PageFormat pf = new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + + p.setSize(frameImages.get(0).getWidth() + 10, frameImages.get(0).getHeight() + 10); + pf.setPaper(p); + + for (int i = 0; i < frameImages.size(); i++) { + BufferedImage img = frameImages.get(i); + Graphics g = job.getGraphics(pf); + g.drawImage(img, 5, 5, img.getWidth(), img.getHeight(), null); + g.dispose(); + } + + job.end(); + ret.add(f); + } + }, handler).run(); + break; + case AVI: + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File f = new File(foutdir + File.separator + "frames.avi"); + makeAVI(frameImages, frameRate, f); + ret.add(f); + } + }, handler).run(); + break; + } + + return ret; + } + + public void exportTexts(AbortRetryIgnoreHandler handler, String outdir, TextExportSettings settings) throws IOException { + new TextExporter().exportTexts(handler, outdir, tags, settings); + } + + public void exportImages(AbortRetryIgnoreHandler handler, String outdir, ImageExportSettings settings) throws IOException { + new ImageExporter().exportImages(handler, outdir, tags, settings); + } + + public void exportShapes(AbortRetryIgnoreHandler handler, String outdir, ShapeExportSettings settings) throws IOException { + new ShapeExporter().exportShapes(handler, outdir, tags, settings); + } + + public void exportMorphShapes(AbortRetryIgnoreHandler handler, String outdir, MorphShapeExportSettings settings) throws IOException { + new MorphShapeExporter().exportMorphShapes(handler, outdir, tags, settings); + } + + public void exportBinaryData(AbortRetryIgnoreHandler handler, String outdir, BinaryDataExportSettings settings) throws IOException { + new BinaryDataExporter().exportBinaryData(handler, outdir, tags, settings); + } + + private final HashMap deobfuscated = new HashMap<>(); + private List> allVariableNames = new ArrayList<>(); + private List allFunctions = new ArrayList<>(); + private HashMap allStrings = new HashMap<>(); + private final HashMap usageTypes = new HashMap<>(); + private final ActionDeobfuscation deobfuscation = new ActionDeobfuscation(); + + private static void getVariables(ConstantPool constantPool, BaseLocalData localData, Stack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { + boolean debugMode = false; + while ((ip > -1) && ip < code.size()) { + if (visited.contains(ip)) { + break; + } + GraphSourceItem ins = code.get(ip); + + if (debugMode) { + System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), code.version, ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); + } + if (ins.isExit()) { + break; + } + if (ins.isIgnored()) { + ip++; + continue; + } + + String usageType = "name"; + GraphTargetItem name = null; + if ((ins instanceof ActionGetVariable) + || (ins instanceof ActionGetMember) + || (ins instanceof ActionDefineLocal2) + || (ins instanceof ActionNewMethod) + || (ins instanceof ActionNewObject) + || (ins instanceof ActionCallMethod) + || (ins instanceof ActionCallFunction)) { + if (stack.isEmpty()) { + break; + } + name = stack.peek(); + } + + if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { + usageType = "variable"; + } + if (ins instanceof ActionGetMember) { + usageType = "member"; + } + if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { + usageType = "class"; + } + if (ins instanceof ActionCallMethod) { + usageType = "function"; //can there be method? + } + if (ins instanceof ActionCallFunction) { + usageType = "function"; + } + + if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { + functions.add(ins); + } + + if (ins instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; + List cntSizes = cnt.getContainerSizes(); + long addr = code.pos2adr(ip + 1); + ip = code.adr2pos(addr); + String cntName = cnt.getName(); + for (Long size : cntSizes) { + if (size == 0) { + continue; + } + ip = code.adr2pos(addr); + addr += size; + int nextip = code.adr2pos(addr); + getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap(), new HashMap(), new HashMap()), 0, path + (cntName == null ? "" : "/" + cntName)); + ip = nextip; + } + List> r = new ArrayList<>(); + r.add(new ArrayList()); + r.add(new ArrayList()); + r.add(new ArrayList()); + try { + ((GraphSourceItemContainer) ins).translateContainer(r, stack, output, new HashMap(), new HashMap(), new HashMap()); + } catch (EmptyStackException ex) { + } + //ip++; + continue; + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { + if (stack.size() < 2) { + break; + } + name = stack.get(stack.size() - 2); + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { + usageType = "variable"; + } + + if (ins instanceof ActionSetMember) { + usageType = "member"; + } + + if (name instanceof DirectValueActionItem) { + variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); + usageTypes.put((DirectValueActionItem) name, usageType); + } + + //for..in return + if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { + stack.push(new DirectValueActionItem(null, 0, new Null(), new ArrayList())); + } + + if (ins instanceof ActionConstantPool) { + constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + + try { + ins.translate(localData, stack, output, staticOperation, path); + } catch (EmptyStackException ex) { + // probably obfucated code, never executed branch + break; + } + if (ins.isExit()) { + break; + } + + if (ins instanceof ActionPush) { + if (!stack.isEmpty()) { + GraphTargetItem top = stack.peek(); + if (top instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) top; + if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { + if (constantPool == null) { + constantPool = new ConstantPool(dvt.constants); + } + strings.put(dvt, constantPool); + } + } + } + } + + if (ins.isBranch() || ins.isJump()) { + if (ins instanceof ActionIf) { + if (stack.isEmpty()) { + break; + } + stack.pop(); + } + visited.add(ip); + List branches = ins.getBranches(code); + for (int b : branches) { + @SuppressWarnings("unchecked") + Stack brStack = (Stack) stack.clone(); + if (b >= 0) { + getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); + } else { + if (debugMode) { + System.out.println("Negative branch:" + b); + } + } + } + // } + break; + } + ip++; + }; + } + + private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageType, ActionGraphSource code, int addr, String path) throws InterruptedException { + ActionLocalData localData = new ActionLocalData(); + getVariables(null, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList(), usageType, path); + } + + private List> getVariables(List> variables, List functions, HashMap strings, HashMap usageType, ASMSource src, String path) throws InterruptedException { + List> ret = new ArrayList<>(); + List actions = src.getActions(); + actionsMap.put(src, actions); + getVariables(variables, functions, strings, usageType, new ActionGraphSource(actions, version, new HashMap(), new HashMap(), new HashMap()), 0, path); + return ret; + } + private HashMap> actionsMap = new HashMap<>(); + + private void getVariables(List objs, String path) throws InterruptedException { + List processed = new ArrayList<>(); + for (ContainerItem o : objs) { + if (o instanceof ASMSource) { + String infPath = path + "/" + o.toString(); + int pos = 1; + String infPath2 = infPath; + while (processed.contains(infPath2)) { + pos++; + infPath2 = infPath + "[" + pos + "]"; + } + processed.add(infPath2); + informListeners("getVariables", infPath2); + getVariables(allVariableNames, allFunctions, allStrings, usageTypes, (ASMSource) o, path); + } + if (o instanceof Container) { + getVariables(((Container) o).getSubItems(), path + "/" + o.toString()); + } + } + } + + public int deobfuscateAS3Identifiers(RenameType renameType) { + for (Tag tag : tags) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); + tag.setModified(true); + } + } + for (Tag tag : tags) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); + tag.setModified(true); + } + } + for (Tag tag : tags) { + if (tag instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) tag; + for (int i = 0; i < sc.names.length; i++) { + String newname = deobfuscation.deobfuscateNameWithPackage(sc.names[i], deobfuscated, renameType, deobfuscated); + if (newname != null) { + sc.names[i] = newname; + } + } + sc.setModified(true); + } + } + deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, new HashMap()); + return deobfuscated.size(); + } + + public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { + findFileAttributes(); + if (fileAttributes == null) { + int cnt = 0; + cnt += deobfuscateAS2Identifiers(renameType); + cnt += deobfuscateAS3Identifiers(renameType); + return cnt; + } else { + if (fileAttributes.actionScript3) { + return deobfuscateAS3Identifiers(renameType); + } else { + return deobfuscateAS2Identifiers(renameType); + } + } + } + + public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { + Map selected = new HashMap<>(); + selected.put(identifier, newname); + renameAS2Identifiers(null, selected); + } + + private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { + return renameAS2Identifiers(renameType, null); + } + + private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { + actionsMap = new HashMap<>(); + allFunctions = new ArrayList<>(); + allVariableNames = new ArrayList<>(); + allStrings = new HashMap<>(); + + List objs = new ArrayList<>(); + int ret = 0; + objs.addAll(tags); + getVariables(objs, ""); + informListeners("rename", ""); + int fc = 0; + for (MyEntry it : allVariableNames) { + String name = it.key.toStringNoH(it.value); + deobfuscation.allVariableNamesStr.add(name); + } + + informListeners("rename", "classes"); + int classCount = 0; + for (Tag t : tags) { + if (t instanceof DoInitActionTag) { + classCount++; + } + } + int cnt = 0; + for (Tag t : tags) { + if (t instanceof DoInitActionTag) { + cnt++; + informListeners("rename", "class " + cnt + "/" + classCount); + DoInitActionTag dia = (DoInitActionTag) t; + String exportName = dia.getExportName(); + final String pkgPrefix = "__Packages."; + String[] classNameParts = null; + if ((exportName != null) && exportName.startsWith(pkgPrefix)) { + String className = exportName.substring(pkgPrefix.length()); + if (className.contains(".")) { + classNameParts = className.split("\\."); + } else { + classNameParts = new String[]{className}; + } + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + List dec; + try { + dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); + } catch (EmptyStackException ex) { + continue; + } + GraphTargetItem name = null; + for (GraphTargetItem it : dec) { + if (it instanceof ClassActionItem) { + ClassActionItem cti = (ClassActionItem) it; + List methods = new ArrayList<>(); + methods.addAll(cti.functions); + methods.addAll(cti.staticFunctions); + + for (GraphTargetItem gti : methods) { + if (gti instanceof FunctionActionItem) { + FunctionActionItem fun = (FunctionActionItem) gti; + if (fun.calculatedFunctionName instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; + String fname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(fname, false, "method", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(fname, changed); + } + } + } + } + + List vars = new ArrayList<>(); + for (MyEntry item : cti.vars) { + vars.add(item.key); + } + for (MyEntry item : cti.staticVars) { + vars.add(item.key); + } + for (GraphTargetItem gti : vars) { + if (gti instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) gti; + String vname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(vname, false, "attribute", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(vname, changed); + } + } + } + + name = cti.className; + break; + } + if (it instanceof InterfaceActionItem) { + InterfaceActionItem ift = (InterfaceActionItem) it; + name = ift.name; + } + } + + if (name != null) { + int pos = 0; + while (name instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) name; + GraphTargetItem memberName = mem.memberName; + if (memberName instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) memberName; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(nameStr, changedNameStr); + pos++; + } + name = mem.object; + } + if (name instanceof GetVariableActionItem) { + GetVariableActionItem var = (GetVariableActionItem) name; + if (var.name instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) var.name; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(nameStr, changedNameStr); + pos++; + } + } + } + t.setModified(true); + } + } + + for (GraphSourceItem fun : allFunctions) { + fc++; + informListeners("rename", "function " + fc + "/" + allFunctions.size()); + if (fun instanceof ActionDefineFunction) { + ActionDefineFunction f = (ActionDefineFunction) fun; + if (f.functionName.isEmpty()) { //anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + if (fun instanceof ActionDefineFunction2) { + ActionDefineFunction2 f = (ActionDefineFunction2) fun; + if (f.functionName.isEmpty()) { //anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + } + + HashSet stringsNoVarH = new HashSet<>(); + List allVariableNamesDv = new ArrayList<>(); + for (MyEntry it : allVariableNames) { + allVariableNamesDv.add(it.key); + } + for (DirectValueActionItem ti : allStrings.keySet()) { + if (!allVariableNamesDv.contains(ti)) { + stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); + } + } + + int vc = 0; + for (MyEntry it : allVariableNames) { + vc++; + String name = it.key.toStringNoH(it.value); + String changed = deobfuscation.deobfuscateName(name, false, usageTypes.get(it.key), deobfuscated, renameType, selected); + if (changed != null) { + boolean addNew = false; + String h = System.identityHashCode(it.key) + "_" + name; + if (stringsNoVarH.contains(h)) { + addNew = true; + } + ActionPush pu = (ActionPush) it.key.src; + if (pu.replacement == null) { + pu.replacement = new ArrayList<>(); + pu.replacement.addAll(pu.values); + } + if (pu.replacement.get(it.key.pos) instanceof ConstantIndex) { + ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.key.pos); + ConstantPool pool = it.value; + if (pool == null) { + continue; + } + if (pool.constants == null) { + continue; + } + if (addNew) { + pool.constants.add(changed); + ci.index = pool.constants.size() - 1; + } else { + pool.constants.set(ci.index, changed); + } + } else { + pu.replacement.set(it.key.pos, changed); + } + ret++; + } + } + for (ASMSource src : actionsMap.keySet()) { + actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, 0, ""/*FIXME path*/)); + src.setActions(actionsMap.get(src)); + src.setModified(); + } + deobfuscation.deobfuscateInstanceNames(deobfuscated, renameType, tags, selected); + return ret; + } + + public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { + XFLConverter.convertSWF(handler, this, swfName, outfile, true, generator, generatorVerName, generatorVersion, parallel, version); + } + + public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException { + XFLConverter.convertSWF(handler, this, swfName, outfile, false, generator, generatorVerName, generatorVersion, parallel, version); + } + + public static AffineTransform matrixToTransform(MATRIX mat) { + return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), + mat.getRotateSkew1Float(), mat.getScaleYFloat(), + mat.translateX, mat.translateY); + } + + private static Cache frameCache = Cache.getInstance(false); + + public static SerializableImage getFromCache(String key) { + if (frameCache.contains(key)) { + return (SerializableImage) SWF.frameCache.get(key); + } + return null; + } + + public static void putToCache(String key, SerializableImage img) { + if (Configuration.useFrameCache.get()) { + frameCache.put(key, img); + } + } + + public static void clearImageCache() { + frameCache.clear(); + } + + public static RECT fixRect(RECT rect) { + RECT ret = new RECT(); + ret.Xmin = rect.Xmin; + ret.Xmax = rect.Xmax; + ret.Ymin = rect.Ymin; + ret.Ymax = rect.Ymax; + + if (ret.Xmax <= 0) { + ret.Xmax = ret.getWidth(); + ret.Xmin = 0; + } + if (ret.Ymax <= 0) { + ret.Ymax = ret.getHeight(); + ret.Ymin = 0; + } + if (ret.Xmin < 0) { + ret.Xmax += (-ret.Xmin); + ret.Xmin = 0; + } + if (ret.Ymin < 0) { + ret.Ymax += (-ret.Ymin); + ret.Ymin = 0; + } + + if (ret.getWidth() < 1 || ret.getHeight() < 1) { + ret.Xmin = 0; + ret.Ymin = 0; + ret.Xmax = 20; + ret.Ymax = 20; + } + return ret; + } + + private static void getNeededCharacters(Timeline timeline, List frames, Set usedCharacters) { + if (frames == null) { + frames = new ArrayList<>(); + for (int i = 0; i < timeline.getFrameCount(); i++) { + frames.add(i); + } + } + for (int frame = 0; frame < frames.size(); frame++) { + Frame frameObj = timeline.frames.get(frame); + for (int depth : frameObj.layers.keySet()) { + DepthState layer = frameObj.layers.get(depth); + if (layer.characterId != -1) { + if (!timeline.swf.characters.containsKey(layer.characterId)) { + continue; + } + usedCharacters.add(layer.characterId); + timeline.swf.characters.get(layer.characterId).getNeededCharactersDeep(usedCharacters); + } + } + } + + } + + private static String jsArrColor(RGB rgb) { + return "[" + rgb.red + "," + rgb.green + "," + rgb.blue + "," + ((rgb instanceof RGBA) ? ((RGBA) rgb).getAlphaFloat() : 1) + "]"; + } + + public static String framesToHtmlCanvas(double unitDivisor, Timeline timeline, List frames, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, ColorTransform colorTransform, Color backGroundColor) { + StringBuilder sb = new StringBuilder(); + if (frames == null) { + frames = new ArrayList<>(); + for (int i = 0; i < timeline.getFrameCount(); i++) { + frames.add(i); + } + } + sb.append("\tvar clips = [];\r\n"); + sb.append("\tvar frame_cnt = ").append(timeline.getFrameCount()).append(";\r\n"); + sb.append("\tframe = frame % frame_cnt;\r\n"); + sb.append("\tswitch(frame){\r\n"); + int maxDepth = timeline.getMaxDepth(); + Stack clipDepths = new Stack<>(); + for (int frame = 0; frame < frames.size(); frame++) { + sb.append("\t\tcase ").append(frame).append(":\r\n"); + Frame frameObj = timeline.frames.get(frame); + for (int i = 1; i <= maxDepth + 1; i++) { + while (!clipDepths.isEmpty() && clipDepths.peek() <= i) { + clipDepths.pop(); + sb.append("\t\t\tvar o = clips.pop();\r\n"); + sb.append("\t\t\tctx.globalCompositeOperation = \"destination-in\";\r\n"); + sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\tctx.drawImage(o.clipCanvas,0,0);\r\n"); + sb.append("\t\t\tvar ms=o.ctx._matrices.slice();\r\n"); + sb.append("\t\t\to.ctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\to.ctx.globalCompositeOperation = \"source-over\";\r\n"); + sb.append("\t\t\to.ctx.drawImage(canvas,0,0);\r\n"); + sb.append("\t\t\to.ctx.applyTransforms(ms);\r\n"); + sb.append("\t\t\tctx = o.ctx;\r\n"); + sb.append("\t\t\tcanvas = o.canvas;\r\n"); + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.characters.containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + CharacterTag character = timeline.swf.characters.get(layer.characterId); + + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + Matrix placeMatrix = new Matrix(layer.matrix); + placeMatrix.scaleX /= unitDivisor; + placeMatrix.scaleY /= unitDivisor; + placeMatrix.rotateSkew0 /= unitDivisor; + placeMatrix.rotateSkew1 /= unitDivisor; + placeMatrix.translateX /= unitDivisor; + placeMatrix.translateY /= unitDivisor; + + int f = 0; + String fstr = "0"; + if (character instanceof DefineSpriteTag) { + DefineSpriteTag sp = (DefineSpriteTag) character; + Timeline tim = sp.getTimeline(); + if (tim.getFrameCount() > 0) { + f = layer.time % tim.getFrameCount(); + fstr = "(" + f + "+time)%" + tim.getFrameCount(); + } + } + + if (layer.clipDepth != -1) { + clipDepths.push(layer.clipDepth); + sb.append("\t\t\tclips.push({ctx:ctx,canvas:canvas});\r\n"); + sb.append("\t\t\tvar ccanvas = createCanvas(canvas.width,canvas.height);\r\n"); + sb.append("\t\t\tvar cctx = ccanvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(cctx);\r\n"); + sb.append("\t\t\tcctx.applyTransforms(ctx._matrices);\r\n"); + sb.append("\t\t\tcanvas = ccanvas;\r\n"); + sb.append("\t\t\tctx = cctx;\r\n"); + } + + if (layer.filters != null) { + sb.append("\t\t\tvar oldctx = ctx;\r\n"); + sb.append("\t\t\tvar fcanvas = createCanvas(canvas.width,canvas.height);"); + sb.append("\t\t\tvar fctx = fcanvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(fctx);\r\n"); + sb.append("\t\t\tfctx.applyTransforms(ctx._matrices);\r\n"); + sb.append("\t\t\tctx = fctx;\r\n"); + } + + ColorTransform ctrans = layer.colorTransForm; + String ctrans_str = "ctrans"; + if (ctrans == null) { + ctrans = new ColorTransform(); + } else { + ctrans_str = "ctrans.merge(new cxform(" + + ctrans.getRedAdd() + "," + ctrans.getGreenAdd() + "," + ctrans.getBlueAdd() + "," + ctrans.getAlphaAdd() + "," + + ctrans.getRedMulti() + "," + ctrans.getGreenMulti() + "," + ctrans.getBlueMulti() + "," + ctrans.getAlphaMulti() + + "))"; + } + sb.append("\t\t\tplace(\"").append(getTypePrefix(character)).append(layer.characterId).append("\",canvas,ctx,[").append(placeMatrix.scaleX).append(",") + .append(placeMatrix.rotateSkew0).append(",") + .append(placeMatrix.rotateSkew1).append(",") + .append(placeMatrix.scaleY).append(",") + .append(placeMatrix.translateX).append(",") + .append(placeMatrix.translateY).append("],").append(ctrans_str).append(",").append("").append(layer.blendMode < 1 ? 1 : layer.blendMode).append(",").append(fstr).append(",").append(layer.ratio < 0 ? 0 : layer.ratio).append(",time").append(");\r\n"); + + if (layer.filters != null) { + for (FILTER filter : layer.filters) { + if (filter instanceof COLORMATRIXFILTER) { + COLORMATRIXFILTER cmf = (COLORMATRIXFILTER) filter; + String mat = "["; + for (int k = 0; k < cmf.matrix.length; k++) { + if (k > 0) { + mat += ","; + } + mat += cmf.matrix[k]; + } + mat += "]"; + sb.append("\t\t\tfcanvas = Filters.colorMatrix(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(");\r\n"); + } + + if (filter instanceof CONVOLUTIONFILTER) { + CONVOLUTIONFILTER cf = (CONVOLUTIONFILTER) filter; + int height = cf.matrix.length; + int width = cf.matrix[0].length; + float[] matrix2 = new float[width * height]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + matrix2[y * width + x] = cf.matrix[x][y] * cf.divisor + cf.bias; + } + } + String mat = "["; + for (int k = 0; k < matrix2.length; k++) { + if (k > 0) { + mat += ","; + } + mat += matrix2[k]; + } + mat += "]"; + sb.append("\t\t\tfcanvas = Filters.convolution(fcanvas,fcanvas.getContext(\"2d\"),").append(mat).append(",false);\r\n"); + } + + if (filter instanceof GLOWFILTER) { + GLOWFILTER gf = (GLOWFILTER) filter; + sb.append("\t\t\tfcanvas = Filters.glow(fcanvas,fcanvas.getContext(\"2d\"),").append(gf.blurX).append(",").append(gf.blurY).append(",").append(gf.strength).append(",").append(jsArrColor(gf.glowColor)).append(",").append(gf.innerGlow ? "true" : "false").append(",").append(gf.knockout ? "true" : "false").append(",").append(gf.passes).append(");\r\n"); + } + + if (filter instanceof DROPSHADOWFILTER) { + DROPSHADOWFILTER ds = (DROPSHADOWFILTER) filter; + sb.append("\t\t\tfcanvas = Filters.dropShadow(fcanvas,fcanvas.getContext(\"2d\"),").append(ds.blurX).append(",").append(ds.blurY).append(",").append((int) (ds.angle * 180 / Math.PI)).append(",").append(ds.distance).append(",").append(jsArrColor(ds.dropShadowColor)).append(",").append(ds.innerShadow ? "true" : "false").append(",").append(ds.passes).append(",").append(ds.strength).append(",").append(ds.knockout ? "true" : "false").append(");\r\n"); + } + if (filter instanceof BEVELFILTER) { + BEVELFILTER bv = (BEVELFILTER) filter; + String type = "Filters.INNER"; + if (bv.onTop && !bv.innerShadow) { + type = "Filters.FULL"; + } else if (!bv.innerShadow) { + type = "Filters.OUTER"; + } + sb.append("\t\t\tfcanvas = Filters.bevel(fcanvas,fcanvas.getContext(\"2d\"),").append(bv.blurX).append(",").append(bv.blurY).append(",").append(bv.strength).append(",").append(type).append(",").append(jsArrColor(bv.highlightColor)).append(",").append(jsArrColor(bv.shadowColor)).append(",").append((int) (bv.angle * 180 / Math.PI)).append(",").append(bv.distance).append(",").append(bv.knockout ? "true" : "false").append(",").append(bv.passes).append(");\r\n"); + } + + if (filter instanceof GRADIENTBEVELFILTER) { + GRADIENTBEVELFILTER gbf = (GRADIENTBEVELFILTER) filter; + String colArr = "["; + String ratArr = "["; + for (int k = 0; k < gbf.gradientColors.length; k++) { + if (k > 0) { + colArr += ","; + ratArr += ","; + } + colArr += jsArrColor(gbf.gradientColors[k]); + ratArr += gbf.gradientRatio[k] / 255f; + } + colArr += "]"; + ratArr += "]"; + String type = "Filters.INNER"; + if (gbf.onTop && !gbf.innerShadow) { + type = "Filters.FULL"; + } else if (!gbf.innerShadow) { + type = "Filters.OUTER"; + } + + sb.append("\t\t\tfcanvas = Filters.gradientBevel(fcanvas,fcanvas.getContext(\"2d\"),").append(colArr).append(",").append(ratArr).append(",").append(gbf.blurX).append(",").append(gbf.blurY).append(",").append(gbf.strength).append(",").append(type).append(",").append((int) (gbf.angle * 180 / Math.PI)).append(",").append(gbf.distance).append(",").append(gbf.knockout ? "true" : "false").append(",").append(gbf.passes).append(");\r\n"); + } + + if (filter instanceof GRADIENTGLOWFILTER) { + GRADIENTGLOWFILTER ggf = (GRADIENTGLOWFILTER) filter; + String colArr = "["; + String ratArr = "["; + for (int k = 0; k < ggf.gradientColors.length; k++) { + if (k > 0) { + colArr += ","; + ratArr += ","; + } + colArr += jsArrColor(ggf.gradientColors[k]); + ratArr += ggf.gradientRatio[k] / 255f; + } + colArr += "]"; + ratArr += "]"; + String type = "Filters.INNER"; + if (ggf.onTop && !ggf.innerShadow) { + type = "Filters.FULL"; + } else if (!ggf.innerShadow) { + type = "Filters.OUTER"; + } + + sb.append("\t\t\tfcanvas = Filters.gradientGlow(fcanvas,fcanvas.getContext(\"2d\"),").append(ggf.blurX).append(",").append(ggf.blurY).append(",").append((int) (ggf.angle * 180 / Math.PI)).append(",").append(ggf.distance).append(",").append(colArr).append(",").append(ratArr).append(",").append(type).append(",").append(ggf.passes).append(",").append(ggf.strength).append(",").append(ggf.knockout ? "true" : "false").append(");\r\n"); + } + } + sb.append("\t\t\tctx = oldctx;\r\n"); + sb.append("\t\t\tvar ms=ctx._matrices;\r\n"); + sb.append("\t\t\tctx.setTransform(1,0,0,1,0,0);\r\n"); + sb.append("\t\t\tctx.drawImage(fcanvas,0,0);\r\n"); + sb.append("\t\t\tctx.applyTransforms(ms);\r\n"); + + } + if (layer.clipDepth != -1) { + sb.append("\t\t\tclips[clips.length-1].clipCanvas = canvas;\r\n"); + sb.append("\t\t\tcanvas = createCanvas(canvas.width,canvas.height);\r\n"); + sb.append("\t\t\tvar nctx = canvas.getContext(\"2d\");\r\n"); + sb.append("\t\t\tenhanceContext(nctx);\r\n"); + sb.append("\t\t\tnctx.applyTransforms(ctx._matrices);\r\n"); + sb.append("\t\t\tctx = nctx;\r\n"); + } + } + sb.append("\t\t\tbreak;\r\n"); + } + sb.append("\t}\r\n"); + return sb.toString(); + } + + public static void frameToSvg(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level) throws IOException { + if (timeline.frames.size() <= frame) { + return; + } + Frame frameObj = timeline.frames.get(frame); + List clips = new ArrayList<>(); + List prevClips = new ArrayList<>(); + + int maxDepth = timeline.getMaxDepth(); + for (int i = 1; i <= maxDepth; i++) { + for (int c = 0; c < clips.size(); c++) { + if (clips.get(c).depth == i) { + exporter.setClip(prevClips.get(c)); + prevClips.remove(c); + clips.remove(c); + } + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.characters.containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + CharacterTag character = timeline.swf.characters.get(layer.characterId); + + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + + ColorTransform clrTrans = Helper.deepCopy(colorTransform); + if (layer.colorTransForm != null && layer.blendMode <= 1) { //Normal blend mode + clrTrans = colorTransform.merge(layer.colorTransForm); + } + + if (character instanceof DrawableTag) { + DrawableTag drawable = (DrawableTag) character; + + String assetName; + Tag drawableTag = (Tag) drawable; + if (exporter.exportedTags.containsKey(drawableTag)) { + assetName = exporter.exportedTags.get(drawableTag); + } else { + assetName = getTagIdPrefix(drawableTag, exporter); + exporter.exportedTags.put(drawableTag, assetName); + exporter.createDefGroup(new ExportRectangle(drawable.getRect()), assetName); + drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1); + exporter.endGroup(); + } + RECT boundRect = drawable.getRect(); + ExportRectangle rect = new ExportRectangle(boundRect); + + // TODO: if (layer.filters != null) + // TODO: if (layer.blendMode > 1) + if (layer.clipDepth > -1) { + String clipName = exporter.getUniqueId("clipPath"); + exporter.createClipPath(new Matrix(), clipName); + SvgClip clip = new SvgClip(clipName, layer.clipDepth); + clips.add(clip); + prevClips.add(exporter.getClip()); + Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); + exporter.addUse(mat, boundRect, assetName); + exporter.setClip(clip.shape); + exporter.endGroup(); + } else { + Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix)); + exporter.addUse(mat, boundRect, assetName); + } + } + } + } + + private static String getTagIdPrefix(Tag tag, SVGExporter exporter) { + if (tag instanceof ShapeTag) { + return exporter.getUniqueId("shape"); + } + if (tag instanceof MorphShapeTag) { + return exporter.getUniqueId("morphshape"); + } + if (tag instanceof DefineSpriteTag) { + return exporter.getUniqueId("sprite"); + } + if (tag instanceof TextTag) { + return exporter.getUniqueId("text"); + } + if (tag instanceof ButtonTag) { + return exporter.getUniqueId("button"); + } + return exporter.getUniqueId("tag"); + } + + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor) { + String key = "frame_" + frame + "_" + timeline.id + "_" + timeline.swf.hashCode(); + SerializableImage image = getFromCache(key); + if (image != null) { + return image; + } + + if (timeline.frames.isEmpty()) { + return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB); + } + + RECT rect = displayRect; + image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, + (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + if (backGroundColor == null) { + image.fillTransparent(); + } else { + Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); + g.setComposite(AlphaComposite.Src); + g.setColor(backGroundColor); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + } + Matrix m = transformation.clone(); + m.translate(-rect.Xmin, -rect.Ymin); + frameToImage(timeline, frame, time, stateUnderCursor, mouseButton, image, m, colorTransform); + putToCache(key, image); + return image; + } + + public static void framesToImage(Timeline timeline, List ret, int startFrame, int stopFrame, DepthState stateUnderCursor, int mouseButton, RECT displayRect, int totalFrameCount, Stack visited, Matrix transformation, ColorTransform colorTransform) { + RECT rect = displayRect; + for (int f = 0; f < timeline.frames.size(); f++) { + SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1, + (int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + image.fillTransparent(); + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + frameToImage(timeline, f, 0, stateUnderCursor, mouseButton, image, m, colorTransform); + ret.add(image); + } + } + + public static void frameToImage(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + float unzoom = (float) SWF.unitDivisor; + if (timeline.frames.size() <= frame) { + return; + } + Frame frameObj = timeline.frames.get(frame); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.setPaint(frameObj.backgroundColor.toColor()); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + g.setTransform(transformation.toTransform()); + List clips = new ArrayList<>(); + List prevClips = new ArrayList<>(); + + int maxDepth = timeline.getMaxDepth(); + for (int i = 1; i <= maxDepth; i++) { + for (int c = 0; c < clips.size(); c++) { + if (clips.get(c).depth == i) { + g.setClip(prevClips.get(c)); + prevClips.remove(c); + clips.remove(c); + } + } + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!timeline.swf.characters.containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + CharacterTag character = timeline.swf.characters.get(layer.characterId); + Matrix mat = new Matrix(layer.matrix); + mat = mat.preConcatenate(transformation); + + if (colorTransform == null) { + colorTransform = new ColorTransform(); + } + + ColorTransform clrTrans = Helper.deepCopy(colorTransform); + if (layer.colorTransForm != null && layer.blendMode <= 1) { //Normal blend mode + clrTrans = colorTransform.merge(layer.colorTransForm); + } + + boolean showPlaceholder = false; + if (character instanceof DrawableTag) { + DrawableTag drawable = (DrawableTag) character; + SerializableImage img; + Matrix drawMatrix = new Matrix(); + int drawableFrameCount = drawable.getNumFrames(); + if (drawableFrameCount == 0) { + drawableFrameCount = 1; + } + int dframe = (time + layer.time) % drawableFrameCount; + if (character instanceof ButtonTag) { + ButtonTag bt = (ButtonTag) character; + dframe = ButtonTag.FRAME_UP; + if (stateUnderCursor == layer) { + if (mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + } + } + + RECT boundRect = drawable.getRect(); + ExportRectangle rect = new ExportRectangle(boundRect); + rect = mat.transform(rect); + Matrix m = mat.clone(); + if (layer.filters != null) { + // calculate size after applying the filters + double deltaXMax = 0; + double deltaYMax = 0; + for (FILTER filter : layer.filters) { + double x = filter.getDeltaX(); + double y = filter.getDeltaY(); + deltaXMax = Math.max(x, deltaXMax); + deltaYMax = Math.max(y, deltaYMax); + } + rect.xMin -= deltaXMax * SWF.unitDivisor; + rect.xMax += deltaXMax * SWF.unitDivisor; + rect.yMin -= deltaYMax * SWF.unitDivisor; + rect.yMax += deltaYMax * SWF.unitDivisor; + } + + rect.xMin = Math.max(0, rect.xMin); + rect.yMin = Math.max(0, rect.yMin); + + int newWidth = (int) (rect.getWidth() / SWF.unitDivisor); + int newHeight = (int) (rect.getHeight() / SWF.unitDivisor); + int deltaX = (int) (rect.xMin / SWF.unitDivisor); + int deltaY = (int) (rect.yMin / SWF.unitDivisor); + newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1; + newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1; + + if (newWidth <= 0 || newHeight <= 0) { + continue; + } + + img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); + img.fillTransparent(); + + m.translate(-rect.xMin, -rect.yMin); + drawMatrix.translate(rect.xMin, rect.yMin); + + drawable.toImage(dframe, layer.time + time, layer.ratio, stateUnderCursor, mouseButton, img, m, clrTrans); + //if(stateUnderCursor == layer){ + /* if(true){ + Graphics2D gg = (Graphics2D)img.getGraphics(); + gg.setStroke(new BasicStroke(3)); + gg.setPaint(Color.red); + gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); + gg.draw(SHAPERECORD.twipToPixelShape(drawable.getOutline(frame, layer.ratio, stateUnderCursor, mouseButton, m))); + }*/ + + if (layer.filters != null) { + for (FILTER filter : layer.filters) { + img = filter.apply(img); + } + } + if (layer.blendMode > 1) { + if (layer.colorTransForm != null) { + img = layer.colorTransForm.apply(img); + } + } + + drawMatrix.translateX /= unzoom; + drawMatrix.translateY /= unzoom; + AffineTransform trans = drawMatrix.toTransform(); + + switch (layer.blendMode) { + case 0: + case 1: + g.setComposite(AlphaComposite.SrcOver); + break; + case 2://Layer + g.setComposite(AlphaComposite.SrcOver); + break; + case 3: + g.setComposite(BlendComposite.Multiply); + break; + case 4: + g.setComposite(BlendComposite.Screen); + break; + case 5: + g.setComposite(BlendComposite.Lighten); + break; + case 6: + g.setComposite(BlendComposite.Darken); + break; + case 7: + g.setComposite(BlendComposite.Difference); + break; + case 8: + g.setComposite(BlendComposite.Add); + break; + case 9: + g.setComposite(BlendComposite.Subtract); + break; + case 10: + g.setComposite(BlendComposite.Invert); + break; + case 11: + g.setComposite(BlendComposite.Alpha); + break; + case 12: + g.setComposite(BlendComposite.Erase); + break; + case 13: + g.setComposite(BlendComposite.Overlay); + break; + case 14: + g.setComposite(BlendComposite.HardLight); + break; + default: //Not implemented + g.setComposite(AlphaComposite.SrcOver); + break; + } + + if (layer.clipDepth > -1) { + BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); + Graphics2D gm = (Graphics2D) mask.getGraphics(); + gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gm.setComposite(AlphaComposite.Src); + gm.setColor(new Color(0, 0, 0, 0f)); + gm.fillRect(0, 0, image.getWidth(), image.getHeight()); + gm.setTransform(trans); + gm.drawImage(img.getBufferedImage(), 0, 0, null); + Clip clip = new Clip(Helper.imageToShape(mask), layer.clipDepth); //Maybe we can get current outline instead converting from image (?) + clips.add(clip); + prevClips.add(g.getClip()); + g.setTransform(AffineTransform.getTranslateInstance(0, 0)); + g.setClip(clip.shape); + } else { + g.setTransform(trans); + g.drawImage(img.getBufferedImage(), 0, 0, null); + } + } else if (character instanceof BoundedTag) { + showPlaceholder = true; + } + + if (showPlaceholder) { + mat.translateX /= unzoom; + mat.translateY /= unzoom; + AffineTransform trans = mat.toTransform(); + g.setTransform(trans); + BoundedTag b = (BoundedTag) character; + g.setPaint(new Color(255, 255, 255, 128)); + g.setComposite(BlendComposite.Invert); + RECT r = b.getRect(); + int div = (int) unzoom; + g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15); + g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div)); + g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div); + g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div); + g.setComposite(AlphaComposite.Dst); + } + } + + g.setTransform(AffineTransform.getScaleInstance(1, 1)); + } + + public void removeTagFromTimeline(Tag toRemove, List timeline) { + int characterId = 0; + if (toRemove instanceof CharacterTag) { + characterId = ((CharacterTag) toRemove).getCharacterId(); + } + Map stage = new HashMap<>(); + + Set dependingChars = new HashSet<>(); + if (characterId != 0) { + dependingChars.add(characterId); + for (int i = 0; i < timeline.size(); i++) { + Tag t = timeline.get(i); + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + if (needed.contains(characterId)) { + dependingChars.add(c.getCharacterId()); + } + } + + } + } + + for (int i = 0; i < timeline.size(); i++) { + Tag t = timeline.get(i); + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + int currentCharId = stage.get(rt.getDepth()); + stage.remove(rt.getDepth()); + if (dependingChars.contains(currentCharId)) { + timeline.remove(i); + i--; + continue; + } + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int placeCharId = po.getCharacterId(); + int placeDepth = po.getDepth(); + if (placeCharId != 0) { + stage.put(placeDepth, placeCharId); + } + int currentCharId = stage.get(placeDepth); + if (dependingChars.contains(currentCharId)) { + timeline.remove(i); + i--; + continue; + } + } + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + if (dependingChars.contains(c.getCharacterId())) { + timeline.remove(i); + i--; + continue; + } + } + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + for (int dep : dependingChars) { + if (needed.contains(dep)) { + timeline.remove(i); + i--; + continue; + } + } + if (t == toRemove) { + timeline.remove(i); + i--; + continue; + } + if (t instanceof DefineSpriteTag) { + DefineSpriteTag spr = (DefineSpriteTag) t; + removeTagFromTimeline(toRemove, spr.subTags); + } + + } + } + + public void removeTag(Tag t) { + List tags; + Timelined timelined = t.getTimelined(); + if (timelined instanceof DefineSpriteTag) { + tags = ((DefineSpriteTag) timelined).getSubTags(); + } else { + tags = this.tags; + } + if (t instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(t.getId())) { + tags.remove(t); + timelined.resetTimeline(); + } else { + removeTagFromTimeline(t, tags); + } + clearImageCache(); + } +} diff --git a/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java b/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java index 336b175e5..e51caaa0d 100644 --- a/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java +++ b/src/com/jpexs/decompiler/flash/SourceGeneratorLocalData.java @@ -1,64 +1,64 @@ -/* - * Copyright (C) 2010-2014 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; - -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * - * @author JPEXS - */ -public class SourceGeneratorLocalData implements Serializable { - - public HashMap registerVars; - public Integer inFunction; - public Boolean inMethod; - public Integer forInLevel; - - //TODO: handle AVM2 separately - public List exceptions = new ArrayList<>(); - public List finallyCatches = new ArrayList(); - public Map finallyCounter = new HashMap<>(); - public int finallyRegister = -1; - public String currentClass; - public int activationReg = 0; - public List callStack = new ArrayList<>(); - public Map> traitUsages = new HashMap<>(); - public String pkg = ""; - public List scopeStack = new ArrayList(); - public boolean documentClass; - public ScriptInfo currentScript; - - public String getFullClass(){ - return pkg==null||pkg.isEmpty()?currentClass:pkg+"."+currentClass; - } - - public SourceGeneratorLocalData(HashMap registerVars, Integer inFunction, Boolean inMethod, Integer forInLevel) { - this.registerVars = registerVars; - this.inFunction = inFunction; - this.inMethod = inMethod; - this.forInLevel = forInLevel; - } -} +/* + * Copyright (C) 2010-2014 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; + +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author JPEXS + */ +public class SourceGeneratorLocalData implements Serializable { + + public HashMap registerVars; + public Integer inFunction; + public Boolean inMethod; + public Integer forInLevel; + + //TODO: handle AVM2 separately + public List exceptions = new ArrayList<>(); + public List finallyCatches = new ArrayList(); + public Map finallyCounter = new HashMap<>(); + public int finallyRegister = -1; + public String currentClass; + public int activationReg = 0; + public List callStack = new ArrayList<>(); + public Map> traitUsages = new HashMap<>(); + public String pkg = ""; + public List scopeStack = new ArrayList(); + public boolean documentClass; + public ScriptInfo currentScript; + + public String getFullClass() { + return pkg == null || pkg.isEmpty() ? currentClass : pkg + "." + currentClass; + } + + public SourceGeneratorLocalData(HashMap registerVars, Integer inFunction, Boolean inMethod, Integer forInLevel) { + this.registerVars = registerVars; + this.inFunction = inFunction; + this.inMethod = inMethod; + this.forInLevel = forInLevel; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/ABC.java b/src/com/jpexs/decompiler/flash/abc/ABC.java index 87e100566..ec0faf7fb 100644 --- a/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -1,1075 +1,1073 @@ -/* - * Copyright (C) 2010-2014 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.abc; - -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MetadataInfo; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; -import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.helpers.utf8.Utf8PrintWriter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ABC { - - public int major_version = 46; - public int minor_version = 16; - public ConstantPool constants = new ConstantPool(); - public List method_info = new ArrayList<>(); - public List metadata_info = new ArrayList<>(); - public List instance_info = new ArrayList<>(); - public List class_info = new ArrayList<>(); - public List script_info = new ArrayList<>(); - public List bodies = new ArrayList<>(); - private List bodyIdxFromMethodIdx = new ArrayList<>(); - public long[] stringOffsets; - public static final int MINORwithDECIMAL = 17; - protected Set listeners = new HashSet<>(); - private static final Logger logger = Logger.getLogger(ABC.class.getName()); - private final AVM2Deobfuscation deobfuscation; - public SWF swf; - public ABCContainerTag parentTag; - - public ABC(SWF swf) { - this.deobfuscation = null; - this.swf = swf; - constants.constant_double.add(null); - constants.constant_int.add(null); - constants.constant_uint.add(null); - constants.constant_string.add(null); - constants.constant_multiname.add(null); - constants.constant_namespace.add(null); - constants.constant_namespace_set.add(null); - } - - public int addMethodBody(MethodBody body) { - bodies.add(body); - if (body.method_info >= bodyIdxFromMethodIdx.size()) { - int newlen = body.method_info + 1; - int oldlen = bodyIdxFromMethodIdx.size(); - for (int i = oldlen; i < newlen; i++) { - bodyIdxFromMethodIdx.add(-1); - } - bodyIdxFromMethodIdx.set(body.method_info, bodies.size() - 1); - } - return bodies.size() - 1; - } - - public int addMethodInfo(MethodInfo mi) { - method_info.add(mi); - return method_info.size() - 1; - } - - public void addEventListener(EventListener listener) { - listeners.add(listener); - } - - public void removeEventListener(EventListener listener) { - listeners.remove(listener); - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public int removeTraps() throws InterruptedException { - int rem = 0; - for (int s = 0; s < script_info.size(); s++) { - rem += script_info.get(s).removeTraps(s, this, ""); - } - return rem; - } - - public int removeDeadCode() throws InterruptedException { - int rem = 0; - for (MethodBody body : bodies) { - rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); - } - return rem; - } - - public void restoreControlFlow() throws InterruptedException { - for (MethodBody body : bodies) { - body.restoreControlFlow(constants, null/*FIXME*/, method_info.get(body.method_info)); - } - } - - public Set getNsStringUsages() { - Set ret = new HashSet<>(); - for (int n = 1; n < constants.getNamespaceCount(); n++) { - ret.add(constants.getNamespace(n).name_index); - } - return ret; - } - - public Set getStringUsages() { - Set ret = new HashSet<>(); - for (MethodBody body : bodies) { - for (AVM2Instruction ins : body.code.code) { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { - ret.add(ins.operands[i]); - } - } - } - } - return ret; - } - - private void setStringUsageType(Map ret, int strIndex, String usageType) { - if (ret.containsKey(strIndex)) { - if (!"name".equals(usageType)) { - if (!ret.get(strIndex).equals(usageType)) { - ret.put(strIndex, "name"); - } - } - } else { - ret.put(strIndex, usageType); - } - } - - private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { - for (Trait t : traits.traits) { - int strIndex = constants.getMultiname(t.name_index).name_index; - String usageType = ""; - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); - getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); - - if (instance_info.get(tc.class_info).name_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); - } - if (instance_info.get(tc.class_info).super_index != 0) { - setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); - } - - usageType = "class"; - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; - usageType = "method"; - MethodBody body = findBody(tm.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - } - if (t instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) t; - MethodBody body = findBody(tf.method_info); - if (body != null) { - getStringUsageTypes(ret, body.traits, classesOnly); - } - usageType = "function"; - } - if (t instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) t; - if (ts.isVar()) { - usageType = "var"; - } - if (ts.isConst()) { - usageType = "const"; - } - } - if (usageType.equals("class") || (!classesOnly)) { - setStringUsageType(ret, strIndex, usageType); - } - } - } - - public void getStringUsageTypes(Map ret, boolean classesOnly) { - for (ScriptInfo script : script_info) { - getStringUsageTypes(ret, script.traits, classesOnly); - } - } - - public void renameMultiname(int multinameIndex, String newname) { - if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { - throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); - } - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - int strIndex = constants.getMultiname(multinameIndex).name_index; - if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //name is used elsewhere as string literal - strIndex = constants.getStringId(newname, true); - constants.getMultiname(multinameIndex).name_index = strIndex; - } else { - constants.setString(strIndex, newname); - } - } - - public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { - Set stringUsages = getStringUsages(); - Set namespaceUsages = getNsStringUsages(); - Map stringUsageTypes = new HashMap<>(); - informListeners("deobfuscate", "Getting usage types..."); - getStringUsageTypes(stringUsageTypes, classesOnly); - for (int i = 0; i < instance_info.size(); i++) { - informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); - InstanceInfo insti = instance_info.get(i); - if (insti.name_index != 0) { - constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); - if (constants.getMultiname(insti.name_index).namespace_index != 0) { - constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index - = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); - } - } - if (insti.super_index != 0) { - constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); - } - } - if (classesOnly) { - return; - } - for (int i = 1; i < constants.getMultinameCount(); i++) { - informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); - constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); - } - for (int i = 1; i < constants.getNamespaceCount(); i++) { - informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); - if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { //only packages - continue; - } - constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); - } - - //process reflection using getDefinitionByName too - for (MethodBody body : bodies) { - for (int ip = 0; ip < body.code.code.size(); ip++) { - if (body.code.code.get(ip).definition instanceof CallPropertyIns) { - int mIndex = body.code.code.get(ip).operands[0]; - if (mIndex > 0) { - Multiname m = constants.getMultiname(mIndex); - if (m.getNameWithNamespace(constants).equals("flash.utils.getDefinitionByName")) { - if (ip > 0) { - if (body.code.code.get(ip - 1).definition instanceof PushStringIns) { - int strIndex = body.code.code.get(ip - 1).operands[0]; - String fullname = constants.getString(strIndex); - String pkg = ""; - String name = fullname; - if (fullname.contains(".")) { - pkg = fullname.substring(0, fullname.lastIndexOf('.')); - name = fullname.substring(fullname.lastIndexOf('.') + 1); - } - if (!pkg.isEmpty()) { - int pkgStrIndex = constants.getStringId(pkg, true); - pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); - pkg = constants.getString(pkgStrIndex); - } - int nameStrIndex = constants.getStringId(name, true); - nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); - name = constants.getString(nameStrIndex); - String fullChanged = ""; - if (!pkg.isEmpty()) { - fullChanged = pkg + "."; - } - fullChanged += name; - strIndex = constants.getStringId(fullChanged, true); - body.code.code.get(ip - 1).operands[0] = strIndex; - } - } - } - } - } - } - } - } - - public ABC(InputStream is, SWF swf, ABCContainerTag tag) throws IOException { - this.swf = swf; - this.parentTag = tag; - ABCInputStream ais = new ABCInputStream(is); - minor_version = ais.readU16(); - major_version = ais.readU16(); - logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); - constants = new ConstantPool(); - deobfuscation = new AVM2Deobfuscation(constants); - //constant integers - int constant_int_pool_count = ais.readU30(); - constants.constant_int = new ArrayList<>(constant_int_pool_count); - if (constant_int_pool_count > 0) { - constants.addInt(0); - } - for (int i = 1; i < constant_int_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addInt(ais.readS32()); - } - - //constant unsigned integers - int constant_uint_pool_count = ais.readU30(); - constants.constant_uint = new ArrayList<>(constant_uint_pool_count); - if (constant_uint_pool_count > 0) { - constants.addUInt(0); - } - for (int i = 1; i < constant_uint_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addUInt(ais.readU32()); - } - - //constant double - int constant_double_pool_count = ais.readU30(); - constants.constant_double = new ArrayList<>(constant_double_pool_count); - if (constant_double_pool_count > 0) { - constants.addDouble(0); - } - for (int i = 1; i < constant_double_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addDouble(ais.readDouble()); - } - - //constant decimal - if (minor_version >= MINORwithDECIMAL) { - int constant_decimal_pool_count = ais.readU30(); - constants.constant_decimal = new ArrayList<>(constant_decimal_pool_count); - if (constant_decimal_pool_count > 0) { - constants.addDecimal(null); - } - for (int i = 1; i < constant_decimal_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addDecimal(ais.readDecimal()); - } - } else { - constants.constant_decimal = new ArrayList<>(0); - } - - //constant string - int constant_string_pool_count = ais.readU30(); - constants.constant_string = new ArrayList<>(constant_string_pool_count); - stringOffsets = new long[constant_string_pool_count]; - if (constant_string_pool_count > 0) { - constants.addString(""); - } - for (int i = 1; i < constant_string_pool_count; i++) { //index 0 not used. Values 1..n-1 - long pos = ais.getPosition(); - constants.addString(ais.readString()); - stringOffsets[i] = pos; - } - - //constant namespace - int constant_namespace_pool_count = ais.readU30(); - constants.constant_namespace = new ArrayList<>(constant_namespace_pool_count); - if (constant_namespace_pool_count > 0) { - constants.addNamespace(null); - } - for (int i = 1; i < constant_namespace_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addNamespace(ais.readNamespace()); - } - - //constant namespace set - int constant_namespace_set_pool_count = ais.readU30(); - constants.constant_namespace_set = new ArrayList<>(constant_namespace_set_pool_count); - if (constant_namespace_set_pool_count > 0) { - constants.addNamespaceSet(null); - } - for (int i = 1; i < constant_namespace_set_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addNamespaceSet(new NamespaceSet()); - int namespace_count = ais.readU30(); - constants.getNamespaceSet(i).namespaces = new int[namespace_count]; - for (int j = 0; j < namespace_count; j++) { - constants.getNamespaceSet(i).namespaces[j] = ais.readU30(); - } - } - - //constant multiname - int constant_multiname_pool_count = ais.readU30(); - constants.constant_multiname = new ArrayList<>(constant_multiname_pool_count); - if (constant_multiname_pool_count > 0) { - constants.addMultiname(null); - } - for (int i = 1; i < constant_multiname_pool_count; i++) { //index 0 not used. Values 1..n-1 - constants.addMultiname(ais.readMultiname()); - } - - //method info - int methods_count = ais.readU30(); - method_info = new ArrayList<>();//MethodInfo[methods_count]; - bodyIdxFromMethodIdx = new ArrayList<>(); //[methods_count]; - for (int i = 0; i < methods_count; i++) { - method_info.add(ais.readMethodInfo()); - bodyIdxFromMethodIdx.add(-1); - } - - //metadata info - int metadata_count = ais.readU30(); - metadata_info = new ArrayList<>(); - for (int i = 0; i < metadata_count; i++) { - int name_index = ais.readU30(); - int values_count = ais.readU30(); - int[] keys = new int[values_count]; - for (int v = 0; v < values_count; v++) { - keys[v] = ais.readU30(); - } - int[] values = new int[values_count]; - for (int v = 0; v < values_count; v++) { - values[v] = ais.readU30(); - } - metadata_info.add(new MetadataInfo(name_index, keys, values)); - } - - int class_count = ais.readU30(); - instance_info = new ArrayList<>(); //InstanceInfo[class_count]; - for (int i = 0; i < class_count; i++) { - instance_info.add(ais.readInstanceInfo()); - } - class_info = new ArrayList<>(); //ClassInfo[class_count]; - for (int i = 0; i < class_count; i++) { - ClassInfo ci = new ClassInfo(); - ci.cinit_index = ais.readU30(); - ci.static_traits = ais.readTraits(); - class_info.add(ci); - } - int script_count = ais.readU30(); - script_info = new ArrayList<>(); //ScriptInfo[script_count]; - for (int i = 0; i < script_count; i++) { - ScriptInfo si = new ScriptInfo(); - si.init_index = ais.readU30(); - si.traits = ais.readTraits(); - script_info.add(si); - } - - int bodies_count = ais.readU30(); - bodies = new ArrayList<>(); //MethodBody[bodies_count]; - for (int i = 0; i < bodies_count; i++) { - MethodBody mb = new MethodBody(); - mb.method_info = ais.readU30(); - mb.max_stack = ais.readU30(); - mb.max_regs = ais.readU30(); - mb.init_scope_depth = ais.readU30(); - mb.max_scope_depth = ais.readU30(); - int code_length = ais.readU30(); - mb.codeBytes = ais.readBytes(code_length); - try { - mb.code = new AVM2Code(new ByteArrayInputStream(mb.codeBytes)); - } catch (UnknownInstructionCode re) { - mb.code = new AVM2Code(); - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, re); - } - mb.code.compact(); - int ex_count = ais.readU30(); - mb.exceptions = new ABCException[ex_count]; - for (int j = 0; j < ex_count; j++) { - ABCException abce = new ABCException(); - abce.start = ais.readU30(); - abce.end = ais.readU30(); - abce.target = ais.readU30(); - abce.type_index = ais.readU30(); - abce.name_index = ais.readU30(); - mb.exceptions[j] = abce; - } - mb.traits = ais.readTraits(); - bodies.add(mb); - method_info.get(mb.method_info).setBody(mb); - bodyIdxFromMethodIdx.set(mb.method_info, i); - } - loadNamespaceMap(); - /*for(int i=0;i(), false, t ,new ArrayList(), null); - } catch (InterruptedException ex) { - Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); - } - System.out.println(""+t.toString()); - } - System.exit(0);*/ - } - - public void saveToStream(OutputStream os) throws IOException { - ABCOutputStream aos = new ABCOutputStream(os); - aos.writeU16(minor_version); - aos.writeU16(major_version); - - aos.writeU30(constants.getIntCount()); - for (int i = 1; i < constants.getIntCount(); i++) { - aos.writeS32(constants.getInt(i)); - } - aos.writeU30(constants.getUIntCount()); - for (int i = 1; i < constants.getUIntCount(); i++) { - aos.writeU32(constants.getUInt(i)); - } - - aos.writeU30(constants.getDoubleCount()); - for (int i = 1; i < constants.getDoubleCount(); i++) { - aos.writeDouble(constants.getDouble(i)); - } - - if (minor_version >= MINORwithDECIMAL) { - aos.writeU30(constants.getDecimalCount()); - for (int i = 1; i < constants.getDecimalCount(); i++) { - aos.writeDecimal(constants.getDecimal(i)); - } - } - - aos.writeU30(constants.getStringCount()); - for (int i = 1; i < constants.getStringCount(); i++) { - aos.writeString(constants.getString(i)); - } - - aos.writeU30(constants.getNamespaceCount()); - for (int i = 1; i < constants.getNamespaceCount(); i++) { - aos.writeNamespace(constants.getNamespace(i)); - } - - aos.writeU30(constants.getNamespaceSetCount()); - for (int i = 1; i < constants.getNamespaceSetCount(); i++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces.length); - for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { - aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); - } - } - - aos.writeU30(constants.getMultinameCount()); - for (int i = 1; i < constants.getMultinameCount(); i++) { - aos.writeMultiname(constants.getMultiname(i)); - } - - aos.writeU30(method_info.size()); - for (MethodInfo mi : method_info) { - aos.writeMethodInfo(mi); - } - - aos.writeU30(metadata_info.size()); - for (MetadataInfo mi : metadata_info) { - aos.writeU30(mi.name_index); - aos.writeU30(mi.values.length); - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.keys[j]); - } - for (int j = 0; j < mi.values.length; j++) { - aos.writeU30(mi.values[j]); - } - } - - aos.writeU30(class_info.size()); - for (InstanceInfo ii : instance_info) { - aos.writeInstanceInfo(ii); - } - for (ClassInfo ci : class_info) { - aos.writeU30(ci.cinit_index); - aos.writeTraits(ci.static_traits); - } - aos.writeU30(script_info.size()); - for (ScriptInfo si : script_info) { - aos.writeU30(si.init_index); - aos.writeTraits(si.traits); - } - - aos.writeU30(bodies.size()); - for (MethodBody mb : bodies) { - aos.writeU30(mb.method_info); - aos.writeU30(mb.max_stack); - aos.writeU30(mb.max_regs); - aos.writeU30(mb.init_scope_depth); - aos.writeU30(mb.max_scope_depth); - byte[] codeBytes = mb.code.getBytes(); - aos.writeU30(codeBytes.length); - aos.write(codeBytes); - aos.writeU30(mb.exceptions.length); - for (int j = 0; j < mb.exceptions.length; j++) { - aos.writeU30(mb.exceptions[j].start); - aos.writeU30(mb.exceptions[j].end); - aos.writeU30(mb.exceptions[j].target); - aos.writeU30(mb.exceptions[j].type_index); - aos.writeU30(mb.exceptions[j].name_index); - } - aos.writeTraits(mb.traits); - } - } - - public MethodBody findBody(int methodInfo) { - if (methodInfo < 0) { - return null; - } - return method_info.get(methodInfo).getBody(); - } - - public int findBodyIndex(int methodInfo) { - if (methodInfo == -1) { - return -1; - } - return bodyIdxFromMethodIdx.get(methodInfo); - } - - public MethodBody findBodyByClassAndName(String className, String methodName) { - for (int i = 0; i < instance_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { - for (Trait t : instance_info.get(i).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { - for (MethodBody body : bodies) { - if (body.method_info == t2.method_info) { - return body; - } - } - } - } - } - //break; - } - } - for (int i = 0; i < class_info.size(); i++) { - if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { - for (Trait t : class_info.get(i).static_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; - if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { - for (MethodBody body : bodies) { - if (body.method_info == t2.method_info) { - return body; - } - } - } - } - } - //break; - } - } - - return null; - } - - public boolean isStaticTraitId(int classIndex, int traitId) { - if (traitId < class_info.get(classIndex).static_traits.traits.size()) { - return true; - } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { - return false; - } else { - return true; //Can be class or instance initializer - } - } - - public Trait findTraitByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return null; - } - if (traitId < class_info.get(classIndex).static_traits.traits.size()) { - return class_info.get(classIndex).static_traits.traits.get(traitId); - } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { - traitId -= class_info.get(classIndex).static_traits.traits.size(); - return instance_info.get(classIndex).instance_traits.traits.get(traitId); - } else { - return null; //Can be class or instance initializer - } - } - - public int findMethodIdByTraitId(int classIndex, int traitId) { - if (classIndex == -1) { - return -1; - } - if (traitId < class_info.get(classIndex).static_traits.traits.size()) { - if (class_info.get(classIndex).static_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) class_info.get(classIndex).static_traits.traits.get(traitId)).method_info; - } else { - return -1; - } - } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { - traitId -= class_info.get(classIndex).static_traits.traits.size(); - if (instance_info.get(classIndex).instance_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { - return ((TraitMethodGetterSetter) instance_info.get(classIndex).instance_traits.traits.get(traitId)).method_info; - } else { - return -1; - } - } else { - traitId -= class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size(); - if (traitId == 0) { - return instance_info.get(classIndex).iinit_index; - } else if (traitId == 1) { - return class_info.get(classIndex).cinit_index; - } else { - return -1; - } - } - } - /* Map from multiname index of namespace value to namespace name**/ - private HashMap namespaceMap; - - private void loadNamespaceMap() { - namespaceMap = new HashMap<>(); - for (ScriptInfo si : script_info) { - for (Trait t : si.traits.traits) { - if (t instanceof TraitSlotConst) { - TraitSlotConst s = ((TraitSlotConst) t); - if (s.isNamespace()) { - String key = constants.getNamespace(s.value_index).getName(constants); //assume not null - String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); - namespaceMap.put(key, val); - } - } - } - } - } - - public String nsValueToName(String value) { - if (namespaceMap.containsKey(value)) { - return namespaceMap.get(value); - } else { - String ns = deobfuscation.builtInNs(value); - if (ns == null) { - return ""; - } else { - return ns; - } - } - } - - public List> getScriptPacks() { - List> ret = new ArrayList<>(); - for (int i = 0; i < script_info.size(); i++) { - ret.addAll(script_info.get(i).getPacks(this, i)); - } - return ret; - } - - public void dump(OutputStream os) { - Utf8PrintWriter output; - output = new Utf8PrintWriter(os); - constants.dump(output); - for (int i = 0; i < method_info.size(); i++) { - output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList())); - } - for (int i = 0; i < metadata_info.size(); i++) { - output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); - } - for (int i = 0; i < instance_info.size(); i++) { - output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < class_info.size(); i++) { - output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < script_info.size(); i++) { - output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList())); - } - for (int i = 0; i < bodies.size(); i++) { - output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); - } - } - - private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { - for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { - if (method_info.get(methodInfo).param_types[p] == multinameIndex) { - ret.add(new MethodParamsMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - break; - } - } - if (method_info.get(methodInfo).ret_type == multinameIndex) { - ret.add(new MethodReturnTypeMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - } - MethodBody body = findBody(methodInfo); - if (body != null) { - findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); - for (ABCException e : body.exceptions) { - if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { - ret.add(new MethodBodyMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - return; - } - } - for (AVM2Instruction ins : body.code.code) { - for (int o = 0; o < ins.definition.operands.length; o++) { - if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { - if (ins.operands[o] == multinameIndex) { - ret.add(new MethodBodyMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); - return; - } - } - } - } - } - } - - private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { - for (int t = 0; t < traits.traits.size(); t++) { - if (traits.traits.get(t) instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); - if (tsc.name_index == multinameIndex) { - ret.add(new ConstVarNameMultinameUsage(multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - if (tsc.type_index == multinameIndex) { - ret.add(new ConstVarTypeMultinameUsage(multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); - } - } - if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); - if (tmgs.name_index == multinameIndex) { - ret.add(new MethodNameMultinameUsage(multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); - } - checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); - } - } - } - - public List findMultinameUsage(int multinameIndex) { - List ret = new ArrayList<>(); - if (multinameIndex == 0) { - return ret; - } - for (int c = 0; c < instance_info.size(); c++) { - if (instance_info.get(c).name_index == multinameIndex) { - ret.add(new ClassNameMultinameUsage(multinameIndex, c)); - } - if (instance_info.get(c).super_index == multinameIndex) { - ret.add(new ExtendsMultinameUsage(multinameIndex, c)); - } - for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { - if (instance_info.get(c).interfaces[i] == multinameIndex) { - ret.add(new ImplementsMultinameUsage(multinameIndex, c)); - } - } - checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); - checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); - findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); - findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); - } - loopm: - for (int m = 1; m < constants.getMultinameCount(); m++) { - if (constants.getMultiname(m).kind == Multiname.TYPENAME) { - if (constants.getMultiname(m).qname_index == multinameIndex) { - ret.add(new TypeNameMultinameUsage(m)); - continue; - } - for (int mp : constants.getMultiname(m).params) { - if (mp == multinameIndex) { - ret.add(new TypeNameMultinameUsage(m)); - continue loopm; - } - } - } - } - return ret; - } - - public int findMethodInfoByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { - return ((TraitMethodGetterSetter) t).method_info; - } - } - } - } - return -1; - } - - public int findMethodBodyByName(int classId, String methodName) { - if (classId > -1) { - for (Trait t : instance_info.get(classId).instance_traits.traits) { - if (t instanceof TraitMethodGetterSetter) { - if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { - return findBodyIndex(((TraitMethodGetterSetter) t).method_info); - } - } - } - } - return -1; - } - - public int findMethodBodyByName(String className, String methodName) { - int classId = findClassByName(className); - return findMethodBodyByName(classId, methodName); - } - - public int findClassByName(String name) { - for (int c = 0; c < instance_info.size(); c++) { - String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); - if (name.equals(s)) { - return c; - } - } - return -1; - } - - public ScriptPack findScriptPackByPath(String name) { - List> packs = getScriptPacks(); - for (MyEntry en : packs) { - if (en.key.toString().equals(name)) { - return en.value; - } - } - return null; - } - - private void removeClassFromTraits(Traits traits,int index){ - for(Trait t:traits.traits){ - if(t instanceof TraitClass){ - TraitClass tc=(TraitClass)t; - removeClassFromTraits(instance_info.get(tc.class_info).instance_traits,index); - removeClassFromTraits(class_info.get(tc.class_info).static_traits,index); - if(tc.class_info>index){ - tc.class_info--; - } - } - } - } - - public void removeClass(int index){ - for(MethodBody b:bodies){ - for(AVM2Instruction ins:b.code.code){ - for(int i=0;iindex){ - ins.operands[i]--; - } - } - } - } - } - for(ScriptInfo si:script_info){ - removeClassFromTraits(si.traits, index); - } - instance_info.remove(index); - class_info.remove(index); - } - - - private void removeMethodFromTraits(Traits traits,int index){ - for(Trait t:traits.traits){ - if(t instanceof TraitClass){ - TraitClass tc=(TraitClass)t; - removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits,index); - removeMethodFromTraits(class_info.get(tc.class_info).static_traits,index); - } - if(t instanceof TraitMethodGetterSetter){ - TraitMethodGetterSetter tmgs=(TraitMethodGetterSetter)t; - if(tmgs.method_info>index){ - tmgs.method_info--; - } - } - if(t instanceof TraitFunction){ - TraitFunction tf=(TraitFunction)t; - if(tf.method_info>index){ - tf.method_info--; - } - } - } - } - - public void removeMethod(int index){ - - int bindex=-1; - for(int b=0;bindex){ - b.method_info--; - } - for(AVM2Instruction ins:b.code.code){ - for(int i=0;iindex){ - ins.operands[i]--; - } - } - } - } - } - - for(int c=0;cindex){ - ii.iinit_index--; - } - ClassInfo ci=class_info.get(c); - if(ci.cinit_index>index){ - ci.cinit_index--; - } - } - - for(ScriptInfo si:script_info){ - if(si.init_index>index){ - si.init_index--; - } - removeMethodFromTraits(si.traits, index); - } - - if(bindex>-1){ - for(int mi=0;mibindex){ - bodyIdxFromMethodIdx.set(mi, bodyIdxFromMethodIdx.get(mi)-1); - } - } - } - bodyIdxFromMethodIdx.remove(index); - - method_info.remove(index); - } - - public void pack(){ - for(int c= 0;c. + */ +package com.jpexs.decompiler.flash.abc; + +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MetadataInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; +import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.utf8.Utf8PrintWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ABC { + + public int major_version = 46; + public int minor_version = 16; + public ConstantPool constants = new ConstantPool(); + public List method_info = new ArrayList<>(); + public List metadata_info = new ArrayList<>(); + public List instance_info = new ArrayList<>(); + public List class_info = new ArrayList<>(); + public List script_info = new ArrayList<>(); + public List bodies = new ArrayList<>(); + private List bodyIdxFromMethodIdx = new ArrayList<>(); + public long[] stringOffsets; + public static final int MINORwithDECIMAL = 17; + protected Set listeners = new HashSet<>(); + private static final Logger logger = Logger.getLogger(ABC.class.getName()); + private final AVM2Deobfuscation deobfuscation; + public SWF swf; + public ABCContainerTag parentTag; + + public ABC(SWF swf) { + this.deobfuscation = null; + this.swf = swf; + constants.constant_double.add(null); + constants.constant_int.add(null); + constants.constant_uint.add(null); + constants.constant_string.add(null); + constants.constant_multiname.add(null); + constants.constant_namespace.add(null); + constants.constant_namespace_set.add(null); + } + + public int addMethodBody(MethodBody body) { + bodies.add(body); + if (body.method_info >= bodyIdxFromMethodIdx.size()) { + int newlen = body.method_info + 1; + int oldlen = bodyIdxFromMethodIdx.size(); + for (int i = oldlen; i < newlen; i++) { + bodyIdxFromMethodIdx.add(-1); + } + bodyIdxFromMethodIdx.set(body.method_info, bodies.size() - 1); + } + return bodies.size() - 1; + } + + public int addMethodInfo(MethodInfo mi) { + method_info.add(mi); + return method_info.size() - 1; + } + + public void addEventListener(EventListener listener) { + listeners.add(listener); + } + + public void removeEventListener(EventListener listener) { + listeners.remove(listener); + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public int removeTraps() throws InterruptedException { + int rem = 0; + for (int s = 0; s < script_info.size(); s++) { + rem += script_info.get(s).removeTraps(s, this, ""); + } + return rem; + } + + public int removeDeadCode() throws InterruptedException { + int rem = 0; + for (MethodBody body : bodies) { + rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); + } + return rem; + } + + public void restoreControlFlow() throws InterruptedException { + for (MethodBody body : bodies) { + body.restoreControlFlow(constants, null/*FIXME*/, method_info.get(body.method_info)); + } + } + + public Set getNsStringUsages() { + Set ret = new HashSet<>(); + for (int n = 1; n < constants.getNamespaceCount(); n++) { + ret.add(constants.getNamespace(n).name_index); + } + return ret; + } + + public Set getStringUsages() { + Set ret = new HashSet<>(); + for (MethodBody body : bodies) { + for (AVM2Instruction ins : body.code.code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { + ret.add(ins.operands[i]); + } + } + } + } + return ret; + } + + private void setStringUsageType(Map ret, int strIndex, String usageType) { + if (ret.containsKey(strIndex)) { + if (!"name".equals(usageType)) { + if (!ret.get(strIndex).equals(usageType)) { + ret.put(strIndex, "name"); + } + } + } else { + ret.put(strIndex, usageType); + } + } + + private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { + for (Trait t : traits.traits) { + int strIndex = constants.getMultiname(t.name_index).name_index; + String usageType = ""; + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); + getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); + + if (instance_info.get(tc.class_info).name_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); + } + if (instance_info.get(tc.class_info).super_index != 0) { + setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); + } + + usageType = "class"; + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + usageType = "method"; + MethodBody body = findBody(tm.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + MethodBody body = findBody(tf.method_info); + if (body != null) { + getStringUsageTypes(ret, body.traits, classesOnly); + } + usageType = "function"; + } + if (t instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) t; + if (ts.isVar()) { + usageType = "var"; + } + if (ts.isConst()) { + usageType = "const"; + } + } + if (usageType.equals("class") || (!classesOnly)) { + setStringUsageType(ret, strIndex, usageType); + } + } + } + + public void getStringUsageTypes(Map ret, boolean classesOnly) { + for (ScriptInfo script : script_info) { + getStringUsageTypes(ret, script.traits, classesOnly); + } + } + + public void renameMultiname(int multinameIndex, String newname) { + if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { + throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); + } + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + int strIndex = constants.getMultiname(multinameIndex).name_index; + if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //name is used elsewhere as string literal + strIndex = constants.getStringId(newname, true); + constants.getMultiname(multinameIndex).name_index = strIndex; + } else { + constants.setString(strIndex, newname); + } + } + + public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { + Set stringUsages = getStringUsages(); + Set namespaceUsages = getNsStringUsages(); + Map stringUsageTypes = new HashMap<>(); + informListeners("deobfuscate", "Getting usage types..."); + getStringUsageTypes(stringUsageTypes, classesOnly); + for (int i = 0; i < instance_info.size(); i++) { + informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); + InstanceInfo insti = instance_info.get(i); + if (insti.name_index != 0) { + constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); + if (constants.getMultiname(insti.name_index).namespace_index != 0) { + constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index + = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); + } + } + if (insti.super_index != 0) { + constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); + } + } + if (classesOnly) { + return; + } + for (int i = 1; i < constants.getMultinameCount(); i++) { + informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); + constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); + } + for (int i = 1; i < constants.getNamespaceCount(); i++) { + informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); + if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { //only packages + continue; + } + constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); + } + + //process reflection using getDefinitionByName too + for (MethodBody body : bodies) { + for (int ip = 0; ip < body.code.code.size(); ip++) { + if (body.code.code.get(ip).definition instanceof CallPropertyIns) { + int mIndex = body.code.code.get(ip).operands[0]; + if (mIndex > 0) { + Multiname m = constants.getMultiname(mIndex); + if (m.getNameWithNamespace(constants).equals("flash.utils.getDefinitionByName")) { + if (ip > 0) { + if (body.code.code.get(ip - 1).definition instanceof PushStringIns) { + int strIndex = body.code.code.get(ip - 1).operands[0]; + String fullname = constants.getString(strIndex); + String pkg = ""; + String name = fullname; + if (fullname.contains(".")) { + pkg = fullname.substring(0, fullname.lastIndexOf('.')); + name = fullname.substring(fullname.lastIndexOf('.') + 1); + } + if (!pkg.isEmpty()) { + int pkgStrIndex = constants.getStringId(pkg, true); + pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); + pkg = constants.getString(pkgStrIndex); + } + int nameStrIndex = constants.getStringId(name, true); + nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); + name = constants.getString(nameStrIndex); + String fullChanged = ""; + if (!pkg.isEmpty()) { + fullChanged = pkg + "."; + } + fullChanged += name; + strIndex = constants.getStringId(fullChanged, true); + body.code.code.get(ip - 1).operands[0] = strIndex; + } + } + } + } + } + } + } + } + + public ABC(InputStream is, SWF swf, ABCContainerTag tag) throws IOException { + this.swf = swf; + this.parentTag = tag; + ABCInputStream ais = new ABCInputStream(is); + minor_version = ais.readU16(); + major_version = ais.readU16(); + logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); + constants = new ConstantPool(); + deobfuscation = new AVM2Deobfuscation(constants); + //constant integers + int constant_int_pool_count = ais.readU30(); + constants.constant_int = new ArrayList<>(constant_int_pool_count); + if (constant_int_pool_count > 0) { + constants.addInt(0); + } + for (int i = 1; i < constant_int_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addInt(ais.readS32()); + } + + //constant unsigned integers + int constant_uint_pool_count = ais.readU30(); + constants.constant_uint = new ArrayList<>(constant_uint_pool_count); + if (constant_uint_pool_count > 0) { + constants.addUInt(0); + } + for (int i = 1; i < constant_uint_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addUInt(ais.readU32()); + } + + //constant double + int constant_double_pool_count = ais.readU30(); + constants.constant_double = new ArrayList<>(constant_double_pool_count); + if (constant_double_pool_count > 0) { + constants.addDouble(0); + } + for (int i = 1; i < constant_double_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addDouble(ais.readDouble()); + } + + //constant decimal + if (minor_version >= MINORwithDECIMAL) { + int constant_decimal_pool_count = ais.readU30(); + constants.constant_decimal = new ArrayList<>(constant_decimal_pool_count); + if (constant_decimal_pool_count > 0) { + constants.addDecimal(null); + } + for (int i = 1; i < constant_decimal_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addDecimal(ais.readDecimal()); + } + } else { + constants.constant_decimal = new ArrayList<>(0); + } + + //constant string + int constant_string_pool_count = ais.readU30(); + constants.constant_string = new ArrayList<>(constant_string_pool_count); + stringOffsets = new long[constant_string_pool_count]; + if (constant_string_pool_count > 0) { + constants.addString(""); + } + for (int i = 1; i < constant_string_pool_count; i++) { //index 0 not used. Values 1..n-1 + long pos = ais.getPosition(); + constants.addString(ais.readString()); + stringOffsets[i] = pos; + } + + //constant namespace + int constant_namespace_pool_count = ais.readU30(); + constants.constant_namespace = new ArrayList<>(constant_namespace_pool_count); + if (constant_namespace_pool_count > 0) { + constants.addNamespace(null); + } + for (int i = 1; i < constant_namespace_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addNamespace(ais.readNamespace()); + } + + //constant namespace set + int constant_namespace_set_pool_count = ais.readU30(); + constants.constant_namespace_set = new ArrayList<>(constant_namespace_set_pool_count); + if (constant_namespace_set_pool_count > 0) { + constants.addNamespaceSet(null); + } + for (int i = 1; i < constant_namespace_set_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addNamespaceSet(new NamespaceSet()); + int namespace_count = ais.readU30(); + constants.getNamespaceSet(i).namespaces = new int[namespace_count]; + for (int j = 0; j < namespace_count; j++) { + constants.getNamespaceSet(i).namespaces[j] = ais.readU30(); + } + } + + //constant multiname + int constant_multiname_pool_count = ais.readU30(); + constants.constant_multiname = new ArrayList<>(constant_multiname_pool_count); + if (constant_multiname_pool_count > 0) { + constants.addMultiname(null); + } + for (int i = 1; i < constant_multiname_pool_count; i++) { //index 0 not used. Values 1..n-1 + constants.addMultiname(ais.readMultiname()); + } + + //method info + int methods_count = ais.readU30(); + method_info = new ArrayList<>();//MethodInfo[methods_count]; + bodyIdxFromMethodIdx = new ArrayList<>(); //[methods_count]; + for (int i = 0; i < methods_count; i++) { + method_info.add(ais.readMethodInfo()); + bodyIdxFromMethodIdx.add(-1); + } + + //metadata info + int metadata_count = ais.readU30(); + metadata_info = new ArrayList<>(); + for (int i = 0; i < metadata_count; i++) { + int name_index = ais.readU30(); + int values_count = ais.readU30(); + int[] keys = new int[values_count]; + for (int v = 0; v < values_count; v++) { + keys[v] = ais.readU30(); + } + int[] values = new int[values_count]; + for (int v = 0; v < values_count; v++) { + values[v] = ais.readU30(); + } + metadata_info.add(new MetadataInfo(name_index, keys, values)); + } + + int class_count = ais.readU30(); + instance_info = new ArrayList<>(); //InstanceInfo[class_count]; + for (int i = 0; i < class_count; i++) { + instance_info.add(ais.readInstanceInfo()); + } + class_info = new ArrayList<>(); //ClassInfo[class_count]; + for (int i = 0; i < class_count; i++) { + ClassInfo ci = new ClassInfo(); + ci.cinit_index = ais.readU30(); + ci.static_traits = ais.readTraits(); + class_info.add(ci); + } + int script_count = ais.readU30(); + script_info = new ArrayList<>(); //ScriptInfo[script_count]; + for (int i = 0; i < script_count; i++) { + ScriptInfo si = new ScriptInfo(); + si.init_index = ais.readU30(); + si.traits = ais.readTraits(); + script_info.add(si); + } + + int bodies_count = ais.readU30(); + bodies = new ArrayList<>(); //MethodBody[bodies_count]; + for (int i = 0; i < bodies_count; i++) { + MethodBody mb = new MethodBody(); + mb.method_info = ais.readU30(); + mb.max_stack = ais.readU30(); + mb.max_regs = ais.readU30(); + mb.init_scope_depth = ais.readU30(); + mb.max_scope_depth = ais.readU30(); + int code_length = ais.readU30(); + mb.codeBytes = ais.readBytes(code_length); + try { + mb.code = new AVM2Code(new ByteArrayInputStream(mb.codeBytes)); + } catch (UnknownInstructionCode re) { + mb.code = new AVM2Code(); + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, re); + } + mb.code.compact(); + int ex_count = ais.readU30(); + mb.exceptions = new ABCException[ex_count]; + for (int j = 0; j < ex_count; j++) { + ABCException abce = new ABCException(); + abce.start = ais.readU30(); + abce.end = ais.readU30(); + abce.target = ais.readU30(); + abce.type_index = ais.readU30(); + abce.name_index = ais.readU30(); + mb.exceptions[j] = abce; + } + mb.traits = ais.readTraits(); + bodies.add(mb); + method_info.get(mb.method_info).setBody(mb); + bodyIdxFromMethodIdx.set(mb.method_info, i); + } + loadNamespaceMap(); + /*for(int i=0;i(), false, t ,new ArrayList(), null); + } catch (InterruptedException ex) { + Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); + } + System.out.println(""+t.toString()); + } + System.exit(0);*/ + } + + public void saveToStream(OutputStream os) throws IOException { + ABCOutputStream aos = new ABCOutputStream(os); + aos.writeU16(minor_version); + aos.writeU16(major_version); + + aos.writeU30(constants.getIntCount()); + for (int i = 1; i < constants.getIntCount(); i++) { + aos.writeS32(constants.getInt(i)); + } + aos.writeU30(constants.getUIntCount()); + for (int i = 1; i < constants.getUIntCount(); i++) { + aos.writeU32(constants.getUInt(i)); + } + + aos.writeU30(constants.getDoubleCount()); + for (int i = 1; i < constants.getDoubleCount(); i++) { + aos.writeDouble(constants.getDouble(i)); + } + + if (minor_version >= MINORwithDECIMAL) { + aos.writeU30(constants.getDecimalCount()); + for (int i = 1; i < constants.getDecimalCount(); i++) { + aos.writeDecimal(constants.getDecimal(i)); + } + } + + aos.writeU30(constants.getStringCount()); + for (int i = 1; i < constants.getStringCount(); i++) { + aos.writeString(constants.getString(i)); + } + + aos.writeU30(constants.getNamespaceCount()); + for (int i = 1; i < constants.getNamespaceCount(); i++) { + aos.writeNamespace(constants.getNamespace(i)); + } + + aos.writeU30(constants.getNamespaceSetCount()); + for (int i = 1; i < constants.getNamespaceSetCount(); i++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces.length); + for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { + aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); + } + } + + aos.writeU30(constants.getMultinameCount()); + for (int i = 1; i < constants.getMultinameCount(); i++) { + aos.writeMultiname(constants.getMultiname(i)); + } + + aos.writeU30(method_info.size()); + for (MethodInfo mi : method_info) { + aos.writeMethodInfo(mi); + } + + aos.writeU30(metadata_info.size()); + for (MetadataInfo mi : metadata_info) { + aos.writeU30(mi.name_index); + aos.writeU30(mi.values.length); + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.keys[j]); + } + for (int j = 0; j < mi.values.length; j++) { + aos.writeU30(mi.values[j]); + } + } + + aos.writeU30(class_info.size()); + for (InstanceInfo ii : instance_info) { + aos.writeInstanceInfo(ii); + } + for (ClassInfo ci : class_info) { + aos.writeU30(ci.cinit_index); + aos.writeTraits(ci.static_traits); + } + aos.writeU30(script_info.size()); + for (ScriptInfo si : script_info) { + aos.writeU30(si.init_index); + aos.writeTraits(si.traits); + } + + aos.writeU30(bodies.size()); + for (MethodBody mb : bodies) { + aos.writeU30(mb.method_info); + aos.writeU30(mb.max_stack); + aos.writeU30(mb.max_regs); + aos.writeU30(mb.init_scope_depth); + aos.writeU30(mb.max_scope_depth); + byte[] codeBytes = mb.code.getBytes(); + aos.writeU30(codeBytes.length); + aos.write(codeBytes); + aos.writeU30(mb.exceptions.length); + for (int j = 0; j < mb.exceptions.length; j++) { + aos.writeU30(mb.exceptions[j].start); + aos.writeU30(mb.exceptions[j].end); + aos.writeU30(mb.exceptions[j].target); + aos.writeU30(mb.exceptions[j].type_index); + aos.writeU30(mb.exceptions[j].name_index); + } + aos.writeTraits(mb.traits); + } + } + + public MethodBody findBody(int methodInfo) { + if (methodInfo < 0) { + return null; + } + return method_info.get(methodInfo).getBody(); + } + + public int findBodyIndex(int methodInfo) { + if (methodInfo == -1) { + return -1; + } + return bodyIdxFromMethodIdx.get(methodInfo); + } + + public MethodBody findBodyByClassAndName(String className, String methodName) { + for (int i = 0; i < instance_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { + for (Trait t : instance_info.get(i).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { + for (MethodBody body : bodies) { + if (body.method_info == t2.method_info) { + return body; + } + } + } + } + } + //break; + } + } + for (int i = 0; i < class_info.size(); i++) { + if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { + for (Trait t : class_info.get(i).static_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; + if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { + for (MethodBody body : bodies) { + if (body.method_info == t2.method_info) { + return body; + } + } + } + } + } + //break; + } + } + + return null; + } + + public boolean isStaticTraitId(int classIndex, int traitId) { + if (traitId < class_info.get(classIndex).static_traits.traits.size()) { + return true; + } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { + return false; + } else { + return true; //Can be class or instance initializer + } + } + + public Trait findTraitByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return null; + } + if (traitId < class_info.get(classIndex).static_traits.traits.size()) { + return class_info.get(classIndex).static_traits.traits.get(traitId); + } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { + traitId -= class_info.get(classIndex).static_traits.traits.size(); + return instance_info.get(classIndex).instance_traits.traits.get(traitId); + } else { + return null; //Can be class or instance initializer + } + } + + public int findMethodIdByTraitId(int classIndex, int traitId) { + if (classIndex == -1) { + return -1; + } + if (traitId < class_info.get(classIndex).static_traits.traits.size()) { + if (class_info.get(classIndex).static_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) class_info.get(classIndex).static_traits.traits.get(traitId)).method_info; + } else { + return -1; + } + } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { + traitId -= class_info.get(classIndex).static_traits.traits.size(); + if (instance_info.get(classIndex).instance_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { + return ((TraitMethodGetterSetter) instance_info.get(classIndex).instance_traits.traits.get(traitId)).method_info; + } else { + return -1; + } + } else { + traitId -= class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size(); + if (traitId == 0) { + return instance_info.get(classIndex).iinit_index; + } else if (traitId == 1) { + return class_info.get(classIndex).cinit_index; + } else { + return -1; + } + } + } + /* Map from multiname index of namespace value to namespace name**/ + private HashMap namespaceMap; + + private void loadNamespaceMap() { + namespaceMap = new HashMap<>(); + for (ScriptInfo si : script_info) { + for (Trait t : si.traits.traits) { + if (t instanceof TraitSlotConst) { + TraitSlotConst s = ((TraitSlotConst) t); + if (s.isNamespace()) { + String key = constants.getNamespace(s.value_index).getName(constants); //assume not null + String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); + namespaceMap.put(key, val); + } + } + } + } + } + + public String nsValueToName(String value) { + if (namespaceMap.containsKey(value)) { + return namespaceMap.get(value); + } else { + String ns = deobfuscation.builtInNs(value); + if (ns == null) { + return ""; + } else { + return ns; + } + } + } + + public List> getScriptPacks() { + List> ret = new ArrayList<>(); + for (int i = 0; i < script_info.size(); i++) { + ret.addAll(script_info.get(i).getPacks(this, i)); + } + return ret; + } + + public void dump(OutputStream os) { + Utf8PrintWriter output; + output = new Utf8PrintWriter(os); + constants.dump(output); + for (int i = 0; i < method_info.size(); i++) { + output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList())); + } + for (int i = 0; i < metadata_info.size(); i++) { + output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); + } + for (int i = 0; i < instance_info.size(); i++) { + output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < class_info.size(); i++) { + output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < script_info.size(); i++) { + output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList())); + } + for (int i = 0; i < bodies.size(); i++) { + output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); + } + } + + private void checkMultinameUsedInMethod(int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { + for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { + if (method_info.get(methodInfo).param_types[p] == multinameIndex) { + ret.add(new MethodParamsMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + break; + } + } + if (method_info.get(methodInfo).ret_type == multinameIndex) { + ret.add(new MethodReturnTypeMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + } + MethodBody body = findBody(methodInfo); + if (body != null) { + findMultinameUsageInTraits(body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); + for (ABCException e : body.exceptions) { + if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { + ret.add(new MethodBodyMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + return; + } + } + for (AVM2Instruction ins : body.code.code) { + for (int o = 0; o < ins.definition.operands.length; o++) { + if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { + if (ins.operands[o] == multinameIndex) { + ret.add(new MethodBodyMultinameUsage(multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); + return; + } + } + } + } + } + } + + private void findMultinameUsageInTraits(Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { + for (int t = 0; t < traits.traits.size(); t++) { + if (traits.traits.get(t) instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); + if (tsc.name_index == multinameIndex) { + ret.add(new ConstVarNameMultinameUsage(multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + if (tsc.type_index == multinameIndex) { + ret.add(new ConstVarTypeMultinameUsage(multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); + } + } + if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); + if (tmgs.name_index == multinameIndex) { + ret.add(new MethodNameMultinameUsage(multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); + } + checkMultinameUsedInMethod(multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); + } + } + } + + public List findMultinameUsage(int multinameIndex) { + List ret = new ArrayList<>(); + if (multinameIndex == 0) { + return ret; + } + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).name_index == multinameIndex) { + ret.add(new ClassNameMultinameUsage(multinameIndex, c)); + } + if (instance_info.get(c).super_index == multinameIndex) { + ret.add(new ExtendsMultinameUsage(multinameIndex, c)); + } + for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { + if (instance_info.get(c).interfaces[i] == multinameIndex) { + ret.add(new ImplementsMultinameUsage(multinameIndex, c)); + } + } + checkMultinameUsedInMethod(multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); + checkMultinameUsedInMethod(multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); + findMultinameUsageInTraits(instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); + findMultinameUsageInTraits(class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); + } + loopm: + for (int m = 1; m < constants.getMultinameCount(); m++) { + if (constants.getMultiname(m).kind == Multiname.TYPENAME) { + if (constants.getMultiname(m).qname_index == multinameIndex) { + ret.add(new TypeNameMultinameUsage(m)); + continue; + } + for (int mp : constants.getMultiname(m).params) { + if (mp == multinameIndex) { + ret.add(new TypeNameMultinameUsage(m)); + continue loopm; + } + } + } + } + return ret; + } + + public int findMethodInfoByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { + return ((TraitMethodGetterSetter) t).method_info; + } + } + } + } + return -1; + } + + public int findMethodBodyByName(int classId, String methodName) { + if (classId > -1) { + for (Trait t : instance_info.get(classId).instance_traits.traits) { + if (t instanceof TraitMethodGetterSetter) { + if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { + return findBodyIndex(((TraitMethodGetterSetter) t).method_info); + } + } + } + } + return -1; + } + + public int findMethodBodyByName(String className, String methodName) { + int classId = findClassByName(className); + return findMethodBodyByName(classId, methodName); + } + + public int findClassByName(String name) { + for (int c = 0; c < instance_info.size(); c++) { + String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); + if (name.equals(s)) { + return c; + } + } + return -1; + } + + public ScriptPack findScriptPackByPath(String name) { + List> packs = getScriptPacks(); + for (MyEntry en : packs) { + if (en.key.toString().equals(name)) { + return en.value; + } + } + return null; + } + + private void removeClassFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); + if (tc.class_info > index) { + tc.class_info--; + } + } + } + } + + public void removeClass(int index) { + for (MethodBody b : bodies) { + for (AVM2Instruction ins : b.code.code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { + if (ins.operands[i] > index) { + ins.operands[i]--; + } + } + } + } + } + for (ScriptInfo si : script_info) { + removeClassFromTraits(si.traits, index); + } + instance_info.remove(index); + class_info.remove(index); + } + + private void removeMethodFromTraits(Traits traits, int index) { + for (Trait t : traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); + removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.method_info > index) { + tmgs.method_info--; + } + } + if (t instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) t; + if (tf.method_info > index) { + tf.method_info--; + } + } + } + } + + public void removeMethod(int index) { + + int bindex = -1; + for (int b = 0; b < bodies.size(); b++) { + if (bodies.get(b).method_info == index) { + bodies.remove(b); + bindex = b; + b--; + } + } + + for (MethodBody b : bodies) { + if (b.method_info > index) { + b.method_info--; + } + for (AVM2Instruction ins : b.code.code) { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { + if (ins.operands[i] > index) { + ins.operands[i]--; + } + } + } + } + } + + for (int c = 0; c < instance_info.size(); c++) { + InstanceInfo ii = instance_info.get(c); + if (ii.iinit_index > index) { + ii.iinit_index--; + } + ClassInfo ci = class_info.get(c); + if (ci.cinit_index > index) { + ci.cinit_index--; + } + } + + for (ScriptInfo si : script_info) { + if (si.init_index > index) { + si.init_index--; + } + removeMethodFromTraits(si.traits, index); + } + + if (bindex > -1) { + for (int mi = 0; mi < bodyIdxFromMethodIdx.size(); mi++) { + if (bodyIdxFromMethodIdx.get(mi) > bindex) { + bodyIdxFromMethodIdx.set(mi, bodyIdxFromMethodIdx.get(mi) - 1); + } + } + } + bodyIdxFromMethodIdx.remove(index); + + method_info.remove(index); + } + + public void pack() { + for (int c = 0; c < instance_info.size(); c++) { + if (instance_info.get(c).deleted) { + removeClass(c); + c--; + } + } + for (int m = 0; m < method_info.size(); m++) { + if (method_info.get(m).deleted) { + removeMethod(m); + m--; + } + } + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java b/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java index 3e0a7c388..048f43c8f 100644 --- a/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java +++ b/src/com/jpexs/decompiler/flash/abc/ABCInputStream.java @@ -1,391 +1,391 @@ -/* - * Copyright (C) 2010-2014 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.abc; - -import com.jpexs.decompiler.flash.abc.types.Decimal; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class ABCInputStream extends InputStream { - - private static final int CLASS_PROTECTED_NS = 8; - private static final int ATTR_METADATA = 4; - private final InputStream is; - private long bytesRead = 0; - private ByteArrayOutputStream bufferOs = null; - public static final boolean DEBUG_READ = false; - - public void startBuffer() { - if (bufferOs == null) { - bufferOs = new ByteArrayOutputStream(); - } else { - bufferOs.reset(); - } - } - - public byte[] stopBuffer() { - if (bufferOs == null) { - return new byte[0]; - } - byte[] ret = bufferOs.toByteArray(); - bufferOs.reset(); - return ret; - } - - public ABCInputStream(InputStream is) { - this.is = is; - } - - @Override - public int read() throws IOException { - bytesRead++; - int i = is.read(); - if (DEBUG_READ) { - System.out.println("Read:0x" + Integer.toHexString(i)); - } - if (bufferOs != null) { - if (i != -1) { - bufferOs.write(i); - } - } - return i; - } - - @Override - public int read(byte[] b) throws IOException { - int currBytesRead = is.read(b); - bytesRead += currBytesRead; - if (DEBUG_READ) { - StringBuilder sb = new StringBuilder("Read["); - sb.append(currBytesRead); - sb.append('/'); - sb.append(b.length); - sb.append("]: "); - for (int jj = 0; jj < currBytesRead; jj++) { - sb.append("0x"); - sb.append(Integer.toHexString(b[jj])); - sb.append(' '); - } - System.out.println(sb.toString()); - } - if (bufferOs != null) { - if (currBytesRead > 0) { - bufferOs.write(b, 0, currBytesRead); - } - } - return currBytesRead; - } - - ; - - public int readU8() throws IOException { - return read(); - } - - public long readU32() throws IOException { - int i; - long ret = 0; - int bytePos = 0; - int byteCount = 0; - boolean nextByte; - do { - i = read(); - nextByte = (i >> 7) == 1; - i &= 0x7f; - ret += (((long) i) << bytePos); - byteCount++; - bytePos += 7; - } while (nextByte); - return ret; - } - - public int readU30() throws IOException { - return (int) readU32(); - } - - public int readS24() throws IOException { - int ret = (read()) + (read() << 8) + (read() << 16); - - if ((ret >> 23) == 1) { - ret |= 0xff000000; - } - - return ret; - } - - public int readU16() throws IOException { - return (read()) + (read() << 8); - } - - public long readS32() throws IOException { - int i; - long ret = 0; - int bytePos = 0; - int byteCount = 0; - boolean nextByte; - do { - i = read(); - nextByte = (i >> 7) == 1; - i &= 0x7f; - ret += (i << bytePos); - byteCount++; - bytePos += 7; - if (bytePos == 35) { - if ((ret >> 31) == 1) { - ret = -(ret & 0x7fffffff); - } - break; - } - } while (nextByte); - return ret; - } - - @Override - public int available() throws IOException { - return is.available(); - } - - public final long readLong() throws IOException { - byte[] readBuffer = safeRead(8); - return (((long) readBuffer[7] << 56) - + ((long) (readBuffer[6] & 255) << 48) - + ((long) (readBuffer[5] & 255) << 40) - + ((long) (readBuffer[4] & 255) << 32) - + ((long) (readBuffer[3] & 255) << 24) - + ((readBuffer[2] & 255) << 16) - + ((readBuffer[1] & 255) << 8) - + ((readBuffer[0] & 255))); - } - - public double readDouble() throws IOException { - long el = readLong(); - double ret = Double.longBitsToDouble(el); - return ret; - } - - private byte[] safeRead(int count) throws IOException { - byte[] ret = new byte[count]; - for (int i = 0; i < count; i++) { - ret[i] = (byte) read(); - } - return ret; - } - - public Namespace readNamespace() throws IOException { - int kind = read(); - int name_index = 0; - for (int k = 0; k < Namespace.nameSpaceKinds.length; k++) { - if (Namespace.nameSpaceKinds[k] == kind) { - name_index = readU30(); - break; - } - } - return new Namespace(kind, name_index); - } - - public Multiname readMultiname() throws IOException { - int kind = readU8(); - int namespace_index = 0; - int name_index = 0; - int namespace_set_index = 0; - int qname_index = 0; - List params = new ArrayList<>(); - - if ((kind == Multiname.QNAME) || (kind == Multiname.QNAMEA)) { - namespace_index = readU30(); - name_index = readU30(); - } else if ((kind == Multiname.RTQNAME) || (kind == Multiname.RTQNAMEA)) { - name_index = readU30(); - } else if ((kind == Multiname.RTQNAMEL) || (kind == Multiname.RTQNAMELA)) { - - } else if ((kind == Multiname.MULTINAME) || (kind == Multiname.MULTINAMEA)) { - name_index = readU30(); - namespace_set_index = readU30(); - } else if ((kind == Multiname.MULTINAMEL) || (kind == Multiname.MULTINAMELA)) { - namespace_set_index = readU30(); - } else if (kind == Multiname.TYPENAME) { - qname_index = readU30(); //Multiname index!!! - int paramsLength = readU30(); - for (int i = 0; i < paramsLength; i++) { - params.add(readU30()); //multiname indices! - } - } else { - throw new IOException("Unknown kind of Multiname:0x" + Integer.toHexString(kind)); - } - - return new Multiname(kind, name_index, namespace_index, namespace_set_index, qname_index, params); - } - - public MethodInfo readMethodInfo() throws IOException { - int param_count = readU30(); - int ret_type = readU30(); - int[] param_types = new int[param_count]; - for (int i = 0; i < param_count; i++) { - param_types[i] = readU30(); - } - int name_index = readU30(); - int flags = read(); - - //// 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional (16=ignore_rest, 32=explicit,) 64=setsdxns, 128=has_paramnames - ValueKind[] optional = new ValueKind[0]; - if ((flags & 8) == 8) { //if has_optional - int optional_count = readU30(); - optional = new ValueKind[optional_count]; - for (int i = 0; i < optional_count; i++) { - optional[i] = new ValueKind(readU30(), read()); - } - } - - int[] param_names = new int[param_count]; - if ((flags & 128) == 128) { //if has_paramnames - for (int i = 0; i < param_count; i++) { - param_names[i] = readU30(); - } - } - return new MethodInfo(param_types, ret_type, name_index, flags, optional, param_names); - } - - public Trait readTrait() throws IOException { - long pos = getPosition(); - startBuffer(); - int name_index = readU30(); - int kind = read(); - int kindType = 0xf & kind; - int kindFlags = kind >> 4; - Trait trait; - - switch (kindType) { - case 0: //slot - case 6: //const - TraitSlotConst t1 = new TraitSlotConst(); - t1.slot_id = readU30(); - t1.type_index = readU30(); - t1.value_index = readU30(); - if (t1.value_index != 0) { - t1.value_kind = read(); - } - trait = t1; - break; - case 1: //method - case 2: //getter - case 3: //setter - TraitMethodGetterSetter t2 = new TraitMethodGetterSetter(); - t2.disp_id = readU30(); - t2.method_info = readU30(); - trait = t2; - break; - case 4: //class - TraitClass t3 = new TraitClass(); - t3.slot_id = readU30(); - t3.class_info = readU30(); - trait = t3; - break; - case 5: //function - TraitFunction t4 = new TraitFunction(); - t4.slot_id = readU30(); - t4.method_info = readU30(); - trait = t4; - break; - default: - throw new IOException("Unknown trait kind:" + kind); - } - trait.fileOffset = pos; - trait.kindType = kindType; - trait.kindFlags = kindFlags; - trait.name_index = name_index; - if ((kindFlags & ATTR_METADATA) != 0) { - int metadata_count = readU30(); - trait.metadata = new int[metadata_count]; - for (int i = 0; i < metadata_count; i++) { - trait.metadata[i] = readU30(); - } - } - trait.bytes = stopBuffer(); - return trait; - } - - public Traits readTraits() throws IOException { - int count = readU30(); - Traits traits = new Traits(); - traits.traits = new ArrayList<>(); - for (int i = 0; i < count; i++) { - traits.traits.add(readTrait()); - } - return traits; - } - - public byte[] readBytes(int count) throws IOException { - byte[] ret = new byte[count]; - for (int i = 0; i < count; i++) { - ret[i] = (byte) read(); - } - return ret; - } - - public Decimal readDecimal() throws IOException { - byte[] data = readBytes(16); - return new Decimal(data); - } - - public InstanceInfo readInstanceInfo() throws IOException { - InstanceInfo ret = new InstanceInfo(); - ret.name_index = readU30(); - ret.super_index = readU30(); - ret.flags = read(); - if ((ret.flags & CLASS_PROTECTED_NS) != 0) { - ret.protectedNS = readU30(); - } - int interfaces_count = readU30(); - ret.interfaces = new int[interfaces_count]; - for (int i = 0; i < interfaces_count; i++) { - ret.interfaces[i] = readU30(); - } - ret.iinit_index = readU30(); - ret.instance_traits = readTraits(); - return ret; - } - - public String readString() throws IOException { - int length = readU30(); - byte[] b = safeRead(length); - String r = new String(b, Utf8Helper.charset); - return r; - } - - - /*public void markStart(){ - bytesRead=0; - }*/ - public long getPosition() { - return bytesRead; - } -} +/* + * Copyright (C) 2010-2014 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.abc; + +import com.jpexs.decompiler.flash.abc.types.Decimal; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class ABCInputStream extends InputStream { + + private static final int CLASS_PROTECTED_NS = 8; + private static final int ATTR_METADATA = 4; + private final InputStream is; + private long bytesRead = 0; + private ByteArrayOutputStream bufferOs = null; + public static final boolean DEBUG_READ = false; + + public void startBuffer() { + if (bufferOs == null) { + bufferOs = new ByteArrayOutputStream(); + } else { + bufferOs.reset(); + } + } + + public byte[] stopBuffer() { + if (bufferOs == null) { + return new byte[0]; + } + byte[] ret = bufferOs.toByteArray(); + bufferOs.reset(); + return ret; + } + + public ABCInputStream(InputStream is) { + this.is = is; + } + + @Override + public int read() throws IOException { + bytesRead++; + int i = is.read(); + if (DEBUG_READ) { + System.out.println("Read:0x" + Integer.toHexString(i)); + } + if (bufferOs != null) { + if (i != -1) { + bufferOs.write(i); + } + } + return i; + } + + @Override + public int read(byte[] b) throws IOException { + int currBytesRead = is.read(b); + bytesRead += currBytesRead; + if (DEBUG_READ) { + StringBuilder sb = new StringBuilder("Read["); + sb.append(currBytesRead); + sb.append('/'); + sb.append(b.length); + sb.append("]: "); + for (int jj = 0; jj < currBytesRead; jj++) { + sb.append("0x"); + sb.append(Integer.toHexString(b[jj])); + sb.append(' '); + } + System.out.println(sb.toString()); + } + if (bufferOs != null) { + if (currBytesRead > 0) { + bufferOs.write(b, 0, currBytesRead); + } + } + return currBytesRead; + } + + ; + + public int readU8() throws IOException { + return read(); + } + + public long readU32() throws IOException { + int i; + long ret = 0; + int bytePos = 0; + int byteCount = 0; + boolean nextByte; + do { + i = read(); + nextByte = (i >> 7) == 1; + i &= 0x7f; + ret += (((long) i) << bytePos); + byteCount++; + bytePos += 7; + } while (nextByte); + return ret; + } + + public int readU30() throws IOException { + return (int) readU32(); + } + + public int readS24() throws IOException { + int ret = (read()) + (read() << 8) + (read() << 16); + + if ((ret >> 23) == 1) { + ret |= 0xff000000; + } + + return ret; + } + + public int readU16() throws IOException { + return (read()) + (read() << 8); + } + + public long readS32() throws IOException { + int i; + long ret = 0; + int bytePos = 0; + int byteCount = 0; + boolean nextByte; + do { + i = read(); + nextByte = (i >> 7) == 1; + i &= 0x7f; + ret += (i << bytePos); + byteCount++; + bytePos += 7; + if (bytePos == 35) { + if ((ret >> 31) == 1) { + ret = -(ret & 0x7fffffff); + } + break; + } + } while (nextByte); + return ret; + } + + @Override + public int available() throws IOException { + return is.available(); + } + + public final long readLong() throws IOException { + byte[] readBuffer = safeRead(8); + return (((long) readBuffer[7] << 56) + + ((long) (readBuffer[6] & 255) << 48) + + ((long) (readBuffer[5] & 255) << 40) + + ((long) (readBuffer[4] & 255) << 32) + + ((long) (readBuffer[3] & 255) << 24) + + ((readBuffer[2] & 255) << 16) + + ((readBuffer[1] & 255) << 8) + + ((readBuffer[0] & 255))); + } + + public double readDouble() throws IOException { + long el = readLong(); + double ret = Double.longBitsToDouble(el); + return ret; + } + + private byte[] safeRead(int count) throws IOException { + byte[] ret = new byte[count]; + for (int i = 0; i < count; i++) { + ret[i] = (byte) read(); + } + return ret; + } + + public Namespace readNamespace() throws IOException { + int kind = read(); + int name_index = 0; + for (int k = 0; k < Namespace.nameSpaceKinds.length; k++) { + if (Namespace.nameSpaceKinds[k] == kind) { + name_index = readU30(); + break; + } + } + return new Namespace(kind, name_index); + } + + public Multiname readMultiname() throws IOException { + int kind = readU8(); + int namespace_index = 0; + int name_index = 0; + int namespace_set_index = 0; + int qname_index = 0; + List params = new ArrayList<>(); + + if ((kind == Multiname.QNAME) || (kind == Multiname.QNAMEA)) { + namespace_index = readU30(); + name_index = readU30(); + } else if ((kind == Multiname.RTQNAME) || (kind == Multiname.RTQNAMEA)) { + name_index = readU30(); + } else if ((kind == Multiname.RTQNAMEL) || (kind == Multiname.RTQNAMELA)) { + + } else if ((kind == Multiname.MULTINAME) || (kind == Multiname.MULTINAMEA)) { + name_index = readU30(); + namespace_set_index = readU30(); + } else if ((kind == Multiname.MULTINAMEL) || (kind == Multiname.MULTINAMELA)) { + namespace_set_index = readU30(); + } else if (kind == Multiname.TYPENAME) { + qname_index = readU30(); //Multiname index!!! + int paramsLength = readU30(); + for (int i = 0; i < paramsLength; i++) { + params.add(readU30()); //multiname indices! + } + } else { + throw new IOException("Unknown kind of Multiname:0x" + Integer.toHexString(kind)); + } + + return new Multiname(kind, name_index, namespace_index, namespace_set_index, qname_index, params); + } + + public MethodInfo readMethodInfo() throws IOException { + int param_count = readU30(); + int ret_type = readU30(); + int[] param_types = new int[param_count]; + for (int i = 0; i < param_count; i++) { + param_types[i] = readU30(); + } + int name_index = readU30(); + int flags = read(); + + //// 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional (16=ignore_rest, 32=explicit,) 64=setsdxns, 128=has_paramnames + ValueKind[] optional = new ValueKind[0]; + if ((flags & 8) == 8) { //if has_optional + int optional_count = readU30(); + optional = new ValueKind[optional_count]; + for (int i = 0; i < optional_count; i++) { + optional[i] = new ValueKind(readU30(), read()); + } + } + + int[] param_names = new int[param_count]; + if ((flags & 128) == 128) { //if has_paramnames + for (int i = 0; i < param_count; i++) { + param_names[i] = readU30(); + } + } + return new MethodInfo(param_types, ret_type, name_index, flags, optional, param_names); + } + + public Trait readTrait() throws IOException { + long pos = getPosition(); + startBuffer(); + int name_index = readU30(); + int kind = read(); + int kindType = 0xf & kind; + int kindFlags = kind >> 4; + Trait trait; + + switch (kindType) { + case 0: //slot + case 6: //const + TraitSlotConst t1 = new TraitSlotConst(); + t1.slot_id = readU30(); + t1.type_index = readU30(); + t1.value_index = readU30(); + if (t1.value_index != 0) { + t1.value_kind = read(); + } + trait = t1; + break; + case 1: //method + case 2: //getter + case 3: //setter + TraitMethodGetterSetter t2 = new TraitMethodGetterSetter(); + t2.disp_id = readU30(); + t2.method_info = readU30(); + trait = t2; + break; + case 4: //class + TraitClass t3 = new TraitClass(); + t3.slot_id = readU30(); + t3.class_info = readU30(); + trait = t3; + break; + case 5: //function + TraitFunction t4 = new TraitFunction(); + t4.slot_id = readU30(); + t4.method_info = readU30(); + trait = t4; + break; + default: + throw new IOException("Unknown trait kind:" + kind); + } + trait.fileOffset = pos; + trait.kindType = kindType; + trait.kindFlags = kindFlags; + trait.name_index = name_index; + if ((kindFlags & ATTR_METADATA) != 0) { + int metadata_count = readU30(); + trait.metadata = new int[metadata_count]; + for (int i = 0; i < metadata_count; i++) { + trait.metadata[i] = readU30(); + } + } + trait.bytes = stopBuffer(); + return trait; + } + + public Traits readTraits() throws IOException { + int count = readU30(); + Traits traits = new Traits(); + traits.traits = new ArrayList<>(); + for (int i = 0; i < count; i++) { + traits.traits.add(readTrait()); + } + return traits; + } + + public byte[] readBytes(int count) throws IOException { + byte[] ret = new byte[count]; + for (int i = 0; i < count; i++) { + ret[i] = (byte) read(); + } + return ret; + } + + public Decimal readDecimal() throws IOException { + byte[] data = readBytes(16); + return new Decimal(data); + } + + public InstanceInfo readInstanceInfo() throws IOException { + InstanceInfo ret = new InstanceInfo(); + ret.name_index = readU30(); + ret.super_index = readU30(); + ret.flags = read(); + if ((ret.flags & CLASS_PROTECTED_NS) != 0) { + ret.protectedNS = readU30(); + } + int interfaces_count = readU30(); + ret.interfaces = new int[interfaces_count]; + for (int i = 0; i < interfaces_count; i++) { + ret.interfaces[i] = readU30(); + } + ret.iinit_index = readU30(); + ret.instance_traits = readTraits(); + return ret; + } + + public String readString() throws IOException { + int length = readU30(); + byte[] b = safeRead(length); + String r = new String(b, Utf8Helper.charset); + return r; + } + + + /*public void markStart(){ + bytesRead=0; + }*/ + public long getPosition() { + return bytesRead; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index e3a74104a..adbce977e 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -1,2894 +1,2889 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ABCInputStream; -import com.jpexs.decompiler.flash.abc.AVM2LocalData; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; -import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2GraphSource; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Lf32Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Lf64Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li16Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li32Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li8Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sf32Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sf64Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si16Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si32Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si8Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi16Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi1Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi8Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DivideIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitNotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewArrayIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugFileIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallMethodIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallStaticIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfEqIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfGeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfGtIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfLeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfLtIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNGeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNGtIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNLeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNLtIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal1Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal2Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal3Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal1Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal2Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal3Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindDefIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNextIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetGlobalSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNamespaceIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNanIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUIntIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeLateIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceOrConvertTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertOIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.InstanceOfIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.IsTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.IsTypeLateIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.TypeOfIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.DXNSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.DXNSLateIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.EscXAttrIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.EscXElemIns; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InitPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewFunctionAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.DeclarationAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphPart; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.NotCompileTimeItem; -import com.jpexs.decompiler.graph.TranslateException; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.ScriptEndItem; -import com.jpexs.helpers.Helper; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class AVM2Code implements Serializable { - - public static final long serialVersionUID = 1L; - private static final boolean DEBUG_MODE = false; - public static int toSourceLimit = -1; - public List code = new ArrayList<>(); - public static boolean DEBUG_REWRITE = false; - public static final int OPT_U30 = 0x100; - public static final int OPT_U8 = 0x200; - public static final int OPT_S24 = 0x300; - public static final int OPT_CASE_OFFSETS = 0x400; - public static final int OPT_BYTE = 0x500; - public static final int DAT_MULTINAME_INDEX = OPT_U30 + 0x01; - public static final int DAT_ARG_COUNT = OPT_U30 + 0x02; - public static final int DAT_METHOD_INDEX = OPT_U30 + 0x03; - public static final int DAT_STRING_INDEX = OPT_U30 + 0x04; - public static final int DAT_DEBUG_TYPE = OPT_U8 + 0x05; - public static final int DAT_REGISTER_INDEX = OPT_U8 + 0x06; - public static final int DAT_LINENUM = OPT_U30 + 0x07; - public static final int DAT_LOCAL_REG_INDEX = OPT_U30 + 0x08; - public static final int DAT_SLOT_INDEX = OPT_U30 + 0x09; - public static final int DAT_SLOT_SCOPE_INDEX = OPT_U30 + 0x0A; - public static final int DAT_OFFSET = OPT_S24 + 0x0B; - public static final int DAT_EXCEPTION_INDEX = OPT_U30 + 0x0C; - public static final int DAT_CLASS_INDEX = OPT_U30 + 0x0D; - public static final int DAT_INT_INDEX = OPT_U30 + 0x0E; - public static final int DAT_UINT_INDEX = OPT_U30 + 0x0F; - public static final int DAT_DOUBLE_INDEX = OPT_U30 + 0x10; - public static final int DAT_DECIMAL_INDEX = OPT_U30 + 0x11; - public static final int DAT_CASE_BASEOFFSET = OPT_S24 + 0x12; - public static final int DAT_DECIMAL_PARAMS = OPT_U30 + 0x13; - public static InstructionDefinition[] instructionSet = new InstructionDefinition[]{ - new AddIns(), - new InstructionDefinition(0x9b, "add_d", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2 + 1; //? - } - }, - new AddIIns(), - new InstructionDefinition(0xb5, "add_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), - new ApplyTypeIns(), - new AsTypeIns(), - new AsTypeLateIns(), - new BitAndIns(), - new BitNotIns(), - new BitOrIns(), - new BitXorIns(), - new InstructionDefinition(0x01, "bkpt", new int[]{}), - new InstructionDefinition(0xf2, "bkptline", new int[]{AVM2Code.OPT_U30}), - new CallIns(), - new InstructionDefinition(0x4d, "callinterface", new int[]{AVM2Code.OPT_U30}), - new CallMethodIns(), - new CallPropertyIns(), - new CallPropLexIns(), - new CallPropVoidIns(), - new CallStaticIns(), - new CallSuperIns(), - new InstructionDefinition(0x4b, "callsuperid", new int[]{}), - new CallSuperVoidIns(), - new CheckFilterIns(), - new CoerceIns(), - new CoerceAIns(), - new InstructionDefinition(0x81, "coerce_b", new int[]{}), //stack:-1+1 - new InstructionDefinition(0x84, "coerce_d", new int[]{}), //stack:-1+1 - new InstructionDefinition(0x83, "coerce_i", new int[]{}), //stack:-1+1 - new InstructionDefinition(0x89, "coerce_o", new int[]{}), //stack:-1+1 - new CoerceSIns(), - new InstructionDefinition(0x88, "coerce_u", new int[]{}), //stack:-1+1 - new InstructionDefinition(0x9a, "concat", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2 + 1; //? - } - }, - new ConstructIns(), - new ConstructPropIns(), - new ConstructSuperIns(), - new ConvertBIns(), - new ConvertIIns(), - new ConvertDIns(), - new ConvertOIns(), - new ConvertUIns(), - new ConvertSIns(), - new InstructionDefinition(0x79, "convert_m", new int[]{}), //-1 +1 - new InstructionDefinition(0x7a, "convert_m_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new DebugIns(), - new DebugFileIns(), - new DebugLineIns(), - new DecLocalIns(), - new DecLocalIIns(), - new DecrementIns(), - new DecrementIIns(), - new InstructionDefinition(0x5b, "deldescendants", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new DeletePropertyIns(), - new InstructionDefinition(0x6b, "deletepropertylate", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new DivideIns(), - new InstructionDefinition(0xb8, "divide_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2 + 1; //? - } - }, - new DupIns(), - new DXNSIns(), - new DXNSLateIns(), - new EqualsIns(), - new EscXAttrIns(), - new EscXElemIns(), - new FindDefIns(), - /* //Duplicate OPCODE with deldescendants. Prefering deldescendants (found in FLEX compiler) - new InstructionDefinition(0x5b,"findpropglobalstrict",new int[]{AVM2Code.DAT_MULTINAME_INDEX}){ - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - },*/ - new InstructionDefinition(0x5c, "findpropglobal", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new FindPropertyIns(), - new FindPropertyStrictIns(), - new GetDescendantsIns(), - new GetGlobalScopeIns(), - new GetGlobalSlotIns(), - new GetLexIns(), - new GetLocalIns(), - new GetLocal0Ins(), - new GetLocal1Ins(), - new GetLocal2Ins(), - new GetLocal3Ins(), - new InstructionDefinition(0x67, "getouterscope", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new GetPropertyIns(), - new GetScopeObjectIns(), - new GetSlotIns(), - new GetSuperIns(), - new GreaterEqualsIns(), - new GreaterThanIns(), - new HasNextIns(), - new HasNext2Ins(), - new IfEqIns(), - new IfFalseIns(), - new IfGeIns(), - new IfGtIns(), - new IfLeIns(), - new IfLtIns(), - new IfNGeIns(), - new IfNGtIns(), - new IfNLeIns(), - new IfNLtIns(), - new IfNeIns(), - new IfStrictEqIns(), - new IfStrictNeIns(), - new IfTrueIns(), - new InIns(), - new IncLocalIns(), - new IncLocalIIns(), - new IncrementIns(), - new IncrementIIns(), - new InstructionDefinition(0x9c, "increment_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), - new InstructionDefinition(0x9d, "inclocal_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS, AVM2Code.DAT_REGISTER_INDEX}), - new InstructionDefinition(0x9e, "decrement_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), - new InstructionDefinition(0x9f, "declocal_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS, AVM2Code.DAT_REGISTER_INDEX}), - new InitPropertyIns(), - new InstanceOfIns(), - new IsTypeIns(), - new IsTypeLateIns(), - new JumpIns(), - new KillIns(), - new LabelIns(), - new LessEqualsIns(), - new LessThanIns(), - new LookupSwitchIns(), - new LShiftIns(), - new ModuloIns(), - new InstructionDefinition(0xb9, "modulo_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2 + 1; //? - } - }, - new MultiplyIns(), - new MultiplyIIns(), - new InstructionDefinition(0xb7, "multiply_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -2 + 1; //? - } - }, - new NegateIns(), - new NegateIIns(), - new InstructionDefinition(0x8f, "negate_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new NewActivationIns(), - new NewArrayIns(), - new NewCatchIns(), - new NewClassIns(), - new NewFunctionIns(), - new NewObjectIns(), - new NextNameIns(), - new NextValueIns(), - new NopIns(), - new NotIns(), - new PopIns(), - new PopScopeIns(), - new PushByteIns(), - new InstructionDefinition(0x22, "pushconstant", new int[]{AVM2Code.DAT_STRING_INDEX}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return 1; //? - } - }, - new InstructionDefinition(0x33, "pushdecimal", new int[]{AVM2Code.DAT_DECIMAL_INDEX}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return 1; //? - } - }, - new InstructionDefinition(0x34, "pushdnan", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return 1; //? - } - }, - new PushDoubleIns(), - new PushFalseIns(), - new PushIntIns(), - new PushNamespaceIns(), - new PushNanIns(), - new PushNullIns(), - new PushScopeIns(), - new PushShortIns(), - new PushStringIns(), - new PushTrueIns(), - new PushUIntIns(), - new PushUndefinedIns(), - new PushWithIns(), - new ReturnValueIns(), - new ReturnVoidIns(), - new RShiftIns(), - new SetLocalIns(), - new SetLocal0Ins(), - new SetLocal1Ins(), - new SetLocal2Ins(), - new SetLocal3Ins(), - new SetGlobalSlotIns(), - new SetPropertyIns(), - new InstructionDefinition(0x69, "setpropertylate", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new SetSlotIns(), - new SetSuperIns(), - new StrictEqualsIns(), - new SubtractIns(), - new SubtractIIns(), - new InstructionDefinition(0xb6, "subtract_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new SwapIns(), - new ThrowIns(), - new InstructionDefinition(0xf3, "timestamp", new int[]{}), - new TypeOfIns(), - new URShiftIns(), - new Li8Ins(), - new Li16Ins(), - new Li32Ins(), - new Lf32Ins(), - new Lf64Ins(), - new Si8Ins(), - new Si16Ins(), - new Si32Ins(), - new Sf32Ins(), - new Sf64Ins(), - new Sxi1Ins(), - new Sxi8Ins(), - new Sxi16Ins(), - new InstructionDefinition(0xf5, "verifypass", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xf6, "alloc", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xf7, "mark", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xf8, "wb", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xf9, "prologue", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xfa, "sendenter", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xfb, "doubletoatom", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xfc, "sweep", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xfd, "codegenop", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xfe, "verifyop", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xff, "decode", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - }, - new InstructionDefinition(0xee, "abs_jump", new int[]{}) { - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - - @Override - public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { - throw new UnsupportedOperationException(); - } - } - }; - //endoflist - public static InstructionDefinition[] instructionSetByCode = buildInstructionSetByCode(); - public boolean hideTemporaryRegisters = true; - - private static InstructionDefinition[] buildInstructionSetByCode() { - InstructionDefinition[] result = new InstructionDefinition[256]; - for (InstructionDefinition id : instructionSet) { - if (result[id.instructionCode] != null) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.WARNING, "Duplicate OPCODE for instruction {0} {1}", new Object[]{result[id.instructionCode], id}); - } - result[id.instructionCode] = id; - } - return result; - } - public static final String IDENTOPEN = "/*IDENTOPEN*/"; - public static final String IDENTCLOSE = "/*IDENTCLOSE*/"; - - public AVM2Code() { - } - - public Object execute(HashMap arguments, ConstantPool constants) { - int pos = 0; - LocalDataArea lda = new LocalDataArea(); - lda.localRegisters = arguments; - try { - while (true) { - AVM2Instruction ins = code.get(pos); - if (ins.definition instanceof JumpIns) { - pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); - continue; - } - if (ins.definition instanceof IfFalseIns) { - Boolean b = (Boolean) lda.operandStack.pop(); - if (b == false) { - pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); - } else { - pos++; - } - continue; - } - if (ins.definition instanceof IfTrueIns) { - Boolean b = (Boolean) lda.operandStack.pop(); - if (b == true) { - pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); - } else { - pos++; - } - continue; - } - if (ins.definition instanceof ReturnValueIns) { - return lda.operandStack.pop(); - } - if (ins.definition instanceof ReturnVoidIns) { - return null; - } - ins.definition.execute(lda, constants, ins.getParamsAsList(constants)); - pos++; - } - } catch (ConvertException e) { - } - return null; - } - - public AVM2Code(InputStream is) throws IOException { - ABCInputStream ais = new ABCInputStream(is); - while (ais.available() > 0) { - long startOffset = ais.getPosition(); - ais.startBuffer(); - int instructionCode = ais.read(); - InstructionDefinition instr = instructionSetByCode[instructionCode]; - if (instr != null) { - int[] actualOperands; - if (instructionCode == 0x1b) { //switch - int firstOperand = ais.readS24(); - int case_count = ais.readU30(); - actualOperands = new int[case_count + 3]; - actualOperands[0] = firstOperand; - actualOperands[1] = case_count; - for (int c = 0; c < case_count + 1; c++) { - actualOperands[2 + c] = ais.readS24(); - } - } else { - actualOperands = new int[instr.operands.length]; - for (int op = 0; op < instr.operands.length; op++) { - switch (instr.operands[op] & 0xff00) { - case OPT_U30: - actualOperands[op] = ais.readU30(); - break; - case OPT_U8: - actualOperands[op] = ais.read(); - break; - case OPT_BYTE: - actualOperands[op] = (byte) ais.read(); - break; - case OPT_S24: - actualOperands[op] = ais.readS24(); - break; - } - } - } - - code.add(new AVM2Instruction(startOffset, instr, actualOperands, ais.stopBuffer())); - } else { - break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions) - //throw new UnknownInstructionCode(instructionCode); - } - } - } - - public void compact() { - if (code instanceof ArrayList) { - ((ArrayList) code).trimToSize(); - } - } - - public byte[] getBytes() { - return getBytes(null); - } - - public byte[] getBytes(byte[] origBytes) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - OutputStream cos; - if ((origBytes != null) && (DEBUG_REWRITE)) { - ByteArrayInputStream origis = new ByteArrayInputStream(origBytes); - cos = new CopyOutputStream(bos, origis); - } else { - cos = bos; - } - try { - for (AVM2Instruction instruction : code) { - cos.write(instruction.getBytes()); - } - } catch (IOException ex) { - } - return bos.toByteArray(); - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - for (AVM2Instruction instruction : code) { - s.append(instruction.toString()); - s.append("\r\n"); - } - return s.toString(); - } - - public GraphTextWriter toString(GraphTextWriter writer, LocalData localData) { - int i = 0; - for (AVM2Instruction instruction : code) { - writer.appendNoHilight(Helper.formatAddress(i)); - writer.appendNoHilight(" "); - instruction.toString(writer, localData).newLine(); - i++; - } - return writer; - } - - public GraphTextWriter toASMSource(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, ScriptExportMode exportMode, GraphTextWriter writer) { - return toASMSource(constants, trait, info, body, new ArrayList(), exportMode, writer); - } - - public GraphTextWriter toASMSource(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, List outputMap, ScriptExportMode exportMode, GraphTextWriter writer) { - invalidateCache(); - if (trait != null) { - if (trait instanceof TraitFunction) { - TraitFunction tf = (TraitFunction) trait; - writer.appendNoHilight("trait "); - writer.hilightSpecial("function ", "traittype"); - writer.hilightSpecial(constants.multinameToString(tf.name_index), "traitname"); - writer.appendNoHilight(" slotid "); - writer.hilightSpecial("" + tf.slot_id, "slotid"); - writer.newLine(); - } - if (trait instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; - writer.appendNoHilight("trait "); - switch (tm.kindType) { - case Trait.TRAIT_METHOD: - writer.hilightSpecial("method ", "traittype"); - break; - case Trait.TRAIT_GETTER: - writer.hilightSpecial("getter ", "traittype"); - break; - case Trait.TRAIT_SETTER: - writer.hilightSpecial("setter ", "traittype"); - break; - } - writer.hilightSpecial(constants.multinameToString(tm.name_index), "traitname"); - writer.appendNoHilight(" dispid "); - writer.hilightSpecial("" + tm.disp_id, "dispid"); - writer.newLine(); - } - } - if (info != null) { - writer.appendNoHilight("method").newLine(); - writer.appendNoHilight("name "); - writer.hilightSpecial(info.name_index == 0 ? "null" : "\"" + Helper.escapeString(info.getName(constants)) + "\"", "methodname"); - writer.newLine(); - if (info.flagExplicit()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("EXPLICIT", "flag.EXPLICIT"); - writer.newLine(); - } - if (info.flagHas_optional()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("HAS_OPTIONAL", "flag.HAS_OPTIONAL"); - writer.newLine(); - writer.appendNoHilight("flag HAS_OPTIONAL").newLine(); - } - if (info.flagHas_paramnames()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("HAS_PARAM_NAMES", "flag.HAS_PARAM_NAMES"); - writer.newLine(); - } - if (info.flagIgnore_rest()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("EXPLICIT", "flag.IGNORE_REST"); - writer.newLine(); - } - if (info.flagNeed_activation()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_ACTIVATION", "flag.NEED_ACTIVATION"); - writer.newLine(); - } - if (info.flagNeed_arguments()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_ARGUMENTS", "flag.NEED_ARGUMENTS"); - writer.newLine(); - } - if (info.flagNeed_rest()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("NEED_REST", "flag.NEED_REST"); - writer.newLine(); - } - if (info.flagSetsdxns()) { - writer.appendNoHilight("flag "); - writer.hilightSpecial("SET_DXNS", "flag.SET_DXNS"); - writer.newLine(); - } - for (int p = 0; p < info.param_types.length; p++) { - writer.appendNoHilight("param "); - writer.hilightSpecial(constants.multinameToString(info.param_types[p]), "param", p); - writer.newLine(); - } - if (info.flagHas_paramnames()) { - for (int n : info.paramNames) { - writer.appendNoHilight("paramname "); - if (n == 0) { - writer.appendNoHilight("null"); - } else { - writer.appendNoHilight("\""); - writer.appendNoHilight(constants.getString(n)); - writer.appendNoHilight("\""); - } - writer.newLine(); - } - } - if (info.flagHas_optional()) { - for (int i = 0; i < info.optional.length; i++) { - ValueKind vk = info.optional[i]; - writer.appendNoHilight("optional "); - writer.hilightSpecial(vk.toString(constants), "optional", i); - writer.newLine(); - } - } - writer.appendNoHilight("returns "); - writer.hilightSpecial(constants.multinameToString(info.ret_type), "returns"); - writer.newLine(); - } - writer.newLine(); - writer.appendNoHilight("body").newLine(); - - writer.appendNoHilight("maxstack "); - writer.appendNoHilight(body.max_stack); - writer.newLine(); - - writer.appendNoHilight("localcount "); - writer.appendNoHilight(body.max_regs); - writer.newLine(); - - writer.appendNoHilight("initscopedepth "); - writer.appendNoHilight(body.init_scope_depth); - writer.newLine(); - - writer.appendNoHilight("maxscopedepth "); - writer.appendNoHilight(body.max_scope_depth); - writer.newLine(); - - List offsets = new ArrayList<>(); - for (int e = 0; e < body.exceptions.length; e++) { - writer.appendNoHilight("try"); - - writer.appendNoHilight(" from "); - writer.appendNoHilight("ofs"); - writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].start)); - offsets.add((long) body.exceptions[e].start); - - writer.appendNoHilight(" to "); - writer.appendNoHilight("ofs"); - writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].end)); - offsets.add((long) body.exceptions[e].end); - - writer.appendNoHilight(" target "); - writer.appendNoHilight("ofs"); - writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].target)); - offsets.add((long) body.exceptions[e].target); - - writer.appendNoHilight(" type "); - writer.hilightSpecial(body.exceptions[e].type_index == 0 ? "null" : constants.getMultiname(body.exceptions[e].type_index).toString(constants, new ArrayList()), "try.type", e); - - writer.appendNoHilight(" name "); - writer.hilightSpecial(body.exceptions[e].name_index == 0 ? "null" : constants.getMultiname(body.exceptions[e].name_index).toString(constants, new ArrayList()), "try.name", e); - writer.newLine(); - } - - writer.newLine(); - writer.appendNoHilight("code").newLine(); - - for (AVM2Instruction ins : code) { - offsets.addAll(ins.getOffsets()); - } - for (AVM2Instruction ins : code) { - if (ins.replaceWith != null) { - for (Object o : ins.replaceWith) { - if (o instanceof ControlFlowTag) { - ControlFlowTag cft = (ControlFlowTag) o; - if (cft.name.equals("appendjump")) { - offsets.add((long) pos2adr(cft.value)); - } - } - } - } - } - long ofs = 0; - int ip = 0; - int largeLimit = 20000; - boolean markOffsets = code.size() <= largeLimit; - - if (exportMode == ScriptExportMode.HEX) { - Helper.byteArrayToHexWithHeader(writer, getBytes()); - } else { - for (AVM2Instruction ins : code) { - if (exportMode == ScriptExportMode.PCODE_HEX) { - writer.appendNoHilight("; "); - writer.appendNoHilight(Helper.bytesToHexString(ins.getBytes())); - writer.newLine(); - } - if (ins.labelname != null) { - writer.appendNoHilight(ins.labelname + ":"); - } else if (Configuration.showAllAddresses.get() || offsets.contains(ofs)) { - writer.appendNoHilight("ofs" + Helper.formatAddress(ofs) + ":"); - } - /*for (int e = 0; e < body.exceptions.length; e++) { - if (body.exceptions[e].start == ofs) { - ret.append("exceptionstart " + e + ":"); - } - if (body.exceptions[e].end == ofs) { - ret.append("exceptionend " + e + ":"); - } - if (body.exceptions[e].target == ofs) { - ret.append("exceptiontarget " + e + ":"); - } - }*/ - if (ins.replaceWith != null) { - for (Object o : ins.replaceWith) { - if (o instanceof Integer) { - AVM2Instruction ins2 = code.get((Integer) o); - if (ins2.isIgnored()) { - continue; - } - writer.append("", ins2.mappedOffset > -1 ? ins2.mappedOffset : ofs); - writer.appendNoHilight(ins2.toStringNoAddress(constants, new ArrayList()) + " ;copy from " + Helper.formatAddress(pos2adr((Integer) o))); - writer.newLine(); - outputMap.add((Integer) o); - } else if (o instanceof ControlFlowTag) { - ControlFlowTag cft = (ControlFlowTag) o; - if (cft.name.equals("appendjump")) { - writer.appendNoHilight("jump ofs" + Helper.formatAddress(pos2adr(cft.value))).newLine(); - outputMap.add(-1); - } - if (cft.name.equals("mark")) { - writer.appendNoHilight("ofs" + Helper.formatAddress(pos2adr(cft.value)) + ":"); - } - } - } - } else { - if (!ins.isIgnored()) { - if (markOffsets) { - writer.append("", ins.mappedOffset > -1 ? ins.mappedOffset : ofs); - } - int fixBranch = ins.getFixBranch(); - if (fixBranch > -1) { - if (ins.definition instanceof IfTypeIns) { - for (int i = 0; i < -ins.definition.getStackDelta(ins, null/*IfTypeIns do not require ABCs*/); i++) { - writer.appendNoHilight(new DeobfuscatePopIns().instructionName).newLine(); - } - if (fixBranch == 0) { //jump - writer.appendNoHilight(new JumpIns().instructionName + " ofs" + Helper.formatAddress(ofs + ins.getBytes().length + ins.operands[0])); - } else { - //nojump, ignore - } - } - //TODO: lookupswitch ? - } else { - if (ins.changeJumpTo > -1) { - writer.appendNoHilight(ins.definition.instructionName + " ofs" + Helper.formatAddress(pos2adr(ins.changeJumpTo))); - } else { - writer.appendNoHilight(ins.toStringNoAddress(constants, new ArrayList())); - } - } - writer.newLine(); - outputMap.add(ip); - } - } - ofs += ins.getBytes().length; - ip++; - } - } - return writer; - } - private boolean cacheActual = false; - private List posCache; - - private void buildCache() { - posCache = new ArrayList<>(); - long a = 0; - for (int i = 0; i < code.size(); i++) { - posCache.add(a); - a += code.get(i).getBytes().length; - } - posCache.add(a); - cacheActual = true; - } - - public int adr2pos(long address) throws ConvertException { - if (!cacheActual) { - buildCache(); - } - int ret = posCache.indexOf(address); - if (ret == -1) { - throw new ConvertException("Bad jump try conver ofs" + Helper.formatAddress(address) + " ", -1); - } - return ret; - } - - public int pos2adr(int pos) { - if (!cacheActual) { - buildCache(); - } - return posCache.get(pos).intValue(); - } - - public void invalidateCache() { - cacheActual = false; - } - private List unknownJumps; - private List ignoredIns; - boolean isCatched = false; - - public boolean isKilled(int regName, int start, int end) { - for (int k = start; k <= end; k++) { - if (code.get(k).definition instanceof KillIns) { - if (code.get(k).operands[0] == regName) { - return true; - } - } - } - return false; - } - private int toSourceCount = 0; - - public HashMap getLocalRegNamesFromDebug(ABC abc) { - HashMap localRegNames = new HashMap<>(); - for (AVM2Instruction ins : code) { - if (ins.definition instanceof DebugIns) { - if (ins.operands[0] == 1) { - localRegNames.put(ins.operands[2] + 1, abc.constants.getString(ins.operands[1])); - } - } - } - return localRegNames; - } - - public List clearTemporaryRegisters(List output) { - for (int i = 0; i < output.size(); i++) { - if (output.get(i) instanceof SetLocalAVM2Item) { - if (isKilled(((SetLocalAVM2Item) output.get(i)).regIndex, 0, code.size() - 1)) { - SetLocalAVM2Item lsi = (SetLocalAVM2Item) output.get(i); - if (i + 1 < output.size()) { - if (output.get(i + 1) instanceof ReturnValueAVM2Item) { - ReturnValueAVM2Item rv = (ReturnValueAVM2Item) output.get(i + 1); - if (rv.value instanceof LocalRegAVM2Item) { - LocalRegAVM2Item lr = (LocalRegAVM2Item) rv.value; - if (lr.regIndex == lsi.regIndex) { - rv.value = lsi.value; - } - } - } - } - output.remove(i); - i--; - } - } else if (output.get(i) instanceof WithAVM2Item) { - clearTemporaryRegisters(((WithAVM2Item) output.get(i)).items); - } - } - return output; - } - - public int fixIPAfterDebugLine(int ip) { - if (code.isEmpty()) { - return ip; - } - if (ip >= code.size()) { - return code.size() - 1; - } - while (code.get(ip).definition instanceof DebugLineIns) { - ip++; - } - return ip; - } - - public int fixAddrAfterDebugLine(int addr) throws ConvertException { - return pos2adr(fixIPAfterDebugLine(adr2pos(addr))); - } - - public ConvertOutput toSourceOutput(String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, Stack scopeStack, ABC abc, ConstantPool constants, List method_info, MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { - boolean debugMode = DEBUG_MODE; - if (debugMode) { - System.out.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); - } - if (visited == null) { - visited = new boolean[code.size()]; - } - //if(true) return ""; - toSourceCount++; - if (toSourceLimit > 0) { - if (toSourceCount > toSourceLimit) { - throw new ConvertException("Limit of subs(" + toSourceLimit + ") was reached", start); - } - } - List output = new ArrayList<>(); - String ret = ""; - int ip = start; - //try { - //int addr; - iploop: - while (ip <= end) { - - if (ignoredIns.contains(ip)) { - ip++; - continue; - } - boolean processTry = processJumps; - //addr = pos2adr(ip); - int ipfix = fixIPAfterDebugLine(ip); - //int addrfix = pos2adr(ipfix); - int maxend = -1; - - if (ip > end) { - break; - } - - if (unknownJumps.contains(ip)) { - unknownJumps.remove(Integer.valueOf(ip)); - throw new UnknownJumpException(stack, ip, output); - } - if (visited[ip]) { - Logger.getLogger(AVM2Code.class.getName()).warning("Code already visited, ofs:" + Helper.formatAddress(pos2adr(ip)) + ", ip:" + ip); - break; - } - visited[ip] = true; - AVM2Instruction ins = code.get(ip); - if (debugMode) { - System.err.println("translating ip " + ip + " ins " + ins.toString() + " stack:" + stack.toString() + " scopeStack:" + scopeStack.toString()); - } - if (ins.definition instanceof NewFunctionIns) { - if (ip + 1 <= end) { - if (code.get(ip + 1).definition instanceof PopIns) { - ip += 2; - continue; - } - } - } - /*if ((ip + 8 < code.size())) { //return in finally clause - if (ins.definition instanceof SetLocalTypeIns) { - if (code.get(ip + 1).definition instanceof PushByteIns) { - AVM2Instruction jmp = code.get(ip + 2); - if (jmp.definition instanceof JumpIns) { - if (jmp.operands[0] == 0) { - if (code.get(ip + 3).definition instanceof LabelIns) { - if (code.get(ip + 4).definition instanceof PopIns) { - if (code.get(ip + 5).definition instanceof LabelIns) { - AVM2Instruction gl = code.get(ip + 6); - if (gl.definition instanceof GetLocalTypeIns) { - if (((GetLocalTypeIns) gl.definition).getRegisterId(gl) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins)) { - AVM2Instruction ki = code.get(ip + 7); - if (ki.definition instanceof KillIns) { - if (ki.operands[0] == ((SetLocalTypeIns) ins.definition).getRegisterId(ins)) { - if (code.get(ip + 8).definition instanceof ReturnValueIns) { - ip = ip + 8; - continue; - } - } - } - } - } - } - } - } - } - } - } - } - }//*/ - - /*if ((ip + 2 < code.size()) && (ins.definition instanceof NewCatchIns)) { //Filling local register in catch clause - if (code.get(ip + 1).definition instanceof DupIns) { - if (code.get(ip + 2).definition instanceof SetLocalTypeIns) { - ins.definition.translate(isStatic, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames); - ip += 3; - continue; - } - } - }*/ - if ((ins.definition instanceof GetLocalTypeIns) && (!output.isEmpty()) && (output.get(output.size() - 1) instanceof SetLocalAVM2Item) && (((SetLocalAVM2Item) output.get(output.size() - 1)).regIndex == ((GetLocalTypeIns) ins.definition).getRegisterId(ins)) && isKilled(((SetLocalAVM2Item) output.get(output.size() - 1)).regIndex, start, end)) { - SetLocalAVM2Item slt = (SetLocalAVM2Item) output.remove(output.size() - 1); - stack.push(slt.getValue()); - ip++; - } else if ((ins.definition instanceof SetLocalTypeIns) && (ip + 1 <= end) && (isKilled(((SetLocalTypeIns) ins.definition).getRegisterId(ins), ip, end))) { //set_local_x,get_local_x..kill x - AVM2Instruction insAfter = code.get(ip + 1); - if ((insAfter.definition instanceof GetLocalTypeIns) && (((GetLocalTypeIns) insAfter.definition).getRegisterId(insAfter) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins))) { - GraphTargetItem before = stack.peek(); - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - stack.push(before); - ip += 2; - continue iploop; - } else { - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - ip++; - continue iploop; - } - } else if (ins.definition instanceof DupIns) { - int nextPos; - do { - AVM2Instruction insAfter = code.get(ip + 1); - AVM2Instruction insBefore = ins; - if (ip - 1 >= start) { - insBefore = code.get(ip - 1); - } - if (insAfter.definition instanceof ConvertBIns) { //SWF compiled with debug contain convert_b - ip++; - //addr = pos2adr(ip); - insAfter = code.get(ip + 1); - } - - boolean isAnd; - if (processJumps && (insAfter.definition instanceof IfFalseIns)) { - //stack.add("(" + stack.pop() + ")&&"); - isAnd = true; - } else if (processJumps && (insAfter.definition instanceof IfTrueIns)) { - //stack.add("(" + stack.pop() + ")||"); - isAnd = false; - } else if (insAfter.definition instanceof SetLocalTypeIns) { - //chained assignments - int reg = (((SetLocalTypeIns) insAfter.definition).getRegisterId(insAfter)); - for (int t = ip + 1; t <= end - 1; t++) { - if (code.get(t).definition instanceof KillIns) { - if (code.get(t).operands[0] == reg) { - break; - } - } - if (code.get(t).definition instanceof GetLocalTypeIns) { - if (((GetLocalTypeIns) code.get(t).definition).getRegisterId(code.get(t)) == reg) { - if (code.get(t + 1).definition instanceof KillIns) { - if (code.get(t + 1).operands[0] == reg) { - ConvertOutput assignment = toSourceOutput(path, part, processJumps, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames, visited, localRegAssigmentIps, refs); - GraphTargetItem tar = assignment.output.remove(assignment.output.size() - 1); - tar.firstPart = part; - stack.push(tar); - ip = t + 2; - continue iploop; - } - } - } - } - } - if (!isKilled(reg, 0, end)) { - for (int i = ip; i >= start; i--) { - if (code.get(i).definition instanceof DupIns) { - if (stack.isEmpty()) { - break;//FIXME?o - } - GraphTargetItem v = stack.pop(); - stack.push(new LocalRegAVM2Item(ins, reg, v)); - stack.push(v); - } else { - break; - } - } - } else { - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - } - ip++; - break; - //} - - } else { - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - ip++; - break; - //throw new ConvertException("Unknown pattern after DUP:" + insComparsion.toString()); - } - } while (ins.definition instanceof DupIns); - } else if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - //ip = end + 1; - break; - } else if (ins.definition instanceof NewFunctionIns) { - String functionName = ""; - if ((ip >= start + 2) && (ip <= end - 4)) { - AVM2Instruction prev2 = code.get(ip - 2); - if (prev2.definition instanceof NewObjectIns) { - if (prev2.operands[0] == 0) { - if (code.get(ip - 1).definition instanceof PushWithIns) { - boolean hasDup = false; - int plus = 0; - if (code.get(ip + 1).definition instanceof DupIns) { - hasDup = true; - plus = 1; - } - AVM2Instruction psco = code.get(ip + 1 + plus); - if (psco.definition instanceof GetScopeObjectIns) { - if (psco.operands[0] == scopeStack.size() - 1) { - if (code.get(ip + plus + 2).definition instanceof SwapIns) { - if (code.get(ip + plus + 4).definition instanceof PopScopeIns) { - if (code.get(ip + plus + 3).definition instanceof SetPropertyIns) { - functionName = abc.constants.getMultiname(code.get(ip + plus + 3).operands[0]).getName(constants, fullyQualifiedNames); - scopeStack.pop();//with - output.remove(output.size() - 1); //with - ip = ip + plus + 4; //+1 below - } - } - } - } - } - } - } - } - } - //What to do when hasDup is false? - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - NewFunctionAVM2Item nft = (NewFunctionAVM2Item) stack.peek(); - nft.functionName = functionName; - ip++; - } else { - try { - ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); - } catch (RuntimeException re) { - /*String last=""; - int len=5; - for(int i=(ip-len<0?0:ip-len);i maxRegister) { - maxRegister = regId; - } - } - return maxRegister + 1; - } - - public HashMap getLocalRegTypes(ConstantPool constants, List fullyQualifiedNames) { - HashMap ret = new HashMap<>(); - AVM2Instruction prev = null; - for (AVM2Instruction ins : code) { - if (ins.definition instanceof SetLocalTypeIns) { - if (prev != null) { - if (prev.definition instanceof CoerceOrConvertTypeIns) { - ret.put(((SetLocalTypeIns) ins.definition).getRegisterId(ins), ((CoerceOrConvertTypeIns) prev.definition).getTargetType(constants, prev, fullyQualifiedNames)); - } - } - } - prev = ins; - } - return ret; - } - - private class Slot { - - public GraphTargetItem scope; - public Multiname multiname; - - public Slot(GraphTargetItem scope, Multiname multiname) { - this.scope = scope; - this.multiname = multiname; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Slot) { - return (((Slot) obj).scope.getThroughRegister() == scope.getThroughRegister()) - && (((Slot) obj).multiname == multiname); - } - return false; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 59 * hash + (this.scope != null ? this.scope.hashCode() : 0); - hash = 59 * hash + (this.multiname != null ? this.multiname.hashCode() : 0); - return hash; - } - } - - public void initToSource() { - toSourceCount = 0; - unknownJumps = new ArrayList<>(); - ignoredIns = new ArrayList<>(); - } - - public List toGraphTargetItems(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, ConstantPool constants, List method_info, MethodBody body, HashMap localRegNames, Stack scopeStack, boolean isStaticInitializer, List fullyQualifiedNames, Traits initTraits, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs) throws InterruptedException { - initToSource(); - List list; - HashMap localRegs = new HashMap<>(); - - int regCount = getRegisterCount(); - - //try { - list = AVM2Graph.translateViaGraph(path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs); - - if (initTraits != null) { - for (int i = 0; i < list.size(); i++) { - GraphTargetItem ti = list.get(i); - if ((ti instanceof InitPropertyAVM2Item) || (ti instanceof SetPropertyAVM2Item)) { - int multinameIndex = 0; - GraphTargetItem value = null; - if (ti instanceof InitPropertyAVM2Item) { - multinameIndex = ((InitPropertyAVM2Item) ti).propertyName.multinameIndex; - value = ((InitPropertyAVM2Item) ti).value; - } - if (ti instanceof SetPropertyAVM2Item) { - multinameIndex = ((FullMultinameAVM2Item) ((SetPropertyAVM2Item) ti).propertyName).multinameIndex; - value = ((SetPropertyAVM2Item) ti).value; - } - for (Trait t : initTraits.traits) { - if (t.name_index == multinameIndex) { - if ((t instanceof TraitSlotConst)) { - if (((TraitSlotConst) t).isConst() || isStaticInitializer) { - ((TraitSlotConst) t).assignedValue = value; - list.remove(i); - i--; - continue; - } - break; - } - } - } - } else { - //In obfuscated code, SetLocal instructions comes first - //break; - } - } - } - if (isStaticInitializer) { - List newList = new ArrayList<>(); - for (GraphTargetItem ti : list) { - if (!(ti instanceof ReturnVoidAVM2Item)) { - if (!(ti instanceof InitPropertyAVM2Item)) { - if (!(ti instanceof SetPropertyAVM2Item)) { - newList.add(ti); - } - } - } - } - list = newList; - if (list.isEmpty()) { - return list; - } - } - //Declarations - boolean[] declaredRegisters = new boolean[regCount]; - for (int b = 0; b < declaredRegisters.length; b++) { - declaredRegisters[b] = false; - } - List declaredSlots = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) { - GraphTargetItem ti = list.get(i); - if (ti instanceof SetLocalAVM2Item) { - int reg = ((SetLocalAVM2Item) ti).regIndex; - if (!declaredRegisters[reg]) { - list.set(i, new DeclarationAVM2Item(ti)); - declaredRegisters[reg] = true; - } - } - if (ti instanceof SetSlotAVM2Item) { - SetSlotAVM2Item ssti = (SetSlotAVM2Item) ti; - Slot sl = new Slot(ssti.scope, ssti.slotName); - if (!declaredSlots.contains(sl)) { - GraphTargetItem type = TypeItem.UNBOUNDED; - for (int t = 0; t < body.traits.traits.size(); t++) { - if (body.traits.traits.get(t).getName(abc) == sl.multiname) { - if (body.traits.traits.get(t) instanceof TraitSlotConst) { - type = PropertyAVM2Item.multinameToType(((TraitSlotConst) body.traits.traits.get(t)).type_index,abc.constants); - } - } - } - list.set(i, new DeclarationAVM2Item(ti, type)); - declaredSlots.add(sl); - } - } - } - - int lastPos = list.size() - 1; - if (lastPos < 0) { - lastPos = 0; - } - if ((list.size() > lastPos) && (list.get(lastPos) instanceof ScriptEndItem)) { - lastPos--; - } - if (lastPos < 0) { - lastPos = 0; - } - if ((list.size() > lastPos) && (list.get(lastPos) instanceof ReturnVoidAVM2Item)) { - list.remove(lastPos); - } - - return list; - } - - public void removeInstruction(int pos, MethodBody body) { - if ((pos < 0) || (pos >= code.size())) { - throw new IndexOutOfBoundsException(); - } - int byteCount = code.get(pos).getBytes().length; - long remOffset = code.get(pos).offset; - for (int i = pos + 1; i < code.size(); i++) { - code.get(i).offset -= byteCount; - } - - for (ABCException ex : body.exceptions) { - if (ex.start > remOffset) { - ex.start -= byteCount; - } - if (ex.end > remOffset) { - ex.end -= byteCount; - } - if (ex.target > remOffset) { - ex.target -= byteCount; - } - } - - for (int i = 0; i < pos; i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target > remOffset) { - code.get(i).operands[0] -= byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target > remOffset) { - code.get(i).operands[k] -= byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target > remOffset) { - code.get(i).operands[j] -= byteCount; - } - } - } - } - } - for (int i = pos + 1; i < code.size(); i++) { - if (code.get(i).definition instanceof LookupSwitchIns) { - long target = code.get(i).offset + code.get(i).operands[0]; - if (target < remOffset) { - code.get(i).operands[0] += byteCount; - } - for (int k = 2; k < code.get(i).operands.length; k++) { - target = code.get(i).offset + code.get(i).operands[k]; - if (target < remOffset) { - code.get(i).operands[k] += byteCount; - } - } - } else { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target < remOffset) { - code.get(i).operands[j] += byteCount; - } - } - } - } - } - - code.remove(pos); - invalidateCache(); - } - - public void insertInstruction(int pos, AVM2Instruction instruction) { - if (pos < 0) { - pos = 0; - } - if (pos > code.size()) { - pos = code.size(); - } - int byteCount = instruction.getBytes().length; - if (pos == code.size()) { - instruction.offset = code.get(pos - 1).offset + code.get(pos - 1).getBytes().length; - } else { - instruction.offset = code.get(pos).offset; - } - - for (int i = 0; i < pos; i++) { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target >= instruction.offset) { - code.get(i).operands[j] += byteCount; - } - } - } - } - for (int i = pos; i < code.size(); i++) { - for (int j = 0; j < code.get(i).definition.operands.length; j++) { - if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { - long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; - if (target < instruction.offset) { - code.get(i).operands[j] -= byteCount; - } - } - } - } - - for (int i = pos + 1; i < code.size(); i++) { - code.get(i).offset += byteCount; - } - code.add(pos, instruction); - } - - @SuppressWarnings("unchecked") - private static AVM2LocalData prepareBranchLocalData(AVM2LocalData localData) { - AVM2LocalData ret = new AVM2LocalData(); - ret.isStatic = localData.isStatic; - ret.classIndex = localData.classIndex; - ret.localRegs = new HashMap<>(localData.localRegs); - ret.scopeStack = (Stack) (localData.scopeStack).clone(); - ret.constants = localData.constants; - ret.methodInfo = localData.methodInfo; - ret.methodBody = localData.methodBody; - ret.abc = localData.abc; - ret.localRegNames = localData.localRegNames; - ret.fullyQualifiedNames = localData.fullyQualifiedNames; - ret.parsedExceptions = localData.parsedExceptions; - ret.finallyJumps = localData.finallyJumps; - ret.ignoredSwitches = localData.ignoredSwitches; - ret.scriptIndex = localData.scriptIndex; - ret.localRegAssignmentIps = localData.localRegAssignmentIps; - ret.ip = localData.ip; - ret.refs = localData.refs; - ret.code = localData.code; - return ret; - } - - public int removeTraps(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, ABC abc, int scriptIndex, int classIndex, boolean isStatic, String path) throws InterruptedException { - removeDeadCode(constants, trait, info, body); - AVM2LocalData localData = new AVM2LocalData(); - localData.isStatic = isStatic; - localData.classIndex = classIndex; - localData.localRegs = new HashMap<>(); - localData.scopeStack = new Stack<>(); - localData.constants = abc.constants; - localData.methodInfo = abc.method_info; - localData.methodBody = body; - localData.abc = abc; - localData.localRegNames = body.getLocalRegNames(abc); - localData.fullyQualifiedNames = new ArrayList<>(); - localData.parsedExceptions = new ArrayList<>(); - localData.finallyJumps = new ArrayList<>(); - localData.ignoredSwitches = new ArrayList<>(); - localData.scriptIndex = scriptIndex; - localData.localRegAssignmentIps = new HashMap<>(); - localData.ip = 0; - HashMap> refs = visitCode(body); - localData.refs = refs; - localData.code = this; - int ret = 0; - ret += removeTraps(constants, trait, info, body, localData, new AVM2GraphSource(this, false, -1, -1, new HashMap(), new Stack(), abc, body, new HashMap(), new ArrayList(), new HashMap(), refs), 0, path, refs); - removeIgnored(constants, trait, info, body); - removeDeadCode(constants, trait, info, body); - - return ret; - } - - private void handleRegister(CodeStats stats, int reg) { - if (reg + 1 > stats.maxlocal) { - stats.maxlocal = reg + 1; - } - } - - private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc) { - while (pos < code.size()) { - AVM2Instruction ins = code.get(pos); - if (stats.instructionStats[pos].seen) { - //check stack mismatch here - return true; - } - - if (ins.definition instanceof NewFunctionIns) { - MethodBody innerBody = abc.findBody(ins.operands[0]); - innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false); - } - - stats.instructionStats[pos].seen = true; - stats.instructionStats[pos].stackpos = stack; - stats.instructionStats[pos].scopepos = scope; - - int stackDelta = ins.definition.getStackDelta(ins, abc); - int scopeDelta = ins.definition.getScopeStackDelta(ins, abc); - int oldStack = stack; - - //+" deltaScope:"+(scopeDelta>0?"+"+scopeDelta:scopeDelta)+" stack:"+stack+" scope:"+scope); - stack += stackDelta; - scope += scopeDelta; - - stats.instructionStats[pos].stackpos_after = stack; - stats.instructionStats[pos].scopepos_after = scope; - - if (stack > stats.maxstack) { - stats.maxstack = stack; - } - if (scope > stats.maxscope) { - stats.maxscope = scope; - } - - //System.out.println("stack "+oldStack+(stackDelta>=0?"+"+stackDelta:stackDelta)+" max:"+stats.maxstack+" "+ins); - if ((ins.definition instanceof DXNSIns) || (ins.definition instanceof DXNSLateIns)) { - stats.has_set_dxns = true; - } - if (ins.definition instanceof NewActivationIns) { - stats.has_activation = true; - } - if (ins.definition instanceof SetLocalTypeIns) { - handleRegister(stats, ((SetLocalTypeIns) ins.definition).getRegisterId(ins)); - } else if (ins.definition instanceof GetLocalTypeIns) { - handleRegister(stats, ((GetLocalTypeIns) ins.definition).getRegisterId(ins)); - } else { - for (int i = 0; i < ins.definition.operands.length; i++) { - if (ins.definition.operands[i] == DAT_REGISTER_INDEX) { - handleRegister(stats, ins.operands[i]); - } - } - } - if (ins.definition instanceof ReturnValueIns) { - //check stack=1 - return true; - } - if (ins.definition instanceof ReturnVoidIns) { - //check stack=0 - return true; - } - if (ins.definition instanceof JumpIns) { - try { - pos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); - continue; - } catch (ConvertException ex) { - return false; - } - } else if (ins.definition instanceof IfTypeIns) { - try { - int newpos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); - walkCode(stats, newpos, stack, scope, abc); - } catch (ConvertException ex) { - return false; - } - } - if (ins.definition instanceof LookupSwitchIns) { - for (int i = 0; i < ins.operands.length; i++) { - if (i == 1) { - continue; - } - try { - int newpos = adr2pos(pos2adr(pos) + ins.operands[i]); - if (!walkCode(stats, newpos, stack, scope, abc)) { - return false; - } - } catch (ConvertException ex) { - return false; - } - } - } - pos++; - } - return true; - } - - public CodeStats getStats(ABC abc, MethodBody body, int initScope) { - CodeStats stats = new CodeStats(this); - stats.initscope = initScope; - if (!walkCode(stats, 0, 0, initScope, abc)) { - return null; - } - int scopePos = -1; - int prevStart = 0; - for (int e = 0; e < body.exceptions.length; e++) { - ABCException ex = body.exceptions[e]; - try { - if (scopePos == -1) { - scopePos = stats.instructionStats[adr2pos(ex.end) - 1].scopepos_after; - } - List visited = new ArrayList<>(); - for (int i = 0; i < stats.instructionStats.length; i++) { - if (stats.instructionStats[i].seen) { - visited.add(i); - } - } - if (!walkCode(stats, adr2pos(ex.target), 1 + (ex.isFinally() ? 1 : 0), scopePos, abc)) { - return null; - } - int maxIp = 0; - //searching for visited instruction in second run which has maximum position - for (int i = 0; i < stats.instructionStats.length; i++) { - if (stats.instructionStats[i].seen && !visited.contains(i)) { - maxIp = i; - } - } - scopePos = stats.instructionStats[maxIp].scopepos_after; - int stackPos = stats.instructionStats[maxIp].stackpos_after; - int nextIp = maxIp + 1; - if (code.get(maxIp).definition instanceof JumpIns) { - nextIp = adr2pos(pos2adr(nextIp) + code.get(maxIp).operands[0]); - } - if(nextIp> refs) throws InterruptedException { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - while (ip < code.size()) { - if (!refs.containsKey(ip)) { - refs.put(ip, new ArrayList()); - } - refs.get(ip).add(lastIp); - lastIp = ip; - if (refs.get(ip).size() > 1) { - break; - } - AVM2Instruction ins = code.get(ip); - if (ins.definition instanceof ThrowIns) { - break; - } - if (ins.definition instanceof ReturnValueIns) { - break; - } - if (ins.definition instanceof ReturnVoidIns) { - break; - } - if (ins.definition instanceof LookupSwitchIns) { - try { - for (int i = 2; i < ins.operands.length; i++) { - visitCode(adr2pos(pos2adr(ip) + ins.operands[i]), ip, refs); - } - ip = adr2pos(pos2adr(ip) + ins.operands[0]); - continue; - } catch (ConvertException ex) { - } - } - if (ins.definition instanceof JumpIns) { - try { - ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]); - continue; - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } else if (ins.definition instanceof IfTypeIns) { - try { - visitCode(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), ip, refs); - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } - ip++; - }; - } - - public HashMap> visitCode(MethodBody body) throws InterruptedException { - HashMap> refs = new HashMap<>(); - for (int i = 0; i < code.size(); i++) { - refs.put(i, new ArrayList()); - } - visitCode(0, 0, refs); - int pos = 0; - for (ABCException e : body.exceptions) { - pos++; - try { - visitCode(adr2pos(e.start), adr2pos(e.start) - 1, refs); - visitCode(adr2pos(e.start), -1, refs); - visitCode(adr2pos(e.target), adr2pos(e.end), refs); - visitCode(adr2pos(e.end), -pos, refs); - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } - return refs; - } - - private int visitCodeTrap(int ip, int[] visited, AVM2Instruction prev, AVM2Instruction prev2) { - int ret = 0; - while (ip < visited.length) { - visited[ip]++; - if (visited[ip] > 1) { - break; - } - AVM2Instruction ins = code.get(ip); - if (ins.definition instanceof ThrowIns) { - break; - } - if (ins.definition instanceof ReturnValueIns) { - break; - } - if (ins.definition instanceof ReturnVoidIns) { - break; - } - if (ins.definition instanceof LookupSwitchIns) { - try { - for (int i = 2; i < ins.operands.length; i++) { - ret += visitCodeTrap(adr2pos(pos2adr(ip) + ins.operands[i]), visited, prev, prev2); - } - ip = adr2pos(pos2adr(ip) + ins.operands[0]); - prev2 = prev; - prev = ins; - continue; - } catch (ConvertException ex) { - } - } - if (ins.definition instanceof JumpIns) { - try { - ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]); - prev2 = prev; - prev = ins; - continue; - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } else if (ins.definition instanceof IfTypeIns) { - if ((prev != null) && (prev2 != null)) { - if ((prev.definition instanceof PushByteIns) && (prev2.definition instanceof PushByteIns)) { - if (ins.definition instanceof IfEqIns) { - prev.ignored = true; - prev2.ignored = true; - if (prev.operands[0] == prev2.operands[0]) { - ins.definition = new JumpIns(); - visited[ip]--; - } else { - ins.ignored = true; - ip++; - } - ret++; - continue; - } - if (ins.definition instanceof IfNeIns) { - prev.ignored = true; - prev2.ignored = true; - if (prev.operands[0] != prev2.operands[0]) { - ins.definition = new JumpIns(); - visited[ip]--; - } else { - ins.ignored = true; - ip++; - } - ret++; - continue; - } - } - } - if ((prev != null) && ins.definition instanceof IfTrueIns) { - if (prev.definition instanceof PushTrueIns) { - prev.ignored = true; - ins.definition = new JumpIns(); - visited[ip]--; - ret++; - continue; - } else if (prev.definition instanceof PushFalseIns) { - prev.ignored = true; - ins.ignored = true; - ret++; - ip++; - continue; - } - } - if ((prev != null) && ins.definition instanceof IfFalseIns) { - if (prev.definition instanceof PushFalseIns) { - prev.ignored = true; - ins.definition = new JumpIns(); - visited[ip]--; - ret++; - continue; - } else if (prev.definition instanceof PushTrueIns) { - prev.ignored = true; - ins.ignored = true; - ret++; - ip++; - continue; - } - } - try { - ret += visitCodeTrap(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), visited, prev, prev2); - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } - ip++; - prev2 = prev; - prev = ins; - }; - return ret; - } - - private static class ControlFlowTag { - - public String name; - public int value; - - public ControlFlowTag(String name, int value) { - this.name = name; - this.value = value; - } - } - - public void restoreControlFlow(int ip, HashMap> refs, int[] visited2, HashMap> appended) throws ConvertException { - List buf = new ArrayList<>(); - boolean cont = false; - int continueip = 0; - for (; ip < code.size(); ip++) { - AVM2Instruction ins = code.get(ip); - - if ((refs.containsKey(ip) && refs.get(ip).size() > 1) || (visited2[ip] > 0)) { - if (cont) { - buf.add(new ControlFlowTag("appendjump", ip)); - } - cont = false; - if (visited2[ip] > 0) { - break; - } - } - visited2[ip]++; - if (ins.definition instanceof LookupSwitchIns) { - - if (cont) { - buf.add(new ControlFlowTag("appendjump", ip)); - } - cont = false; - restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[0]), refs, visited2, appended); - for (int i = 2; i < ins.operands.length; i++) { - restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[i]), refs, visited2, appended); - } - break; - } - if (ins.definition instanceof JumpIns) { - int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); - - boolean allJumpsOrIfs = true; - for (int ref : refs.get(ip)) { - if (ref < 0) { - continue; - } - if (!(code.get(ref).definition instanceof JumpIns)) { - if (!(code.get(ref).definition instanceof IfTypeIns)) { - allJumpsOrIfs = false; - break; - } else { - if (adr2pos(pos2adr(ref + 1) + code.get(ref).operands[0]) != ip) { - allJumpsOrIfs = false; - break; - } - } - } - } - if (allJumpsOrIfs) { - for (int ref : refs.get(ip)) { - if (ref < 0) { - continue; - } - code.get(ref).changeJumpTo = newip; - } - } - if ((newip < code.size()) && (refs.containsKey(newip) && refs.get(newip).size() == 1)) { - if (!cont) { - continueip = ip; - buf = new ArrayList<>(); - appended.put(continueip, buf); - } - cont = true; - } else { - if (cont) { - buf.add(new ControlFlowTag("appendjump", newip)); - } - cont = false; - } - ip = newip - 1; - } else if (ins.definition instanceof IfTypeIns) { - int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); - if (cont) { - buf.add(new ControlFlowTag("appendjump", ip)); - } - cont = false; - restoreControlFlow(newip, refs, visited2, appended); - } else if ((ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ThrowIns)) { - if (cont) { - buf.add(ip); - } - break; - } else if (cont) { - buf.add(ip); - } - } - - } - - private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) throws InterruptedException { - try { - HashMap> refs; - int[] visited2 = new int[code.size()]; - refs = visitCode(body); - HashMap> appended = new HashMap<>(); - /*if (secondpass) { - restoreControlFlow(code.size() - 1, refs, visited2, appended); - } else*/ { - restoreControlFlow(0, refs, visited2, appended); - for (ABCException e : body.exceptions) { - try { - restoreControlFlow(adr2pos(e.start), refs, visited2, appended); - restoreControlFlow(adr2pos(e.target), refs, visited2, appended); - restoreControlFlow(adr2pos(e.end), refs, visited2, appended); - } catch (ConvertException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - } - } - for (int ip : appended.keySet()) { - code.get(ip).replaceWith = appended.get(ip); - } - } catch (ConvertException cex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, "Error during restore control flow", cex); - } - invalidateCache(); - try { - List outputMap = new ArrayList<>(); - HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); - toASMSource(constants, trait, info, body, outputMap, ScriptExportMode.PCODE, writer); - String src = writer.toString(); - - AVM2Code acode = ASM3Parser.parse(new StringReader(src), constants, null, body, info); - for (int i = 0; i < acode.code.size(); i++) { - if (outputMap.size() > i) { - int tpos = outputMap.get(i); - if (tpos == -1) { - } else if (code.get(tpos).mappedOffset >= 0) { - acode.code.get(i).mappedOffset = code.get(tpos).mappedOffset; - } else { - acode.code.get(i).mappedOffset = pos2adr(tpos); - } - - } - } - this.code = acode.code; - } catch (IOException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); - } catch (ParseException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); - } - invalidateCache(); - removeDeadCode(constants, trait, info, body); - } - - public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { - restoreControlFlowPass(constants, trait, info, body, false); - //restoreControlFlowPass(constants, body, true); - } - - /*private void removeIgnored(MethodBody body) { - for (int rem = code.size() - 1; rem >= 0; rem--) { - if (code.get(rem).ignored) { - removeInstruction(rem, body); - } - } - }*/ - public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { - try { - List outputMap = new ArrayList<>(); - HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); - toASMSource(constants, trait, info, body, outputMap, ScriptExportMode.PCODE, writer); - String src = writer.toString(); - AVM2Code acode = ASM3Parser.parse(new StringReader(src), constants, trait, body, info); - for (int i = 0; i < acode.code.size(); i++) { - if (outputMap.size() > i) { - int tpos = outputMap.get(i); - if (tpos == -1) { - } else if (code.get(tpos).mappedOffset >= 0) { - acode.code.get(i).mappedOffset = code.get(tpos).mappedOffset; - } else { - acode.code.get(i).mappedOffset = pos2adr(tpos); - } - } - } - this.code = acode.code; - } catch (IOException | ParseException ex) { - } - invalidateCache(); - } - - public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { - HashMap> refs = visitCode(body); - - int cnt = 0; - for (int i = code.size() - 1; i >= 0; i--) { - if (refs.get(i).isEmpty()) { - code.get(i).ignored = true; - //removeInstruction(i, body); - cnt++; - } - } - - removeIgnored(constants, trait, info, body); - for (int i = code.size() - 1; i >= 0; i--) { - AVM2Instruction ins = code.get(i); - if (ins.definition instanceof JumpIns) { - if (ins.operands[0] == 0) { - code.get(i).ignored = true; - //removeInstruction(i, body); - cnt++; - } - } - } - removeIgnored(constants, trait, info, body); - return cnt; - } - - public void markMappedOffsets() { - int ofs = 0; - for (int i = 0; i < code.size(); i++) { - code.get(i).mappedOffset = ofs; - ofs += code.get(i).getBytes().length; - } - } - - public AVM2Code deepCopy() { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { - oos.writeObject(this); - oos.flush(); - } - AVM2Code copy; - try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { - copy = (AVM2Code) ois.readObject(); - } - return copy; - } catch (IOException | ClassNotFoundException ex) { - Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, "Error during deepCopy", ex); - return null; - } - } - - private static class Decision { - - public boolean jumpUsed = false; - public boolean skipUsed = false; - public Set casesUsed = new HashSet<>(); - HashMap registers = new HashMap<>(); - } - - private static int getMostCommonIp(AVM2GraphSource code, List branches) { - List> reachable = new ArrayList<>(); - for (int i = 0; i < branches.size(); i++) { - List r = new ArrayList<>(); - getReachableIps(code, branches.get(i), r); - } - - int commonLevel; - Map levelMap = new HashMap<>(); - for (List first : reachable) { - int maxclevel = 0; - Set visited = new HashSet<>(); - for (Integer p : first) { - if (visited.contains(p)) { - continue; - } - visited.add(p); - boolean common = true; - commonLevel = 1; - for (List r : reachable) { - if (r == first) { - continue; - } - if (r.contains(p)) { - commonLevel++; - } - } - if (commonLevel <= maxclevel) { - continue; - } - maxclevel = commonLevel; - if (levelMap.containsKey(p)) { - if (levelMap.get(p) > commonLevel) { - commonLevel = levelMap.get(p); - } - } - levelMap.put(p, commonLevel); - if (common) { - //return p; - } - } - } - for (int i = reachable.size() - 1; i >= 2; i--) { - for (Integer p : levelMap.keySet()) { - if (levelMap.get(p) == i) { - return p; - } - } - } - for (Integer p : levelMap.keySet()) { - if (levelMap.get(p) == branches.size()) { - return p; - } - } - return -1; - } - - public static void getReachableIps(AVM2GraphSource code, int ip, List reachable) { - do { - if (reachable.contains(ip)) { - return; - } - reachable.add(ip); - GraphSourceItem ins = code.get(ip); - if (ins.isJump() || ins.isBranch()) { - List branches = ins.getBranches(code); - for (int i = 1; i < branches.size(); i++) { - getReachableIps(code, branches.get(i), reachable); - } - ip = branches.get(0); - continue; - } - ip++; - } while (ip < code.size()); - } - - public static boolean isDirectAncestor(int currentIp, int ancestor, HashMap> refs) { - return isDirectAncestor(currentIp, ancestor, refs, new ArrayList()); - } - - private static boolean isDirectAncestor(int currentIp, int ancestor, HashMap> refs, List visited) { - if (currentIp == -1) { - return true; - } - do { - if (currentIp == ancestor) { - return true; - } - if (currentIp == 0) { - return false; - } - if (visited.contains(currentIp)) { - return true; - } - visited.add(currentIp); - if (refs.containsKey(currentIp)) { - List currentRefs = refs.get(currentIp); - if ((currentRefs != null) && (!currentRefs.isEmpty())) { - for (int i = 1; i < currentRefs.size(); i++) { - if (!isDirectAncestor(currentRefs.get(i), ancestor, refs, visited)) { - return false; - } - } - currentIp = currentRefs.get(0); - continue; - } - } - currentIp--; - } while (currentIp >= 0); - return false; - } - - public static boolean getPreviousReachableIps(int currentIp, HashMap> refs, Set reachable, Set visited) { - do { - if (visited.contains(currentIp)) { - return false; - } - reachable.add(currentIp); - visited.add(currentIp); - if (refs.containsKey(currentIp)) { - List currentRefs = refs.get(currentIp); - if ((currentRefs != null) && (!currentRefs.isEmpty())) { - if (currentRefs.size() == 1) { - currentIp = currentRefs.get(0); - continue; - } - boolean r = false; - for (int i = 0; i < currentRefs.size(); i++) { - Set nr = new HashSet<>(); - boolean v = getPreviousReachableIps(currentRefs.get(i), refs, nr, visited); - if ((!v) || nr.contains(0)) { - reachable.addAll(nr); - } - r = r || v; - } - return r; - } - } - currentIp--; - } while (currentIp >= 0); - return true; - } - - @SuppressWarnings("unchecked") - private static int removeTraps(HashMap> refs, boolean secondPass, boolean indeterminate, AVM2LocalData localData, Stack stack, List output, AVM2GraphSource code, int ip, HashMap visited, HashMap> visitedStates, HashMap decisions, String path, int recursionLevel) throws InterruptedException { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - if (recursionLevel > code.size() + 1) { - throw new TranslateException("removeTraps max recursion level reached."); - } - boolean debugMode = false; - int ret = 0; - iploop: - while ((ip > -1) && ip < code.size()) { - - if (false) { //useVisited) { - if (visited.containsKey(ip)) { - break; - } - if (!visited.containsKey(ip)) { - visited.put(ip, 0); - } else { - visited.put(ip, visited.get(ip) + 1); - } - } else { - HashMap currentState = localData.localRegs; - - if (visitedStates.containsKey(ip)) { - HashMap lastState = visitedStates.get(ip); - if (lastState.equals(currentState)) { - break; - } - } - visitedStates.put(ip, (HashMap) currentState.clone()); - - } - - int curVisited; - if (!visited.containsKey(ip)) { - curVisited = 1; - } else { - curVisited = visited.get(ip) + 1; - } - visited.put(ip, curVisited); - - List r = refs.get(ip); - /*if (r != null) { - if (r.size() > 1) { - if (!stack.isEmpty()) { - GraphTargetItem it = stack.pop(); - stack.push(new NotCompileTimeAVM2Item(null, it)); - } - } - }*/ - - AVM2Instruction ins = code.get(ip); - //Errorneous code inserted by some obfuscators - if (ins.definition instanceof NewObjectIns) { - if (ins.operands[0] > stack.size()) { - ins.setIgnored(true, 0); - } - } - if (ins.definition instanceof NewArrayIns) { - if (ins.operands[0] > stack.size()) { - ins.setIgnored(true, 0); - } - } - - if (ins.isIgnored()) { - ip++; - continue; - } - - if (debugMode) { - System.out.println((indeterminate ? "useV " : "") + (secondPass ? "secondPass " : "") + "Visit " + ip + ": " + ins + " stack:" + stack.toString()); - HashMap registers = localData.localRegs; - System.out.print("Registers:"); - for (int reg : registers.keySet()) { - try { - System.out.print(" r" + reg + ": " + registers.get(reg).getResult()); - } catch (NullPointerException npe) { - System.out.print(" r" + reg + ": " + "null"); - } - } - System.out.println(""); - } - if (secondPass) { - /*if ((ins instanceof AVM2Instruction) && (((AVM2Instruction) ins).definition instanceof PopIns)) { - GraphTargetItem top = stack.peek(); - for (GraphSourceItemPos p : top.getNeededSources()) { - if (p == null) { - continue; - } - if (p.item == null) { - continue; - } - if (p.item.isIgnored()) { - ins.setIgnored(true, 0); - break; - } - } - }*/ - } - - if (((AVM2Instruction) ins).definition instanceof NewFunctionIns) { - stack.push(new BooleanAVM2Item(null, true)); - } else { - localData.ip = ip; - ins.translate(localData, stack, output, Graph.SOP_USE_STATIC, path); - } - - if (ins.definition instanceof SetLocalTypeIns) { - SetLocalTypeIns slt = (SetLocalTypeIns) ins.definition; - int regId = slt.getRegisterId(ins); - if (indeterminate) { - HashMap registers = localData.localRegs; - GraphTargetItem regVal = registers.get(regId); - if (regVal.isCompileTime()) { - registers.put(regId, new NotCompileTimeItem(null, regVal)); - } - } - } - - if (ins.isExit()) { - break; - } - - if (ins.isBranch() || ins.isJump()) { - List branches = ins.getBranches(code); - if (((AVM2Instruction) ins).definition instanceof IfTypeIns - && (!(((AVM2Instruction) ins).definition instanceof JumpIns)) - && (!stack.isEmpty()) - && (stack.peek().isCompileTime()) - && (!stack.peek().hasSideEffect())) { - boolean condition = EcmaScript.toBoolean(stack.peek().getResult()); - if (debugMode) { - if (condition) { - System.out.println("JUMP"); - } else { - System.out.println("SKIP"); - } - } - Decision dec = new Decision(); - if (decisions.containsKey(ins)) { - dec = decisions.get(ins); - } else { - decisions.put(ins, dec); - } - if (condition) { - dec.jumpUsed = true; - } else { - dec.skipUsed = true; - } - - if (branches.size() > 1) { - if (secondPass) { - if (condition && (dec.jumpUsed) && (!dec.skipUsed)) { - ins.setFixBranch(0); - //((AVM2Instruction) ins).definition = new JumpIns(); - } - if ((!condition) && (!dec.jumpUsed) && (dec.skipUsed)) { - ins.setFixBranch(1); - //ins.setIgnored(true, 0); - } - } - } - GraphTargetItem tar = stack.pop(); - /*if (secondPass && (dec.jumpUsed != dec.skipUsed)) { - for (GraphSourceItemPos pos : tar.getNeededSources()) { - if (pos.item instanceof AVM2Instruction) { - if (((AVM2Instruction) pos.item).definition instanceof DupIns) { - pos.item.setIgnored(true, 0); - break; - } - } - if (pos.item != ins) { - pos.item.setIgnored(true, 0); - } - } - - }*/ - if (branches.size() == 1) { - ip = branches.get(0); - } else { - ip = condition ? branches.get(0) : branches.get(1); - } - continue; - } else { - if (ins.isBranch() && (!ins.isJump())) { - GraphTargetItem top = stack.pop(); - - Decision dec = new Decision(); - if (decisions.containsKey(ins)) { - dec = decisions.get(ins); - } else { - decisions.put(ins, dec); - } - HashMap registers = localData.localRegs; - boolean regChanged = false; - if (!dec.registers.isEmpty()) { - if (dec.registers.size() != registers.size()) { - regChanged = true; - } else { - for (int reg : registers.keySet()) { - if (!dec.registers.containsKey(reg)) { - regChanged = true; - break; - } - if (!registers.get(reg).isCompileTime() && dec.registers.get(reg).isCompileTime()) { - regChanged = true; - break; - } - } - } - } - dec.registers.putAll(registers); - dec.jumpUsed = true; - dec.skipUsed = true; - - if (!regChanged && ((!(top instanceof HasNextAVM2Item) && curVisited > 1) || (curVisited > 2))) { - for (int b : branches) { - int visc = 0; - if (visited.containsKey(b)) { - visc = visited.get(b); - } - if (visc == 0) {// brStack = (Stack) stack.clone(); - if (b >= 0) { //useVisited || (!ins.isJump()) - ret += removeTraps(refs, secondPass, indeterminate, prepareBranchLocalData(localData), brStack, output, code, b, visited, visitedStates, decisions, path, recursionLevel + 1); - } else { - if (debugMode) { - System.out.println("Negative branch:" + b); - } - } - } - } - break; - } - ip++; - } - if (ip < 0) { - System.out.println("Visited Negative: " + ip); - } - return ret; - } - - public static int removeTraps(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, AVM2LocalData localData, AVM2GraphSource code, int addr, String path, HashMap> refs) throws InterruptedException { - HashMap decisions = new HashMap<>(); - removeTraps(refs, false, false, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), new HashMap(), new HashMap>(), decisions, path, 0); - int cnt = 0; - for (AVM2Instruction src : decisions.keySet()) { - Decision dec = decisions.get(src); - if (dec != null) { - if (((AVM2Instruction) src).definition instanceof LookupSwitchIns) { - AVM2Instruction asrc = (AVM2Instruction) src; - if (dec.casesUsed.size() == 1) { - for (int c : dec.casesUsed) { - asrc.setFixBranch(c); - cnt++; - } - } - } else { - if (dec.jumpUsed && !dec.skipUsed) { - src.setFixBranch(0); - cnt++; - } - if (!dec.jumpUsed && dec.skipUsed) { - src.setFixBranch(1); - cnt++; - } - } - } - } - //int cnt = removeTraps(refs, true, false, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), new HashMap(), new HashMap>(), decisions, path); - code.getCode().removeIgnored(constants, trait, info, body); - return cnt; - } - /*public static int removeTraps(AVM2LocalData localData, AVM2GraphSource code, int addr) { - AVM2Graph.translateViaGraph(localData, "", code, new ArrayList(), Graph.SOP_REMOVE_STATIC); - return 1; - }*/ -} +/* + * Copyright (C) 2010-2014 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.abc.avm2; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ABCInputStream; +import com.jpexs.decompiler.flash.abc.AVM2LocalData; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; +import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2GraphSource; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.IfTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Lf32Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Lf64Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li16Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li32Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Li8Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sf32Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sf64Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si16Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si32Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Si8Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi16Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi1Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.alchemy.Sxi8Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.AddIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DivideIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.ModuloIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.MultiplyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NegateIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.SubtractIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitAndIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitNotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitOrIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.BitXorIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.LShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.RShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.bitwise.URShiftIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.EqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.GreaterThanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.LessThanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.comparison.StrictEqualsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewArrayIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugFileIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallMethodIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallStaticIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallSuperVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfEqIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfGeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfGtIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfLeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfLtIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNGeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNGtIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNLeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNLtIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfNeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal1Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal2Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal3Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal1Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal2Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocal3Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindDefIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNextIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetGlobalSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushDoubleIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushFalseIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNamespaceIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNanIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushTrueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUIntIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeLateIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceOrConvertTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertOIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.InstanceOfIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.IsTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.IsTypeLateIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.TypeOfIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.DXNSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.DXNSLateIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.EscXAttrIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.EscXElemIns; +import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.HasNextAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InitPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewFunctionAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetLocalAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetSlotAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.DeclarationAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphPart; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.NotCompileTimeItem; +import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.ScriptEndItem; +import com.jpexs.helpers.Helper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class AVM2Code implements Serializable { + + public static final long serialVersionUID = 1L; + private static final boolean DEBUG_MODE = false; + public static int toSourceLimit = -1; + public List code = new ArrayList<>(); + public static boolean DEBUG_REWRITE = false; + public static final int OPT_U30 = 0x100; + public static final int OPT_U8 = 0x200; + public static final int OPT_S24 = 0x300; + public static final int OPT_CASE_OFFSETS = 0x400; + public static final int OPT_BYTE = 0x500; + public static final int DAT_MULTINAME_INDEX = OPT_U30 + 0x01; + public static final int DAT_ARG_COUNT = OPT_U30 + 0x02; + public static final int DAT_METHOD_INDEX = OPT_U30 + 0x03; + public static final int DAT_STRING_INDEX = OPT_U30 + 0x04; + public static final int DAT_DEBUG_TYPE = OPT_U8 + 0x05; + public static final int DAT_REGISTER_INDEX = OPT_U8 + 0x06; + public static final int DAT_LINENUM = OPT_U30 + 0x07; + public static final int DAT_LOCAL_REG_INDEX = OPT_U30 + 0x08; + public static final int DAT_SLOT_INDEX = OPT_U30 + 0x09; + public static final int DAT_SLOT_SCOPE_INDEX = OPT_U30 + 0x0A; + public static final int DAT_OFFSET = OPT_S24 + 0x0B; + public static final int DAT_EXCEPTION_INDEX = OPT_U30 + 0x0C; + public static final int DAT_CLASS_INDEX = OPT_U30 + 0x0D; + public static final int DAT_INT_INDEX = OPT_U30 + 0x0E; + public static final int DAT_UINT_INDEX = OPT_U30 + 0x0F; + public static final int DAT_DOUBLE_INDEX = OPT_U30 + 0x10; + public static final int DAT_DECIMAL_INDEX = OPT_U30 + 0x11; + public static final int DAT_CASE_BASEOFFSET = OPT_S24 + 0x12; + public static final int DAT_DECIMAL_PARAMS = OPT_U30 + 0x13; + public static InstructionDefinition[] instructionSet = new InstructionDefinition[]{ + new AddIns(), + new InstructionDefinition(0x9b, "add_d", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2 + 1; //? + } + }, + new AddIIns(), + new InstructionDefinition(0xb5, "add_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), + new ApplyTypeIns(), + new AsTypeIns(), + new AsTypeLateIns(), + new BitAndIns(), + new BitNotIns(), + new BitOrIns(), + new BitXorIns(), + new InstructionDefinition(0x01, "bkpt", new int[]{}), + new InstructionDefinition(0xf2, "bkptline", new int[]{AVM2Code.OPT_U30}), + new CallIns(), + new InstructionDefinition(0x4d, "callinterface", new int[]{AVM2Code.OPT_U30}), + new CallMethodIns(), + new CallPropertyIns(), + new CallPropLexIns(), + new CallPropVoidIns(), + new CallStaticIns(), + new CallSuperIns(), + new InstructionDefinition(0x4b, "callsuperid", new int[]{}), + new CallSuperVoidIns(), + new CheckFilterIns(), + new CoerceIns(), + new CoerceAIns(), + new InstructionDefinition(0x81, "coerce_b", new int[]{}), //stack:-1+1 + new InstructionDefinition(0x84, "coerce_d", new int[]{}), //stack:-1+1 + new InstructionDefinition(0x83, "coerce_i", new int[]{}), //stack:-1+1 + new InstructionDefinition(0x89, "coerce_o", new int[]{}), //stack:-1+1 + new CoerceSIns(), + new InstructionDefinition(0x88, "coerce_u", new int[]{}), //stack:-1+1 + new InstructionDefinition(0x9a, "concat", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2 + 1; //? + } + }, + new ConstructIns(), + new ConstructPropIns(), + new ConstructSuperIns(), + new ConvertBIns(), + new ConvertIIns(), + new ConvertDIns(), + new ConvertOIns(), + new ConvertUIns(), + new ConvertSIns(), + new InstructionDefinition(0x79, "convert_m", new int[]{}), //-1 +1 + new InstructionDefinition(0x7a, "convert_m_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new DebugIns(), + new DebugFileIns(), + new DebugLineIns(), + new DecLocalIns(), + new DecLocalIIns(), + new DecrementIns(), + new DecrementIIns(), + new InstructionDefinition(0x5b, "deldescendants", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new DeletePropertyIns(), + new InstructionDefinition(0x6b, "deletepropertylate", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new DivideIns(), + new InstructionDefinition(0xb8, "divide_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2 + 1; //? + } + }, + new DupIns(), + new DXNSIns(), + new DXNSLateIns(), + new EqualsIns(), + new EscXAttrIns(), + new EscXElemIns(), + new FindDefIns(), + /* //Duplicate OPCODE with deldescendants. Prefering deldescendants (found in FLEX compiler) + new InstructionDefinition(0x5b,"findpropglobalstrict",new int[]{AVM2Code.DAT_MULTINAME_INDEX}){ + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + },*/ + new InstructionDefinition(0x5c, "findpropglobal", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new FindPropertyIns(), + new FindPropertyStrictIns(), + new GetDescendantsIns(), + new GetGlobalScopeIns(), + new GetGlobalSlotIns(), + new GetLexIns(), + new GetLocalIns(), + new GetLocal0Ins(), + new GetLocal1Ins(), + new GetLocal2Ins(), + new GetLocal3Ins(), + new InstructionDefinition(0x67, "getouterscope", new int[]{AVM2Code.DAT_MULTINAME_INDEX}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new GetPropertyIns(), + new GetScopeObjectIns(), + new GetSlotIns(), + new GetSuperIns(), + new GreaterEqualsIns(), + new GreaterThanIns(), + new HasNextIns(), + new HasNext2Ins(), + new IfEqIns(), + new IfFalseIns(), + new IfGeIns(), + new IfGtIns(), + new IfLeIns(), + new IfLtIns(), + new IfNGeIns(), + new IfNGtIns(), + new IfNLeIns(), + new IfNLtIns(), + new IfNeIns(), + new IfStrictEqIns(), + new IfStrictNeIns(), + new IfTrueIns(), + new InIns(), + new IncLocalIns(), + new IncLocalIIns(), + new IncrementIns(), + new IncrementIIns(), + new InstructionDefinition(0x9c, "increment_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), + new InstructionDefinition(0x9d, "inclocal_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS, AVM2Code.DAT_REGISTER_INDEX}), + new InstructionDefinition(0x9e, "decrement_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}), + new InstructionDefinition(0x9f, "declocal_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS, AVM2Code.DAT_REGISTER_INDEX}), + new InitPropertyIns(), + new InstanceOfIns(), + new IsTypeIns(), + new IsTypeLateIns(), + new JumpIns(), + new KillIns(), + new LabelIns(), + new LessEqualsIns(), + new LessThanIns(), + new LookupSwitchIns(), + new LShiftIns(), + new ModuloIns(), + new InstructionDefinition(0xb9, "modulo_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2 + 1; //? + } + }, + new MultiplyIns(), + new MultiplyIIns(), + new InstructionDefinition(0xb7, "multiply_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -2 + 1; //? + } + }, + new NegateIns(), + new NegateIIns(), + new InstructionDefinition(0x8f, "negate_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new NewActivationIns(), + new NewArrayIns(), + new NewCatchIns(), + new NewClassIns(), + new NewFunctionIns(), + new NewObjectIns(), + new NextNameIns(), + new NextValueIns(), + new NopIns(), + new NotIns(), + new PopIns(), + new PopScopeIns(), + new PushByteIns(), + new InstructionDefinition(0x22, "pushconstant", new int[]{AVM2Code.DAT_STRING_INDEX}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return 1; //? + } + }, + new InstructionDefinition(0x33, "pushdecimal", new int[]{AVM2Code.DAT_DECIMAL_INDEX}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return 1; //? + } + }, + new InstructionDefinition(0x34, "pushdnan", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return 1; //? + } + }, + new PushDoubleIns(), + new PushFalseIns(), + new PushIntIns(), + new PushNamespaceIns(), + new PushNanIns(), + new PushNullIns(), + new PushScopeIns(), + new PushShortIns(), + new PushStringIns(), + new PushTrueIns(), + new PushUIntIns(), + new PushUndefinedIns(), + new PushWithIns(), + new ReturnValueIns(), + new ReturnVoidIns(), + new RShiftIns(), + new SetLocalIns(), + new SetLocal0Ins(), + new SetLocal1Ins(), + new SetLocal2Ins(), + new SetLocal3Ins(), + new SetGlobalSlotIns(), + new SetPropertyIns(), + new InstructionDefinition(0x69, "setpropertylate", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new SetSlotIns(), + new SetSuperIns(), + new StrictEqualsIns(), + new SubtractIns(), + new SubtractIIns(), + new InstructionDefinition(0xb6, "subtract_p", new int[]{AVM2Code.DAT_DECIMAL_PARAMS}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new SwapIns(), + new ThrowIns(), + new InstructionDefinition(0xf3, "timestamp", new int[]{}), + new TypeOfIns(), + new URShiftIns(), + new Li8Ins(), + new Li16Ins(), + new Li32Ins(), + new Lf32Ins(), + new Lf64Ins(), + new Si8Ins(), + new Si16Ins(), + new Si32Ins(), + new Sf32Ins(), + new Sf64Ins(), + new Sxi1Ins(), + new Sxi8Ins(), + new Sxi16Ins(), + new InstructionDefinition(0xf5, "verifypass", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xf6, "alloc", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xf7, "mark", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xf8, "wb", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xf9, "prologue", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xfa, "sendenter", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xfb, "doubletoatom", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xfc, "sweep", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xfd, "codegenop", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xfe, "verifyop", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xff, "decode", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + }, + new InstructionDefinition(0xee, "abs_jump", new int[]{}) { + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + + @Override + public int getScopeStackDelta(AVM2Instruction ins, ABC abc) { + throw new UnsupportedOperationException(); + } + } + }; + //endoflist + public static InstructionDefinition[] instructionSetByCode = buildInstructionSetByCode(); + public boolean hideTemporaryRegisters = true; + + private static InstructionDefinition[] buildInstructionSetByCode() { + InstructionDefinition[] result = new InstructionDefinition[256]; + for (InstructionDefinition id : instructionSet) { + if (result[id.instructionCode] != null) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.WARNING, "Duplicate OPCODE for instruction {0} {1}", new Object[]{result[id.instructionCode], id}); + } + result[id.instructionCode] = id; + } + return result; + } + public static final String IDENTOPEN = "/*IDENTOPEN*/"; + public static final String IDENTCLOSE = "/*IDENTCLOSE*/"; + + public AVM2Code() { + } + + public Object execute(HashMap arguments, ConstantPool constants) { + int pos = 0; + LocalDataArea lda = new LocalDataArea(); + lda.localRegisters = arguments; + try { + while (true) { + AVM2Instruction ins = code.get(pos); + if (ins.definition instanceof JumpIns) { + pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); + continue; + } + if (ins.definition instanceof IfFalseIns) { + Boolean b = (Boolean) lda.operandStack.pop(); + if (b == false) { + pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); + } else { + pos++; + } + continue; + } + if (ins.definition instanceof IfTrueIns) { + Boolean b = (Boolean) lda.operandStack.pop(); + if (b == true) { + pos = adr2pos((Long) ins.getParamsAsList(constants).get(0)); + } else { + pos++; + } + continue; + } + if (ins.definition instanceof ReturnValueIns) { + return lda.operandStack.pop(); + } + if (ins.definition instanceof ReturnVoidIns) { + return null; + } + ins.definition.execute(lda, constants, ins.getParamsAsList(constants)); + pos++; + } + } catch (ConvertException e) { + } + return null; + } + + public AVM2Code(InputStream is) throws IOException { + ABCInputStream ais = new ABCInputStream(is); + while (ais.available() > 0) { + long startOffset = ais.getPosition(); + ais.startBuffer(); + int instructionCode = ais.read(); + InstructionDefinition instr = instructionSetByCode[instructionCode]; + if (instr != null) { + int[] actualOperands; + if (instructionCode == 0x1b) { //switch + int firstOperand = ais.readS24(); + int case_count = ais.readU30(); + actualOperands = new int[case_count + 3]; + actualOperands[0] = firstOperand; + actualOperands[1] = case_count; + for (int c = 0; c < case_count + 1; c++) { + actualOperands[2 + c] = ais.readS24(); + } + } else { + actualOperands = new int[instr.operands.length]; + for (int op = 0; op < instr.operands.length; op++) { + switch (instr.operands[op] & 0xff00) { + case OPT_U30: + actualOperands[op] = ais.readU30(); + break; + case OPT_U8: + actualOperands[op] = ais.read(); + break; + case OPT_BYTE: + actualOperands[op] = (byte) ais.read(); + break; + case OPT_S24: + actualOperands[op] = ais.readS24(); + break; + } + } + } + + code.add(new AVM2Instruction(startOffset, instr, actualOperands, ais.stopBuffer())); + } else { + break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions) + //throw new UnknownInstructionCode(instructionCode); + } + } + } + + public void compact() { + if (code instanceof ArrayList) { + ((ArrayList) code).trimToSize(); + } + } + + public byte[] getBytes() { + return getBytes(null); + } + + public byte[] getBytes(byte[] origBytes) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + OutputStream cos; + if ((origBytes != null) && (DEBUG_REWRITE)) { + ByteArrayInputStream origis = new ByteArrayInputStream(origBytes); + cos = new CopyOutputStream(bos, origis); + } else { + cos = bos; + } + try { + for (AVM2Instruction instruction : code) { + cos.write(instruction.getBytes()); + } + } catch (IOException ex) { + } + return bos.toByteArray(); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + for (AVM2Instruction instruction : code) { + s.append(instruction.toString()); + s.append("\r\n"); + } + return s.toString(); + } + + public GraphTextWriter toString(GraphTextWriter writer, LocalData localData) { + int i = 0; + for (AVM2Instruction instruction : code) { + writer.appendNoHilight(Helper.formatAddress(i)); + writer.appendNoHilight(" "); + instruction.toString(writer, localData).newLine(); + i++; + } + return writer; + } + + public GraphTextWriter toASMSource(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, ScriptExportMode exportMode, GraphTextWriter writer) { + return toASMSource(constants, trait, info, body, new ArrayList(), exportMode, writer); + } + + public GraphTextWriter toASMSource(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, List outputMap, ScriptExportMode exportMode, GraphTextWriter writer) { + invalidateCache(); + if (trait != null) { + if (trait instanceof TraitFunction) { + TraitFunction tf = (TraitFunction) trait; + writer.appendNoHilight("trait "); + writer.hilightSpecial("function ", "traittype"); + writer.hilightSpecial(constants.multinameToString(tf.name_index), "traitname"); + writer.appendNoHilight(" slotid "); + writer.hilightSpecial("" + tf.slot_id, "slotid"); + writer.newLine(); + } + if (trait instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; + writer.appendNoHilight("trait "); + switch (tm.kindType) { + case Trait.TRAIT_METHOD: + writer.hilightSpecial("method ", "traittype"); + break; + case Trait.TRAIT_GETTER: + writer.hilightSpecial("getter ", "traittype"); + break; + case Trait.TRAIT_SETTER: + writer.hilightSpecial("setter ", "traittype"); + break; + } + writer.hilightSpecial(constants.multinameToString(tm.name_index), "traitname"); + writer.appendNoHilight(" dispid "); + writer.hilightSpecial("" + tm.disp_id, "dispid"); + writer.newLine(); + } + } + if (info != null) { + writer.appendNoHilight("method").newLine(); + writer.appendNoHilight("name "); + writer.hilightSpecial(info.name_index == 0 ? "null" : "\"" + Helper.escapeString(info.getName(constants)) + "\"", "methodname"); + writer.newLine(); + if (info.flagExplicit()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("EXPLICIT", "flag.EXPLICIT"); + writer.newLine(); + } + if (info.flagHas_optional()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("HAS_OPTIONAL", "flag.HAS_OPTIONAL"); + writer.newLine(); + writer.appendNoHilight("flag HAS_OPTIONAL").newLine(); + } + if (info.flagHas_paramnames()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("HAS_PARAM_NAMES", "flag.HAS_PARAM_NAMES"); + writer.newLine(); + } + if (info.flagIgnore_rest()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("EXPLICIT", "flag.IGNORE_REST"); + writer.newLine(); + } + if (info.flagNeed_activation()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_ACTIVATION", "flag.NEED_ACTIVATION"); + writer.newLine(); + } + if (info.flagNeed_arguments()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_ARGUMENTS", "flag.NEED_ARGUMENTS"); + writer.newLine(); + } + if (info.flagNeed_rest()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("NEED_REST", "flag.NEED_REST"); + writer.newLine(); + } + if (info.flagSetsdxns()) { + writer.appendNoHilight("flag "); + writer.hilightSpecial("SET_DXNS", "flag.SET_DXNS"); + writer.newLine(); + } + for (int p = 0; p < info.param_types.length; p++) { + writer.appendNoHilight("param "); + writer.hilightSpecial(constants.multinameToString(info.param_types[p]), "param", p); + writer.newLine(); + } + if (info.flagHas_paramnames()) { + for (int n : info.paramNames) { + writer.appendNoHilight("paramname "); + if (n == 0) { + writer.appendNoHilight("null"); + } else { + writer.appendNoHilight("\""); + writer.appendNoHilight(constants.getString(n)); + writer.appendNoHilight("\""); + } + writer.newLine(); + } + } + if (info.flagHas_optional()) { + for (int i = 0; i < info.optional.length; i++) { + ValueKind vk = info.optional[i]; + writer.appendNoHilight("optional "); + writer.hilightSpecial(vk.toString(constants), "optional", i); + writer.newLine(); + } + } + writer.appendNoHilight("returns "); + writer.hilightSpecial(constants.multinameToString(info.ret_type), "returns"); + writer.newLine(); + } + writer.newLine(); + writer.appendNoHilight("body").newLine(); + + writer.appendNoHilight("maxstack "); + writer.appendNoHilight(body.max_stack); + writer.newLine(); + + writer.appendNoHilight("localcount "); + writer.appendNoHilight(body.max_regs); + writer.newLine(); + + writer.appendNoHilight("initscopedepth "); + writer.appendNoHilight(body.init_scope_depth); + writer.newLine(); + + writer.appendNoHilight("maxscopedepth "); + writer.appendNoHilight(body.max_scope_depth); + writer.newLine(); + + List offsets = new ArrayList<>(); + for (int e = 0; e < body.exceptions.length; e++) { + writer.appendNoHilight("try"); + + writer.appendNoHilight(" from "); + writer.appendNoHilight("ofs"); + writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].start)); + offsets.add((long) body.exceptions[e].start); + + writer.appendNoHilight(" to "); + writer.appendNoHilight("ofs"); + writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].end)); + offsets.add((long) body.exceptions[e].end); + + writer.appendNoHilight(" target "); + writer.appendNoHilight("ofs"); + writer.appendNoHilight(Helper.formatAddress(body.exceptions[e].target)); + offsets.add((long) body.exceptions[e].target); + + writer.appendNoHilight(" type "); + writer.hilightSpecial(body.exceptions[e].type_index == 0 ? "null" : constants.getMultiname(body.exceptions[e].type_index).toString(constants, new ArrayList()), "try.type", e); + + writer.appendNoHilight(" name "); + writer.hilightSpecial(body.exceptions[e].name_index == 0 ? "null" : constants.getMultiname(body.exceptions[e].name_index).toString(constants, new ArrayList()), "try.name", e); + writer.newLine(); + } + + writer.newLine(); + writer.appendNoHilight("code").newLine(); + + for (AVM2Instruction ins : code) { + offsets.addAll(ins.getOffsets()); + } + for (AVM2Instruction ins : code) { + if (ins.replaceWith != null) { + for (Object o : ins.replaceWith) { + if (o instanceof ControlFlowTag) { + ControlFlowTag cft = (ControlFlowTag) o; + if (cft.name.equals("appendjump")) { + offsets.add((long) pos2adr(cft.value)); + } + } + } + } + } + long ofs = 0; + int ip = 0; + int largeLimit = 20000; + boolean markOffsets = code.size() <= largeLimit; + + if (exportMode == ScriptExportMode.HEX) { + Helper.byteArrayToHexWithHeader(writer, getBytes()); + } else { + for (AVM2Instruction ins : code) { + if (exportMode == ScriptExportMode.PCODE_HEX) { + writer.appendNoHilight("; "); + writer.appendNoHilight(Helper.bytesToHexString(ins.getBytes())); + writer.newLine(); + } + if (ins.labelname != null) { + writer.appendNoHilight(ins.labelname + ":"); + } else if (Configuration.showAllAddresses.get() || offsets.contains(ofs)) { + writer.appendNoHilight("ofs" + Helper.formatAddress(ofs) + ":"); + } + /*for (int e = 0; e < body.exceptions.length; e++) { + if (body.exceptions[e].start == ofs) { + ret.append("exceptionstart " + e + ":"); + } + if (body.exceptions[e].end == ofs) { + ret.append("exceptionend " + e + ":"); + } + if (body.exceptions[e].target == ofs) { + ret.append("exceptiontarget " + e + ":"); + } + }*/ + if (ins.replaceWith != null) { + for (Object o : ins.replaceWith) { + if (o instanceof Integer) { + AVM2Instruction ins2 = code.get((Integer) o); + if (ins2.isIgnored()) { + continue; + } + writer.append("", ins2.mappedOffset > -1 ? ins2.mappedOffset : ofs); + writer.appendNoHilight(ins2.toStringNoAddress(constants, new ArrayList()) + " ;copy from " + Helper.formatAddress(pos2adr((Integer) o))); + writer.newLine(); + outputMap.add((Integer) o); + } else if (o instanceof ControlFlowTag) { + ControlFlowTag cft = (ControlFlowTag) o; + if (cft.name.equals("appendjump")) { + writer.appendNoHilight("jump ofs" + Helper.formatAddress(pos2adr(cft.value))).newLine(); + outputMap.add(-1); + } + if (cft.name.equals("mark")) { + writer.appendNoHilight("ofs" + Helper.formatAddress(pos2adr(cft.value)) + ":"); + } + } + } + } else { + if (!ins.isIgnored()) { + if (markOffsets) { + writer.append("", ins.mappedOffset > -1 ? ins.mappedOffset : ofs); + } + int fixBranch = ins.getFixBranch(); + if (fixBranch > -1) { + if (ins.definition instanceof IfTypeIns) { + for (int i = 0; i < -ins.definition.getStackDelta(ins, null/*IfTypeIns do not require ABCs*/); i++) { + writer.appendNoHilight(new DeobfuscatePopIns().instructionName).newLine(); + } + if (fixBranch == 0) { //jump + writer.appendNoHilight(new JumpIns().instructionName + " ofs" + Helper.formatAddress(ofs + ins.getBytes().length + ins.operands[0])); + } else { + //nojump, ignore + } + } + //TODO: lookupswitch ? + } else { + if (ins.changeJumpTo > -1) { + writer.appendNoHilight(ins.definition.instructionName + " ofs" + Helper.formatAddress(pos2adr(ins.changeJumpTo))); + } else { + writer.appendNoHilight(ins.toStringNoAddress(constants, new ArrayList())); + } + } + writer.newLine(); + outputMap.add(ip); + } + } + ofs += ins.getBytes().length; + ip++; + } + } + return writer; + } + private boolean cacheActual = false; + private List posCache; + + private void buildCache() { + posCache = new ArrayList<>(); + long a = 0; + for (int i = 0; i < code.size(); i++) { + posCache.add(a); + a += code.get(i).getBytes().length; + } + posCache.add(a); + cacheActual = true; + } + + public int adr2pos(long address) throws ConvertException { + if (!cacheActual) { + buildCache(); + } + int ret = posCache.indexOf(address); + if (ret == -1) { + throw new ConvertException("Bad jump try conver ofs" + Helper.formatAddress(address) + " ", -1); + } + return ret; + } + + public int pos2adr(int pos) { + if (!cacheActual) { + buildCache(); + } + return posCache.get(pos).intValue(); + } + + public void invalidateCache() { + cacheActual = false; + } + private List unknownJumps; + private List ignoredIns; + boolean isCatched = false; + + public boolean isKilled(int regName, int start, int end) { + for (int k = start; k <= end; k++) { + if (code.get(k).definition instanceof KillIns) { + if (code.get(k).operands[0] == regName) { + return true; + } + } + } + return false; + } + private int toSourceCount = 0; + + public HashMap getLocalRegNamesFromDebug(ABC abc) { + HashMap localRegNames = new HashMap<>(); + for (AVM2Instruction ins : code) { + if (ins.definition instanceof DebugIns) { + if (ins.operands[0] == 1) { + localRegNames.put(ins.operands[2] + 1, abc.constants.getString(ins.operands[1])); + } + } + } + return localRegNames; + } + + public List clearTemporaryRegisters(List output) { + for (int i = 0; i < output.size(); i++) { + if (output.get(i) instanceof SetLocalAVM2Item) { + if (isKilled(((SetLocalAVM2Item) output.get(i)).regIndex, 0, code.size() - 1)) { + SetLocalAVM2Item lsi = (SetLocalAVM2Item) output.get(i); + if (i + 1 < output.size()) { + if (output.get(i + 1) instanceof ReturnValueAVM2Item) { + ReturnValueAVM2Item rv = (ReturnValueAVM2Item) output.get(i + 1); + if (rv.value instanceof LocalRegAVM2Item) { + LocalRegAVM2Item lr = (LocalRegAVM2Item) rv.value; + if (lr.regIndex == lsi.regIndex) { + rv.value = lsi.value; + } + } + } + } + output.remove(i); + i--; + } + } else if (output.get(i) instanceof WithAVM2Item) { + clearTemporaryRegisters(((WithAVM2Item) output.get(i)).items); + } + } + return output; + } + + public int fixIPAfterDebugLine(int ip) { + if (code.isEmpty()) { + return ip; + } + if (ip >= code.size()) { + return code.size() - 1; + } + while (code.get(ip).definition instanceof DebugLineIns) { + ip++; + } + return ip; + } + + public int fixAddrAfterDebugLine(int addr) throws ConvertException { + return pos2adr(fixIPAfterDebugLine(adr2pos(addr))); + } + + public ConvertOutput toSourceOutput(String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, Stack scopeStack, ABC abc, ConstantPool constants, List method_info, MethodBody body, int start, int end, HashMap localRegNames, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { + boolean debugMode = DEBUG_MODE; + if (debugMode) { + System.out.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); + } + if (visited == null) { + visited = new boolean[code.size()]; + } + //if(true) return ""; + toSourceCount++; + if (toSourceLimit > 0) { + if (toSourceCount > toSourceLimit) { + throw new ConvertException("Limit of subs(" + toSourceLimit + ") was reached", start); + } + } + List output = new ArrayList<>(); + String ret = ""; + int ip = start; + //try { + //int addr; + iploop: + while (ip <= end) { + + if (ignoredIns.contains(ip)) { + ip++; + continue; + } + boolean processTry = processJumps; + //addr = pos2adr(ip); + int ipfix = fixIPAfterDebugLine(ip); + //int addrfix = pos2adr(ipfix); + int maxend = -1; + + if (ip > end) { + break; + } + + if (unknownJumps.contains(ip)) { + unknownJumps.remove(Integer.valueOf(ip)); + throw new UnknownJumpException(stack, ip, output); + } + if (visited[ip]) { + Logger.getLogger(AVM2Code.class.getName()).warning("Code already visited, ofs:" + Helper.formatAddress(pos2adr(ip)) + ", ip:" + ip); + break; + } + visited[ip] = true; + AVM2Instruction ins = code.get(ip); + if (debugMode) { + System.err.println("translating ip " + ip + " ins " + ins.toString() + " stack:" + stack.toString() + " scopeStack:" + scopeStack.toString()); + } + if (ins.definition instanceof NewFunctionIns) { + if (ip + 1 <= end) { + if (code.get(ip + 1).definition instanceof PopIns) { + ip += 2; + continue; + } + } + } + /*if ((ip + 8 < code.size())) { //return in finally clause + if (ins.definition instanceof SetLocalTypeIns) { + if (code.get(ip + 1).definition instanceof PushByteIns) { + AVM2Instruction jmp = code.get(ip + 2); + if (jmp.definition instanceof JumpIns) { + if (jmp.operands[0] == 0) { + if (code.get(ip + 3).definition instanceof LabelIns) { + if (code.get(ip + 4).definition instanceof PopIns) { + if (code.get(ip + 5).definition instanceof LabelIns) { + AVM2Instruction gl = code.get(ip + 6); + if (gl.definition instanceof GetLocalTypeIns) { + if (((GetLocalTypeIns) gl.definition).getRegisterId(gl) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins)) { + AVM2Instruction ki = code.get(ip + 7); + if (ki.definition instanceof KillIns) { + if (ki.operands[0] == ((SetLocalTypeIns) ins.definition).getRegisterId(ins)) { + if (code.get(ip + 8).definition instanceof ReturnValueIns) { + ip = ip + 8; + continue; + } + } + } + } + } + } + } + } + } + } + } + } + }//*/ + + /*if ((ip + 2 < code.size()) && (ins.definition instanceof NewCatchIns)) { //Filling local register in catch clause + if (code.get(ip + 1).definition instanceof DupIns) { + if (code.get(ip + 2).definition instanceof SetLocalTypeIns) { + ins.definition.translate(isStatic, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames); + ip += 3; + continue; + } + } + }*/ + if ((ins.definition instanceof GetLocalTypeIns) && (!output.isEmpty()) && (output.get(output.size() - 1) instanceof SetLocalAVM2Item) && (((SetLocalAVM2Item) output.get(output.size() - 1)).regIndex == ((GetLocalTypeIns) ins.definition).getRegisterId(ins)) && isKilled(((SetLocalAVM2Item) output.get(output.size() - 1)).regIndex, start, end)) { + SetLocalAVM2Item slt = (SetLocalAVM2Item) output.remove(output.size() - 1); + stack.push(slt.getValue()); + ip++; + } else if ((ins.definition instanceof SetLocalTypeIns) && (ip + 1 <= end) && (isKilled(((SetLocalTypeIns) ins.definition).getRegisterId(ins), ip, end))) { //set_local_x,get_local_x..kill x + AVM2Instruction insAfter = code.get(ip + 1); + if ((insAfter.definition instanceof GetLocalTypeIns) && (((GetLocalTypeIns) insAfter.definition).getRegisterId(insAfter) == ((SetLocalTypeIns) ins.definition).getRegisterId(ins))) { + GraphTargetItem before = stack.peek(); + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + stack.push(before); + ip += 2; + continue iploop; + } else { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + ip++; + continue iploop; + } + } else if (ins.definition instanceof DupIns) { + int nextPos; + do { + AVM2Instruction insAfter = code.get(ip + 1); + AVM2Instruction insBefore = ins; + if (ip - 1 >= start) { + insBefore = code.get(ip - 1); + } + if (insAfter.definition instanceof ConvertBIns) { //SWF compiled with debug contain convert_b + ip++; + //addr = pos2adr(ip); + insAfter = code.get(ip + 1); + } + + boolean isAnd; + if (processJumps && (insAfter.definition instanceof IfFalseIns)) { + //stack.add("(" + stack.pop() + ")&&"); + isAnd = true; + } else if (processJumps && (insAfter.definition instanceof IfTrueIns)) { + //stack.add("(" + stack.pop() + ")||"); + isAnd = false; + } else if (insAfter.definition instanceof SetLocalTypeIns) { + //chained assignments + int reg = (((SetLocalTypeIns) insAfter.definition).getRegisterId(insAfter)); + for (int t = ip + 1; t <= end - 1; t++) { + if (code.get(t).definition instanceof KillIns) { + if (code.get(t).operands[0] == reg) { + break; + } + } + if (code.get(t).definition instanceof GetLocalTypeIns) { + if (((GetLocalTypeIns) code.get(t).definition).getRegisterId(code.get(t)) == reg) { + if (code.get(t + 1).definition instanceof KillIns) { + if (code.get(t + 1).operands[0] == reg) { + ConvertOutput assignment = toSourceOutput(path, part, processJumps, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, abc, constants, method_info, body, ip + 2, t - 1, localRegNames, fullyQualifiedNames, visited, localRegAssigmentIps, refs); + GraphTargetItem tar = assignment.output.remove(assignment.output.size() - 1); + tar.firstPart = part; + stack.push(tar); + ip = t + 2; + continue iploop; + } + } + } + } + } + if (!isKilled(reg, 0, end)) { + for (int i = ip; i >= start; i--) { + if (code.get(i).definition instanceof DupIns) { + if (stack.isEmpty()) { + break;//FIXME?o + } + GraphTargetItem v = stack.pop(); + stack.push(new LocalRegAVM2Item(ins, reg, v)); + stack.push(v); + } else { + break; + } + } + } else { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + } + ip++; + break; + //} + + } else { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + ip++; + break; + //throw new ConvertException("Unknown pattern after DUP:" + insComparsion.toString()); + } + } while (ins.definition instanceof DupIns); + } else if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + //ip = end + 1; + break; + } else if (ins.definition instanceof NewFunctionIns) { + String functionName = ""; + if ((ip >= start + 2) && (ip <= end - 4)) { + AVM2Instruction prev2 = code.get(ip - 2); + if (prev2.definition instanceof NewObjectIns) { + if (prev2.operands[0] == 0) { + if (code.get(ip - 1).definition instanceof PushWithIns) { + boolean hasDup = false; + int plus = 0; + if (code.get(ip + 1).definition instanceof DupIns) { + hasDup = true; + plus = 1; + } + AVM2Instruction psco = code.get(ip + 1 + plus); + if (psco.definition instanceof GetScopeObjectIns) { + if (psco.operands[0] == scopeStack.size() - 1) { + if (code.get(ip + plus + 2).definition instanceof SwapIns) { + if (code.get(ip + plus + 4).definition instanceof PopScopeIns) { + if (code.get(ip + plus + 3).definition instanceof SetPropertyIns) { + functionName = abc.constants.getMultiname(code.get(ip + plus + 3).operands[0]).getName(constants, fullyQualifiedNames); + scopeStack.pop();//with + output.remove(output.size() - 1); //with + ip = ip + plus + 4; //+1 below + } + } + } + } + } + } + } + } + } + //What to do when hasDup is false? + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + NewFunctionAVM2Item nft = (NewFunctionAVM2Item) stack.peek(); + nft.functionName = functionName; + ip++; + } else { + try { + ins.definition.translate(isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, constants, ins, method_info, output, body, abc, localRegNames, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this); + } catch (RuntimeException re) { + /*String last=""; + int len=5; + for(int i=(ip-len<0?0:ip-len);i maxRegister) { + maxRegister = regId; + } + } + return maxRegister + 1; + } + + public HashMap getLocalRegTypes(ConstantPool constants, List fullyQualifiedNames) { + HashMap ret = new HashMap<>(); + AVM2Instruction prev = null; + for (AVM2Instruction ins : code) { + if (ins.definition instanceof SetLocalTypeIns) { + if (prev != null) { + if (prev.definition instanceof CoerceOrConvertTypeIns) { + ret.put(((SetLocalTypeIns) ins.definition).getRegisterId(ins), ((CoerceOrConvertTypeIns) prev.definition).getTargetType(constants, prev, fullyQualifiedNames)); + } + } + } + prev = ins; + } + return ret; + } + + private class Slot { + + public GraphTargetItem scope; + public Multiname multiname; + + public Slot(GraphTargetItem scope, Multiname multiname) { + this.scope = scope; + this.multiname = multiname; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Slot) { + return (((Slot) obj).scope.getThroughRegister() == scope.getThroughRegister()) + && (((Slot) obj).multiname == multiname); + } + return false; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 59 * hash + (this.scope != null ? this.scope.hashCode() : 0); + hash = 59 * hash + (this.multiname != null ? this.multiname.hashCode() : 0); + return hash; + } + } + + public void initToSource() { + toSourceCount = 0; + unknownJumps = new ArrayList<>(); + ignoredIns = new ArrayList<>(); + } + + public List toGraphTargetItems(String path, boolean isStatic, int scriptIndex, int classIndex, ABC abc, ConstantPool constants, List method_info, MethodBody body, HashMap localRegNames, Stack scopeStack, boolean isStaticInitializer, List fullyQualifiedNames, Traits initTraits, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs) throws InterruptedException { + initToSource(); + List list; + HashMap localRegs = new HashMap<>(); + + int regCount = getRegisterCount(); + + //try { + list = AVM2Graph.translateViaGraph(path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs); + + if (initTraits != null) { + for (int i = 0; i < list.size(); i++) { + GraphTargetItem ti = list.get(i); + if ((ti instanceof InitPropertyAVM2Item) || (ti instanceof SetPropertyAVM2Item)) { + int multinameIndex = 0; + GraphTargetItem value = null; + if (ti instanceof InitPropertyAVM2Item) { + multinameIndex = ((InitPropertyAVM2Item) ti).propertyName.multinameIndex; + value = ((InitPropertyAVM2Item) ti).value; + } + if (ti instanceof SetPropertyAVM2Item) { + multinameIndex = ((FullMultinameAVM2Item) ((SetPropertyAVM2Item) ti).propertyName).multinameIndex; + value = ((SetPropertyAVM2Item) ti).value; + } + for (Trait t : initTraits.traits) { + if (t.name_index == multinameIndex) { + if ((t instanceof TraitSlotConst)) { + if (((TraitSlotConst) t).isConst() || isStaticInitializer) { + ((TraitSlotConst) t).assignedValue = value; + list.remove(i); + i--; + continue; + } + break; + } + } + } + } else { + //In obfuscated code, SetLocal instructions comes first + //break; + } + } + } + if (isStaticInitializer) { + List newList = new ArrayList<>(); + for (GraphTargetItem ti : list) { + if (!(ti instanceof ReturnVoidAVM2Item)) { + if (!(ti instanceof InitPropertyAVM2Item)) { + if (!(ti instanceof SetPropertyAVM2Item)) { + newList.add(ti); + } + } + } + } + list = newList; + if (list.isEmpty()) { + return list; + } + } + //Declarations + boolean[] declaredRegisters = new boolean[regCount]; + for (int b = 0; b < declaredRegisters.length; b++) { + declaredRegisters[b] = false; + } + List declaredSlots = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + GraphTargetItem ti = list.get(i); + if (ti instanceof SetLocalAVM2Item) { + int reg = ((SetLocalAVM2Item) ti).regIndex; + if (!declaredRegisters[reg]) { + list.set(i, new DeclarationAVM2Item(ti)); + declaredRegisters[reg] = true; + } + } + if (ti instanceof SetSlotAVM2Item) { + SetSlotAVM2Item ssti = (SetSlotAVM2Item) ti; + Slot sl = new Slot(ssti.scope, ssti.slotName); + if (!declaredSlots.contains(sl)) { + GraphTargetItem type = TypeItem.UNBOUNDED; + for (int t = 0; t < body.traits.traits.size(); t++) { + if (body.traits.traits.get(t).getName(abc) == sl.multiname) { + if (body.traits.traits.get(t) instanceof TraitSlotConst) { + type = PropertyAVM2Item.multinameToType(((TraitSlotConst) body.traits.traits.get(t)).type_index, abc.constants); + } + } + } + list.set(i, new DeclarationAVM2Item(ti, type)); + declaredSlots.add(sl); + } + } + } + + int lastPos = list.size() - 1; + if (lastPos < 0) { + lastPos = 0; + } + if ((list.size() > lastPos) && (list.get(lastPos) instanceof ScriptEndItem)) { + lastPos--; + } + if (lastPos < 0) { + lastPos = 0; + } + if ((list.size() > lastPos) && (list.get(lastPos) instanceof ReturnVoidAVM2Item)) { + list.remove(lastPos); + } + + return list; + } + + public void removeInstruction(int pos, MethodBody body) { + if ((pos < 0) || (pos >= code.size())) { + throw new IndexOutOfBoundsException(); + } + int byteCount = code.get(pos).getBytes().length; + long remOffset = code.get(pos).offset; + for (int i = pos + 1; i < code.size(); i++) { + code.get(i).offset -= byteCount; + } + + for (ABCException ex : body.exceptions) { + if (ex.start > remOffset) { + ex.start -= byteCount; + } + if (ex.end > remOffset) { + ex.end -= byteCount; + } + if (ex.target > remOffset) { + ex.target -= byteCount; + } + } + + for (int i = 0; i < pos; i++) { + if (code.get(i).definition instanceof LookupSwitchIns) { + long target = code.get(i).offset + code.get(i).operands[0]; + if (target > remOffset) { + code.get(i).operands[0] -= byteCount; + } + for (int k = 2; k < code.get(i).operands.length; k++) { + target = code.get(i).offset + code.get(i).operands[k]; + if (target > remOffset) { + code.get(i).operands[k] -= byteCount; + } + } + } else { + for (int j = 0; j < code.get(i).definition.operands.length; j++) { + if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { + long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; + if (target > remOffset) { + code.get(i).operands[j] -= byteCount; + } + } + } + } + } + for (int i = pos + 1; i < code.size(); i++) { + if (code.get(i).definition instanceof LookupSwitchIns) { + long target = code.get(i).offset + code.get(i).operands[0]; + if (target < remOffset) { + code.get(i).operands[0] += byteCount; + } + for (int k = 2; k < code.get(i).operands.length; k++) { + target = code.get(i).offset + code.get(i).operands[k]; + if (target < remOffset) { + code.get(i).operands[k] += byteCount; + } + } + } else { + for (int j = 0; j < code.get(i).definition.operands.length; j++) { + if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { + long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; + if (target < remOffset) { + code.get(i).operands[j] += byteCount; + } + } + } + } + } + + code.remove(pos); + invalidateCache(); + } + + public void insertInstruction(int pos, AVM2Instruction instruction) { + if (pos < 0) { + pos = 0; + } + if (pos > code.size()) { + pos = code.size(); + } + int byteCount = instruction.getBytes().length; + if (pos == code.size()) { + instruction.offset = code.get(pos - 1).offset + code.get(pos - 1).getBytes().length; + } else { + instruction.offset = code.get(pos).offset; + } + + for (int i = 0; i < pos; i++) { + for (int j = 0; j < code.get(i).definition.operands.length; j++) { + if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { + long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; + if (target >= instruction.offset) { + code.get(i).operands[j] += byteCount; + } + } + } + } + for (int i = pos; i < code.size(); i++) { + for (int j = 0; j < code.get(i).definition.operands.length; j++) { + if (code.get(i).definition.operands[j] == AVM2Code.DAT_OFFSET) { + long target = code.get(i).offset + code.get(i).getBytes().length + code.get(i).operands[j]; + if (target < instruction.offset) { + code.get(i).operands[j] -= byteCount; + } + } + } + } + + for (int i = pos + 1; i < code.size(); i++) { + code.get(i).offset += byteCount; + } + code.add(pos, instruction); + } + + @SuppressWarnings("unchecked") + private static AVM2LocalData prepareBranchLocalData(AVM2LocalData localData) { + AVM2LocalData ret = new AVM2LocalData(); + ret.isStatic = localData.isStatic; + ret.classIndex = localData.classIndex; + ret.localRegs = new HashMap<>(localData.localRegs); + ret.scopeStack = (Stack) (localData.scopeStack).clone(); + ret.constants = localData.constants; + ret.methodInfo = localData.methodInfo; + ret.methodBody = localData.methodBody; + ret.abc = localData.abc; + ret.localRegNames = localData.localRegNames; + ret.fullyQualifiedNames = localData.fullyQualifiedNames; + ret.parsedExceptions = localData.parsedExceptions; + ret.finallyJumps = localData.finallyJumps; + ret.ignoredSwitches = localData.ignoredSwitches; + ret.scriptIndex = localData.scriptIndex; + ret.localRegAssignmentIps = localData.localRegAssignmentIps; + ret.ip = localData.ip; + ret.refs = localData.refs; + ret.code = localData.code; + return ret; + } + + public int removeTraps(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, ABC abc, int scriptIndex, int classIndex, boolean isStatic, String path) throws InterruptedException { + removeDeadCode(constants, trait, info, body); + AVM2LocalData localData = new AVM2LocalData(); + localData.isStatic = isStatic; + localData.classIndex = classIndex; + localData.localRegs = new HashMap<>(); + localData.scopeStack = new Stack<>(); + localData.constants = abc.constants; + localData.methodInfo = abc.method_info; + localData.methodBody = body; + localData.abc = abc; + localData.localRegNames = body.getLocalRegNames(abc); + localData.fullyQualifiedNames = new ArrayList<>(); + localData.parsedExceptions = new ArrayList<>(); + localData.finallyJumps = new ArrayList<>(); + localData.ignoredSwitches = new ArrayList<>(); + localData.scriptIndex = scriptIndex; + localData.localRegAssignmentIps = new HashMap<>(); + localData.ip = 0; + HashMap> refs = visitCode(body); + localData.refs = refs; + localData.code = this; + int ret = 0; + ret += removeTraps(constants, trait, info, body, localData, new AVM2GraphSource(this, false, -1, -1, new HashMap(), new Stack(), abc, body, new HashMap(), new ArrayList(), new HashMap(), refs), 0, path, refs); + removeIgnored(constants, trait, info, body); + removeDeadCode(constants, trait, info, body); + + return ret; + } + + private void handleRegister(CodeStats stats, int reg) { + if (reg + 1 > stats.maxlocal) { + stats.maxlocal = reg + 1; + } + } + + private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc) { + while (pos < code.size()) { + AVM2Instruction ins = code.get(pos); + if (stats.instructionStats[pos].seen) { + //check stack mismatch here + return true; + } + + if (ins.definition instanceof NewFunctionIns) { + MethodBody innerBody = abc.findBody(ins.operands[0]); + innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false); + } + + stats.instructionStats[pos].seen = true; + stats.instructionStats[pos].stackpos = stack; + stats.instructionStats[pos].scopepos = scope; + + int stackDelta = ins.definition.getStackDelta(ins, abc); + int scopeDelta = ins.definition.getScopeStackDelta(ins, abc); + int oldStack = stack; + + //+" deltaScope:"+(scopeDelta>0?"+"+scopeDelta:scopeDelta)+" stack:"+stack+" scope:"+scope); + stack += stackDelta; + scope += scopeDelta; + + stats.instructionStats[pos].stackpos_after = stack; + stats.instructionStats[pos].scopepos_after = scope; + + if (stack > stats.maxstack) { + stats.maxstack = stack; + } + if (scope > stats.maxscope) { + stats.maxscope = scope; + } + + //System.out.println("stack "+oldStack+(stackDelta>=0?"+"+stackDelta:stackDelta)+" max:"+stats.maxstack+" "+ins); + if ((ins.definition instanceof DXNSIns) || (ins.definition instanceof DXNSLateIns)) { + stats.has_set_dxns = true; + } + if (ins.definition instanceof NewActivationIns) { + stats.has_activation = true; + } + if (ins.definition instanceof SetLocalTypeIns) { + handleRegister(stats, ((SetLocalTypeIns) ins.definition).getRegisterId(ins)); + } else if (ins.definition instanceof GetLocalTypeIns) { + handleRegister(stats, ((GetLocalTypeIns) ins.definition).getRegisterId(ins)); + } else { + for (int i = 0; i < ins.definition.operands.length; i++) { + if (ins.definition.operands[i] == DAT_REGISTER_INDEX) { + handleRegister(stats, ins.operands[i]); + } + } + } + if (ins.definition instanceof ReturnValueIns) { + //check stack=1 + return true; + } + if (ins.definition instanceof ReturnVoidIns) { + //check stack=0 + return true; + } + if (ins.definition instanceof JumpIns) { + try { + pos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); + continue; + } catch (ConvertException ex) { + return false; + } + } else if (ins.definition instanceof IfTypeIns) { + try { + int newpos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); + walkCode(stats, newpos, stack, scope, abc); + } catch (ConvertException ex) { + return false; + } + } + if (ins.definition instanceof LookupSwitchIns) { + for (int i = 0; i < ins.operands.length; i++) { + if (i == 1) { + continue; + } + try { + int newpos = adr2pos(pos2adr(pos) + ins.operands[i]); + if (!walkCode(stats, newpos, stack, scope, abc)) { + return false; + } + } catch (ConvertException ex) { + return false; + } + } + } + pos++; + } + return true; + } + + public CodeStats getStats(ABC abc, MethodBody body, int initScope) { + CodeStats stats = new CodeStats(this); + stats.initscope = initScope; + if (!walkCode(stats, 0, 0, initScope, abc)) { + return null; + } + int scopePos = -1; + int prevStart = 0; + for (int e = 0; e < body.exceptions.length; e++) { + ABCException ex = body.exceptions[e]; + try { + if (scopePos == -1) { + scopePos = stats.instructionStats[adr2pos(ex.end) - 1].scopepos_after; + } + List visited = new ArrayList<>(); + for (int i = 0; i < stats.instructionStats.length; i++) { + if (stats.instructionStats[i].seen) { + visited.add(i); + } + } + if (!walkCode(stats, adr2pos(ex.target), 1 + (ex.isFinally() ? 1 : 0), scopePos, abc)) { + return null; + } + int maxIp = 0; + //searching for visited instruction in second run which has maximum position + for (int i = 0; i < stats.instructionStats.length; i++) { + if (stats.instructionStats[i].seen && !visited.contains(i)) { + maxIp = i; + } + } + scopePos = stats.instructionStats[maxIp].scopepos_after; + int stackPos = stats.instructionStats[maxIp].stackpos_after; + int nextIp = maxIp + 1; + if (code.get(maxIp).definition instanceof JumpIns) { + nextIp = adr2pos(pos2adr(nextIp) + code.get(maxIp).operands[0]); + } + if (nextIp < stats.instructionStats.length) { + int origScopePos = stats.instructionStats[nextIp].scopepos; + int origStackPos = stats.instructionStats[nextIp].stackpos; + + if (prevStart == ex.start && ex.isFinally() && !code.get(nextIp).isExit() && stats.instructionStats[nextIp].seen) { + for (int i = 0; i < stats.instructionStats.length; i++) { + stats.instructionStats[i].seen = false; + } + //Rerun rest with new scopePos, stackPos + if (!walkCode(stats, nextIp, origStackPos + 1/*magic!*/, scopePos - 1 /*magic!*/, abc)) { + return null; + } + scopePos--; + } + } + prevStart = ex.start; + } catch (ConvertException ex1) { + //ignore + } + } + //stats.maxscope+=initScope; + return stats; + } + + private void visitCode(int ip, int lastIp, HashMap> refs) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + while (ip < code.size()) { + if (!refs.containsKey(ip)) { + refs.put(ip, new ArrayList()); + } + refs.get(ip).add(lastIp); + lastIp = ip; + if (refs.get(ip).size() > 1) { + break; + } + AVM2Instruction ins = code.get(ip); + if (ins.definition instanceof ThrowIns) { + break; + } + if (ins.definition instanceof ReturnValueIns) { + break; + } + if (ins.definition instanceof ReturnVoidIns) { + break; + } + if (ins.definition instanceof LookupSwitchIns) { + try { + for (int i = 2; i < ins.operands.length; i++) { + visitCode(adr2pos(pos2adr(ip) + ins.operands[i]), ip, refs); + } + ip = adr2pos(pos2adr(ip) + ins.operands[0]); + continue; + } catch (ConvertException ex) { + } + } + if (ins.definition instanceof JumpIns) { + try { + ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]); + continue; + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } else if (ins.definition instanceof IfTypeIns) { + try { + visitCode(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), ip, refs); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } + ip++; + }; + } + + public HashMap> visitCode(MethodBody body) throws InterruptedException { + HashMap> refs = new HashMap<>(); + for (int i = 0; i < code.size(); i++) { + refs.put(i, new ArrayList()); + } + visitCode(0, 0, refs); + int pos = 0; + for (ABCException e : body.exceptions) { + pos++; + try { + visitCode(adr2pos(e.start), adr2pos(e.start) - 1, refs); + visitCode(adr2pos(e.start), -1, refs); + visitCode(adr2pos(e.target), adr2pos(e.end), refs); + visitCode(adr2pos(e.end), -pos, refs); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } + return refs; + } + + private int visitCodeTrap(int ip, int[] visited, AVM2Instruction prev, AVM2Instruction prev2) { + int ret = 0; + while (ip < visited.length) { + visited[ip]++; + if (visited[ip] > 1) { + break; + } + AVM2Instruction ins = code.get(ip); + if (ins.definition instanceof ThrowIns) { + break; + } + if (ins.definition instanceof ReturnValueIns) { + break; + } + if (ins.definition instanceof ReturnVoidIns) { + break; + } + if (ins.definition instanceof LookupSwitchIns) { + try { + for (int i = 2; i < ins.operands.length; i++) { + ret += visitCodeTrap(adr2pos(pos2adr(ip) + ins.operands[i]), visited, prev, prev2); + } + ip = adr2pos(pos2adr(ip) + ins.operands[0]); + prev2 = prev; + prev = ins; + continue; + } catch (ConvertException ex) { + } + } + if (ins.definition instanceof JumpIns) { + try { + ip = adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]); + prev2 = prev; + prev = ins; + continue; + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } else if (ins.definition instanceof IfTypeIns) { + if ((prev != null) && (prev2 != null)) { + if ((prev.definition instanceof PushByteIns) && (prev2.definition instanceof PushByteIns)) { + if (ins.definition instanceof IfEqIns) { + prev.ignored = true; + prev2.ignored = true; + if (prev.operands[0] == prev2.operands[0]) { + ins.definition = new JumpIns(); + visited[ip]--; + } else { + ins.ignored = true; + ip++; + } + ret++; + continue; + } + if (ins.definition instanceof IfNeIns) { + prev.ignored = true; + prev2.ignored = true; + if (prev.operands[0] != prev2.operands[0]) { + ins.definition = new JumpIns(); + visited[ip]--; + } else { + ins.ignored = true; + ip++; + } + ret++; + continue; + } + } + } + if ((prev != null) && ins.definition instanceof IfTrueIns) { + if (prev.definition instanceof PushTrueIns) { + prev.ignored = true; + ins.definition = new JumpIns(); + visited[ip]--; + ret++; + continue; + } else if (prev.definition instanceof PushFalseIns) { + prev.ignored = true; + ins.ignored = true; + ret++; + ip++; + continue; + } + } + if ((prev != null) && ins.definition instanceof IfFalseIns) { + if (prev.definition instanceof PushFalseIns) { + prev.ignored = true; + ins.definition = new JumpIns(); + visited[ip]--; + ret++; + continue; + } else if (prev.definition instanceof PushTrueIns) { + prev.ignored = true; + ins.ignored = true; + ret++; + ip++; + continue; + } + } + try { + ret += visitCodeTrap(adr2pos(pos2adr(ip) + ins.getBytes().length + ins.operands[0]), visited, prev, prev2); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } + ip++; + prev2 = prev; + prev = ins; + }; + return ret; + } + + private static class ControlFlowTag { + + public String name; + public int value; + + public ControlFlowTag(String name, int value) { + this.name = name; + this.value = value; + } + } + + public void restoreControlFlow(int ip, HashMap> refs, int[] visited2, HashMap> appended) throws ConvertException { + List buf = new ArrayList<>(); + boolean cont = false; + int continueip = 0; + for (; ip < code.size(); ip++) { + AVM2Instruction ins = code.get(ip); + + if ((refs.containsKey(ip) && refs.get(ip).size() > 1) || (visited2[ip] > 0)) { + if (cont) { + buf.add(new ControlFlowTag("appendjump", ip)); + } + cont = false; + if (visited2[ip] > 0) { + break; + } + } + visited2[ip]++; + if (ins.definition instanceof LookupSwitchIns) { + + if (cont) { + buf.add(new ControlFlowTag("appendjump", ip)); + } + cont = false; + restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[0]), refs, visited2, appended); + for (int i = 2; i < ins.operands.length; i++) { + restoreControlFlow(adr2pos(pos2adr(ip) + ins.operands[i]), refs, visited2, appended); + } + break; + } + if (ins.definition instanceof JumpIns) { + int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); + + boolean allJumpsOrIfs = true; + for (int ref : refs.get(ip)) { + if (ref < 0) { + continue; + } + if (!(code.get(ref).definition instanceof JumpIns)) { + if (!(code.get(ref).definition instanceof IfTypeIns)) { + allJumpsOrIfs = false; + break; + } else { + if (adr2pos(pos2adr(ref + 1) + code.get(ref).operands[0]) != ip) { + allJumpsOrIfs = false; + break; + } + } + } + } + if (allJumpsOrIfs) { + for (int ref : refs.get(ip)) { + if (ref < 0) { + continue; + } + code.get(ref).changeJumpTo = newip; + } + } + if ((newip < code.size()) && (refs.containsKey(newip) && refs.get(newip).size() == 1)) { + if (!cont) { + continueip = ip; + buf = new ArrayList<>(); + appended.put(continueip, buf); + } + cont = true; + } else { + if (cont) { + buf.add(new ControlFlowTag("appendjump", newip)); + } + cont = false; + } + ip = newip - 1; + } else if (ins.definition instanceof IfTypeIns) { + int newip = adr2pos(pos2adr(ip + 1) + ins.operands[0]); + if (cont) { + buf.add(new ControlFlowTag("appendjump", ip)); + } + cont = false; + restoreControlFlow(newip, refs, visited2, appended); + } else if ((ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ThrowIns)) { + if (cont) { + buf.add(ip); + } + break; + } else if (cont) { + buf.add(ip); + } + } + + } + + private void restoreControlFlowPass(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, boolean secondpass) throws InterruptedException { + try { + HashMap> refs; + int[] visited2 = new int[code.size()]; + refs = visitCode(body); + HashMap> appended = new HashMap<>(); + /*if (secondpass) { + restoreControlFlow(code.size() - 1, refs, visited2, appended); + } else*/ { + restoreControlFlow(0, refs, visited2, appended); + for (ABCException e : body.exceptions) { + try { + restoreControlFlow(adr2pos(e.start), refs, visited2, appended); + restoreControlFlow(adr2pos(e.target), refs, visited2, appended); + restoreControlFlow(adr2pos(e.end), refs, visited2, appended); + } catch (ConvertException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + } + } + for (int ip : appended.keySet()) { + code.get(ip).replaceWith = appended.get(ip); + } + } catch (ConvertException cex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, "Error during restore control flow", cex); + } + invalidateCache(); + try { + List outputMap = new ArrayList<>(); + HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); + toASMSource(constants, trait, info, body, outputMap, ScriptExportMode.PCODE, writer); + String src = writer.toString(); + + AVM2Code acode = ASM3Parser.parse(new StringReader(src), constants, null, body, info); + for (int i = 0; i < acode.code.size(); i++) { + if (outputMap.size() > i) { + int tpos = outputMap.get(i); + if (tpos == -1) { + } else if (code.get(tpos).mappedOffset >= 0) { + acode.code.get(i).mappedOffset = code.get(tpos).mappedOffset; + } else { + acode.code.get(i).mappedOffset = pos2adr(tpos); + } + + } + } + this.code = acode.code; + } catch (IOException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, null, ex); + } catch (ParseException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.FINE, null, ex); + } + invalidateCache(); + removeDeadCode(constants, trait, info, body); + } + + public void restoreControlFlow(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + restoreControlFlowPass(constants, trait, info, body, false); + //restoreControlFlowPass(constants, body, true); + } + + /*private void removeIgnored(MethodBody body) { + for (int rem = code.size() - 1; rem >= 0; rem--) { + if (code.get(rem).ignored) { + removeInstruction(rem, body); + } + } + }*/ + public void removeIgnored(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + try { + List outputMap = new ArrayList<>(); + HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); + toASMSource(constants, trait, info, body, outputMap, ScriptExportMode.PCODE, writer); + String src = writer.toString(); + AVM2Code acode = ASM3Parser.parse(new StringReader(src), constants, trait, body, info); + for (int i = 0; i < acode.code.size(); i++) { + if (outputMap.size() > i) { + int tpos = outputMap.get(i); + if (tpos == -1) { + } else if (code.get(tpos).mappedOffset >= 0) { + acode.code.get(i).mappedOffset = code.get(tpos).mappedOffset; + } else { + acode.code.get(i).mappedOffset = pos2adr(tpos); + } + } + } + this.code = acode.code; + } catch (IOException | ParseException ex) { + } + invalidateCache(); + } + + public int removeDeadCode(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException { + HashMap> refs = visitCode(body); + + int cnt = 0; + for (int i = code.size() - 1; i >= 0; i--) { + if (refs.get(i).isEmpty()) { + code.get(i).ignored = true; + //removeInstruction(i, body); + cnt++; + } + } + + removeIgnored(constants, trait, info, body); + for (int i = code.size() - 1; i >= 0; i--) { + AVM2Instruction ins = code.get(i); + if (ins.definition instanceof JumpIns) { + if (ins.operands[0] == 0) { + code.get(i).ignored = true; + //removeInstruction(i, body); + cnt++; + } + } + } + removeIgnored(constants, trait, info, body); + return cnt; + } + + public void markMappedOffsets() { + int ofs = 0; + for (int i = 0; i < code.size(); i++) { + code.get(i).mappedOffset = ofs; + ofs += code.get(i).getBytes().length; + } + } + + public AVM2Code deepCopy() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(this); + oos.flush(); + } + AVM2Code copy; + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + copy = (AVM2Code) ois.readObject(); + } + return copy; + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(AVM2Code.class.getName()).log(Level.SEVERE, "Error during deepCopy", ex); + return null; + } + } + + private static class Decision { + + public boolean jumpUsed = false; + public boolean skipUsed = false; + public Set casesUsed = new HashSet<>(); + HashMap registers = new HashMap<>(); + } + + private static int getMostCommonIp(AVM2GraphSource code, List branches) { + List> reachable = new ArrayList<>(); + for (int i = 0; i < branches.size(); i++) { + List r = new ArrayList<>(); + getReachableIps(code, branches.get(i), r); + } + + int commonLevel; + Map levelMap = new HashMap<>(); + for (List first : reachable) { + int maxclevel = 0; + Set visited = new HashSet<>(); + for (Integer p : first) { + if (visited.contains(p)) { + continue; + } + visited.add(p); + boolean common = true; + commonLevel = 1; + for (List r : reachable) { + if (r == first) { + continue; + } + if (r.contains(p)) { + commonLevel++; + } + } + if (commonLevel <= maxclevel) { + continue; + } + maxclevel = commonLevel; + if (levelMap.containsKey(p)) { + if (levelMap.get(p) > commonLevel) { + commonLevel = levelMap.get(p); + } + } + levelMap.put(p, commonLevel); + if (common) { + //return p; + } + } + } + for (int i = reachable.size() - 1; i >= 2; i--) { + for (Integer p : levelMap.keySet()) { + if (levelMap.get(p) == i) { + return p; + } + } + } + for (Integer p : levelMap.keySet()) { + if (levelMap.get(p) == branches.size()) { + return p; + } + } + return -1; + } + + public static void getReachableIps(AVM2GraphSource code, int ip, List reachable) { + do { + if (reachable.contains(ip)) { + return; + } + reachable.add(ip); + GraphSourceItem ins = code.get(ip); + if (ins.isJump() || ins.isBranch()) { + List branches = ins.getBranches(code); + for (int i = 1; i < branches.size(); i++) { + getReachableIps(code, branches.get(i), reachable); + } + ip = branches.get(0); + continue; + } + ip++; + } while (ip < code.size()); + } + + public static boolean isDirectAncestor(int currentIp, int ancestor, HashMap> refs) { + return isDirectAncestor(currentIp, ancestor, refs, new ArrayList()); + } + + private static boolean isDirectAncestor(int currentIp, int ancestor, HashMap> refs, List visited) { + if (currentIp == -1) { + return true; + } + do { + if (currentIp == ancestor) { + return true; + } + if (currentIp == 0) { + return false; + } + if (visited.contains(currentIp)) { + return true; + } + visited.add(currentIp); + if (refs.containsKey(currentIp)) { + List currentRefs = refs.get(currentIp); + if ((currentRefs != null) && (!currentRefs.isEmpty())) { + for (int i = 1; i < currentRefs.size(); i++) { + if (!isDirectAncestor(currentRefs.get(i), ancestor, refs, visited)) { + return false; + } + } + currentIp = currentRefs.get(0); + continue; + } + } + currentIp--; + } while (currentIp >= 0); + return false; + } + + public static boolean getPreviousReachableIps(int currentIp, HashMap> refs, Set reachable, Set visited) { + do { + if (visited.contains(currentIp)) { + return false; + } + reachable.add(currentIp); + visited.add(currentIp); + if (refs.containsKey(currentIp)) { + List currentRefs = refs.get(currentIp); + if ((currentRefs != null) && (!currentRefs.isEmpty())) { + if (currentRefs.size() == 1) { + currentIp = currentRefs.get(0); + continue; + } + boolean r = false; + for (int i = 0; i < currentRefs.size(); i++) { + Set nr = new HashSet<>(); + boolean v = getPreviousReachableIps(currentRefs.get(i), refs, nr, visited); + if ((!v) || nr.contains(0)) { + reachable.addAll(nr); + } + r = r || v; + } + return r; + } + } + currentIp--; + } while (currentIp >= 0); + return true; + } + + @SuppressWarnings("unchecked") + private static int removeTraps(HashMap> refs, boolean secondPass, boolean indeterminate, AVM2LocalData localData, Stack stack, List output, AVM2GraphSource code, int ip, HashMap visited, HashMap> visitedStates, HashMap decisions, String path, int recursionLevel) throws InterruptedException { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + if (recursionLevel > code.size() + 1) { + throw new TranslateException("removeTraps max recursion level reached."); + } + boolean debugMode = false; + int ret = 0; + iploop: + while ((ip > -1) && ip < code.size()) { + + if (false) { //useVisited) { + if (visited.containsKey(ip)) { + break; + } + if (!visited.containsKey(ip)) { + visited.put(ip, 0); + } else { + visited.put(ip, visited.get(ip) + 1); + } + } else { + HashMap currentState = localData.localRegs; + + if (visitedStates.containsKey(ip)) { + HashMap lastState = visitedStates.get(ip); + if (lastState.equals(currentState)) { + break; + } + } + visitedStates.put(ip, (HashMap) currentState.clone()); + + } + + int curVisited; + if (!visited.containsKey(ip)) { + curVisited = 1; + } else { + curVisited = visited.get(ip) + 1; + } + visited.put(ip, curVisited); + + List r = refs.get(ip); + /*if (r != null) { + if (r.size() > 1) { + if (!stack.isEmpty()) { + GraphTargetItem it = stack.pop(); + stack.push(new NotCompileTimeAVM2Item(null, it)); + } + } + }*/ + + AVM2Instruction ins = code.get(ip); + //Errorneous code inserted by some obfuscators + if (ins.definition instanceof NewObjectIns) { + if (ins.operands[0] > stack.size()) { + ins.setIgnored(true, 0); + } + } + if (ins.definition instanceof NewArrayIns) { + if (ins.operands[0] > stack.size()) { + ins.setIgnored(true, 0); + } + } + + if (ins.isIgnored()) { + ip++; + continue; + } + + if (debugMode) { + System.out.println((indeterminate ? "useV " : "") + (secondPass ? "secondPass " : "") + "Visit " + ip + ": " + ins + " stack:" + stack.toString()); + HashMap registers = localData.localRegs; + System.out.print("Registers:"); + for (int reg : registers.keySet()) { + try { + System.out.print(" r" + reg + ": " + registers.get(reg).getResult()); + } catch (NullPointerException npe) { + System.out.print(" r" + reg + ": " + "null"); + } + } + System.out.println(""); + } + if (secondPass) { + /*if ((ins instanceof AVM2Instruction) && (((AVM2Instruction) ins).definition instanceof PopIns)) { + GraphTargetItem top = stack.peek(); + for (GraphSourceItemPos p : top.getNeededSources()) { + if (p == null) { + continue; + } + if (p.item == null) { + continue; + } + if (p.item.isIgnored()) { + ins.setIgnored(true, 0); + break; + } + } + }*/ + } + + if (((AVM2Instruction) ins).definition instanceof NewFunctionIns) { + stack.push(new BooleanAVM2Item(null, true)); + } else { + localData.ip = ip; + ins.translate(localData, stack, output, Graph.SOP_USE_STATIC, path); + } + + if (ins.definition instanceof SetLocalTypeIns) { + SetLocalTypeIns slt = (SetLocalTypeIns) ins.definition; + int regId = slt.getRegisterId(ins); + if (indeterminate) { + HashMap registers = localData.localRegs; + GraphTargetItem regVal = registers.get(regId); + if (regVal.isCompileTime()) { + registers.put(regId, new NotCompileTimeItem(null, regVal)); + } + } + } + + if (ins.isExit()) { + break; + } + + if (ins.isBranch() || ins.isJump()) { + List branches = ins.getBranches(code); + if (((AVM2Instruction) ins).definition instanceof IfTypeIns + && (!(((AVM2Instruction) ins).definition instanceof JumpIns)) + && (!stack.isEmpty()) + && (stack.peek().isCompileTime()) + && (!stack.peek().hasSideEffect())) { + boolean condition = EcmaScript.toBoolean(stack.peek().getResult()); + if (debugMode) { + if (condition) { + System.out.println("JUMP"); + } else { + System.out.println("SKIP"); + } + } + Decision dec = new Decision(); + if (decisions.containsKey(ins)) { + dec = decisions.get(ins); + } else { + decisions.put(ins, dec); + } + if (condition) { + dec.jumpUsed = true; + } else { + dec.skipUsed = true; + } + + if (branches.size() > 1) { + if (secondPass) { + if (condition && (dec.jumpUsed) && (!dec.skipUsed)) { + ins.setFixBranch(0); + //((AVM2Instruction) ins).definition = new JumpIns(); + } + if ((!condition) && (!dec.jumpUsed) && (dec.skipUsed)) { + ins.setFixBranch(1); + //ins.setIgnored(true, 0); + } + } + } + GraphTargetItem tar = stack.pop(); + /*if (secondPass && (dec.jumpUsed != dec.skipUsed)) { + for (GraphSourceItemPos pos : tar.getNeededSources()) { + if (pos.item instanceof AVM2Instruction) { + if (((AVM2Instruction) pos.item).definition instanceof DupIns) { + pos.item.setIgnored(true, 0); + break; + } + } + if (pos.item != ins) { + pos.item.setIgnored(true, 0); + } + } + + }*/ + if (branches.size() == 1) { + ip = branches.get(0); + } else { + ip = condition ? branches.get(0) : branches.get(1); + } + continue; + } else { + if (ins.isBranch() && (!ins.isJump())) { + GraphTargetItem top = stack.pop(); + + Decision dec = new Decision(); + if (decisions.containsKey(ins)) { + dec = decisions.get(ins); + } else { + decisions.put(ins, dec); + } + HashMap registers = localData.localRegs; + boolean regChanged = false; + if (!dec.registers.isEmpty()) { + if (dec.registers.size() != registers.size()) { + regChanged = true; + } else { + for (int reg : registers.keySet()) { + if (!dec.registers.containsKey(reg)) { + regChanged = true; + break; + } + if (!registers.get(reg).isCompileTime() && dec.registers.get(reg).isCompileTime()) { + regChanged = true; + break; + } + } + } + } + dec.registers.putAll(registers); + dec.jumpUsed = true; + dec.skipUsed = true; + + if (!regChanged && ((!(top instanceof HasNextAVM2Item) && curVisited > 1) || (curVisited > 2))) { + for (int b : branches) { + int visc = 0; + if (visited.containsKey(b)) { + visc = visited.get(b); + } + if (visc == 0) {// brStack = (Stack) stack.clone(); + if (b >= 0) { //useVisited || (!ins.isJump()) + ret += removeTraps(refs, secondPass, indeterminate, prepareBranchLocalData(localData), brStack, output, code, b, visited, visitedStates, decisions, path, recursionLevel + 1); + } else { + if (debugMode) { + System.out.println("Negative branch:" + b); + } + } + } + } + break; + } + ip++; + } + if (ip < 0) { + System.out.println("Visited Negative: " + ip); + } + return ret; + } + + public static int removeTraps(ConstantPool constants, Trait trait, MethodInfo info, MethodBody body, AVM2LocalData localData, AVM2GraphSource code, int addr, String path, HashMap> refs) throws InterruptedException { + HashMap decisions = new HashMap<>(); + removeTraps(refs, false, false, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), new HashMap(), new HashMap>(), decisions, path, 0); + int cnt = 0; + for (AVM2Instruction src : decisions.keySet()) { + Decision dec = decisions.get(src); + if (dec != null) { + if (((AVM2Instruction) src).definition instanceof LookupSwitchIns) { + AVM2Instruction asrc = (AVM2Instruction) src; + if (dec.casesUsed.size() == 1) { + for (int c : dec.casesUsed) { + asrc.setFixBranch(c); + cnt++; + } + } + } else { + if (dec.jumpUsed && !dec.skipUsed) { + src.setFixBranch(0); + cnt++; + } + if (!dec.jumpUsed && dec.skipUsed) { + src.setFixBranch(1); + cnt++; + } + } + } + } + //int cnt = removeTraps(refs, true, false, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), new HashMap(), new HashMap>(), decisions, path); + code.getCode().removeIgnored(constants, trait, info, body); + return cnt; + } + /*public static int removeTraps(AVM2LocalData localData, AVM2GraphSource code, int addr) { + AVM2Graph.translateViaGraph(localData, "", code, new ArrayList(), Graph.SOP_REMOVE_STATIC); + return 1; + }*/ +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/ConstantPool.java b/src/com/jpexs/decompiler/flash/abc/avm2/ConstantPool.java index 04ce6447d..6e77899c9 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/ConstantPool.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/ConstantPool.java @@ -1,402 +1,402 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2; - -import com.jpexs.decompiler.flash.abc.types.Decimal; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.helpers.utf8.Utf8PrintWriter; -import java.util.ArrayList; -import java.util.List; - -public class ConstantPool { - - public List constant_int = new ArrayList<>(); - public List constant_uint = new ArrayList<>(); - public List constant_double = new ArrayList<>(); - /* Only for some minor versions */ - public List constant_decimal = new ArrayList<>(); - public List constant_string = new ArrayList<>(); - public List constant_namespace = new ArrayList<>(); - public List constant_namespace_set = new ArrayList<>(); - public List constant_multiname = new ArrayList<>(); - - public synchronized int addInt(long value) { - constant_int.add(value); - return constant_int.size() - 1; - } - - public synchronized int addNamespace(Namespace ns) { - constant_namespace.add(ns); - return constant_namespace.size() - 1; - } - - public synchronized int addNamespaceSet(NamespaceSet nss) { - constant_namespace_set.add(nss); - return constant_namespace_set.size() - 1; - } - - public synchronized int addMultiname(Multiname m) { - constant_multiname.add(m); - return constant_multiname.size() - 1; - } - - public synchronized int addUInt(long value) { - constant_uint.add(value); - return constant_uint.size() - 1; - } - - public synchronized int addDouble(double value) { - constant_double.add(value); - return constant_double.size() - 1; - } - - public synchronized int addDecimal(Decimal value) { - constant_decimal.add(value); - return constant_decimal.size() - 1; - } - - public synchronized int addString(String value) { - constant_string.add(value); - return constant_string.size() - 1; - } - - public long setInt(int index, long value) { - constant_int.set(index, value); - return value; - } - - public Namespace setNamespace(int index, Namespace ns) { - constant_namespace.set(index, ns); - return ns; - } - - public NamespaceSet setNamespaceSet(int index, NamespaceSet nss) { - constant_namespace_set.set(index, nss); - return nss; - } - - public Multiname setMultiname(int index, Multiname m) { - constant_multiname.set(index, m); - return m; - } - - public long setUInt(int index, long value) { - constant_uint.set(index, value); - return value; - } - - public double setDouble(int index, double value) { - constant_double.set(index, value); - return value; - } - - public Decimal setDecimal(int index, Decimal value) { - constant_decimal.set(index, value); - return value; - } - - public String setString(int index, String value) { - constant_string.set(index, value); - return value; - } - - public long getInt(int index) { - return constant_int.get(index); - } - - public Namespace getNamespace(int index) { - return constant_namespace.get(index); - } - - public NamespaceSet getNamespaceSet(int index) { - return constant_namespace_set.get(index); - } - - public Multiname getMultiname(int index) { - return constant_multiname.get(index); - } - - public long getUInt(int index) { - return constant_uint.get(index); - } - - public double getDouble(int index) { - return constant_double.get(index); - } - - public Decimal getDecimal(int index) { - return constant_decimal.get(index); - } - - public String getString(int index) { - return constant_string.get(index); - } - - public int getIntCount() { - return constant_int.size(); - } - - public int getNamespaceCount() { - return constant_namespace.size(); - } - - public int getNamespaceSetCount() { - return constant_namespace_set.size(); - } - - public int getMultinameCount() { - return constant_multiname.size(); - } - - public int getUIntCount() { - return constant_uint.size(); - } - - public int getDoubleCount() { - return constant_double.size(); - } - - public int getDecimalCount() { - return constant_decimal.size(); - } - - public int getStringCount() { - return constant_string.size(); - } - - public int getNamespaceSubIndex(int namespaceId){ - Namespace ns = constant_namespace.get(namespaceId); - int index=0; - for (int n = 1; n < namespaceId; n++) { - if(constant_namespace.get(n).name_index == ns.name_index && constant_namespace.get(n).kind == ns.kind){ - index++; - } - } - return index; - } - - public int getNamespaceId(Namespace val, int index) { - for (int n = 1; n < constant_namespace.size(); n++) { - Namespace ns = constant_namespace.get(n); - if (ns.name_index == val.name_index && (ns.kind == val.kind)) { - if (index == 0) { - return n; - } - index--; - } - } - return 0; - } - - public int getIntId(long value) { - for (int i = 1; i < constant_int.size(); i++) { - if (constant_int.get(i) == value) { - return i; - } - } - return 0; - } - - public int getUIntId(long value) { - for (int i = 1; i < constant_uint.size(); i++) { - if (constant_uint.get(i) == value) { - return i; - } - } - return 0; - } - - public int getDoubleId(double value) { - for (int i = 1; i < constant_double.size(); i++) { - if(Double.isNaN(value) && Double.isNaN(constant_double.get(i))){ - return i; - } - if (Double.compare(constant_double.get(i), value) == 0) { - return i; - } - } - return 0; - } - - public int getStringId(String val) { - if (val == null) { - return 0; - } - for (int i = 1; i < constant_string.size(); i++) { - if (constant_string.get(i).equals(val)) { - return i; - } - } - return 0; - } - - public int getMultinameId(Multiname val) { - loopm: - for (int m = 1; m < constant_multiname.size(); m++) { - Multiname mul = constant_multiname.get(m); - if (mul.kind == val.kind && mul.name_index == val.name_index && mul.namespace_index == val.namespace_index && mul.namespace_set_index == val.namespace_set_index && mul.qname_index == val.qname_index && mul.params.size() == val.params.size()) { - for (int p = 0; p < mul.params.size(); p++) { - if (mul.params.get(p) != val.params.get(p)) { - continue loopm; - } - } - return m; - } - } - return 0; - } - - public int getQnameId(String name, int namespaceKind, String namespaceName, boolean add) { - return getMultinameId(new Multiname(Multiname.QNAME, getStringId(name, add), getNamespaceId(new Namespace(namespaceKind, getStringId(namespaceName, add)), 0, add), -1, -1, new ArrayList()), add); - } - - public int getPublicQnameId(String name, boolean add) { - return getQnameId(name, Namespace.KIND_PACKAGE, "", add); - } - - public int getMultinameId(Multiname val, boolean add) { - int id = getMultinameId(val); - if (add && id == 0) { - id = addMultiname(val); - } - return id; - } - - public int getStringId(String val, boolean add) { - if (val == null) { - return 0; - } - int id = getStringId(val); - if (add && id == 0) { - id = addString(val); - } - return id; - } - - public int getIntId(long val, boolean add) { - int id = getIntId(val); - if (add && id == 0) { - id = addInt(val); - } - return id; - } - - public int getNamespaceId(Namespace val, int index, boolean add) { - int id = getNamespaceId(val, index); - if (add && id == 0) { - id = addNamespace(val); - } - return id; - } - - public int getNamespaceSetId(NamespaceSet val) { - loopi: - for (int i = 1; i < constant_namespace_set.size(); i++) { - NamespaceSet ts = constant_namespace_set.get(i); - if (ts.namespaces.length != val.namespaces.length) { - continue; - } - for (int j = 0; j < val.namespaces.length; j++) { - boolean found = false; - for (int k = 0; k < val.namespaces.length; k++) { - if (ts.namespaces[j] == val.namespaces[k]) { - found = true; - break; - } - } - if (!found) { - continue loopi; - } - } - return i; - } - return 0; - } - - public int getNamespaceSetId(NamespaceSet val, boolean add) { - int id = getNamespaceSetId(val); - if (add && id == 0) { - id = addNamespaceSet(val); - } - return id; - } - - public int getUIntId(long val, boolean add) { - int id = getUIntId(val); - if (add && id == 0) { - id = addUInt(val); - } - return id; - } - - public int getDoubleId(double val, boolean add) { - int id = getDoubleId(val); - if (add && id == 0) { - id = addDouble(val); - } - return id; - } - - public void dump(Utf8PrintWriter writer) { - String s = ""; - for (int i = 1; i < constant_int.size(); i++) { - writer.println("INT[" + i + "]=" + constant_int.get(i)); - } - for (int i = 1; i < constant_uint.size(); i++) { - writer.println("UINT[" + i + "]=" + constant_uint.get(i)); - } - for (int i = 1; i < constant_double.size(); i++) { - writer.println("Double[" + i + "]=" + constant_double.get(i)); - } - for (int i = 1; i < constant_string.size(); i++) { - writer.println("String[" + i + "]=" + constant_string.get(i)); - } - for (int i = 1; i < constant_namespace.size(); i++) { - writer.println("Namespace[" + i + "]=" + constant_namespace.get(i).toString(this)); - } - for (int i = 1; i < constant_namespace_set.size(); i++) { - writer.println("NamespaceSet[" + i + "]=" + constant_namespace_set.get(i).toString(this)); - } - - for (int i = 1; i < constant_multiname.size(); i++) { - writer.println("Multiname[" + i + "]=" + constant_multiname.get(i).toString(this, new ArrayList())); - } - } - - public String multinameToString(int index) { - if (index == 0) { - return "null"; - } - return constant_multiname.get(index).toString(this, new ArrayList()); - } - - public String namespaceToString(int index) { - if (index == 0) { - return "null"; - } - return constant_namespace.get(index).toString(this); - } - - public String namespaceSetToString(int index) { - if (index == 0) { - return "null"; - } - return constant_namespace_set.get(index).toString(this); - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2; + +import com.jpexs.decompiler.flash.abc.types.Decimal; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.helpers.utf8.Utf8PrintWriter; +import java.util.ArrayList; +import java.util.List; + +public class ConstantPool { + + public List constant_int = new ArrayList<>(); + public List constant_uint = new ArrayList<>(); + public List constant_double = new ArrayList<>(); + /* Only for some minor versions */ + public List constant_decimal = new ArrayList<>(); + public List constant_string = new ArrayList<>(); + public List constant_namespace = new ArrayList<>(); + public List constant_namespace_set = new ArrayList<>(); + public List constant_multiname = new ArrayList<>(); + + public synchronized int addInt(long value) { + constant_int.add(value); + return constant_int.size() - 1; + } + + public synchronized int addNamespace(Namespace ns) { + constant_namespace.add(ns); + return constant_namespace.size() - 1; + } + + public synchronized int addNamespaceSet(NamespaceSet nss) { + constant_namespace_set.add(nss); + return constant_namespace_set.size() - 1; + } + + public synchronized int addMultiname(Multiname m) { + constant_multiname.add(m); + return constant_multiname.size() - 1; + } + + public synchronized int addUInt(long value) { + constant_uint.add(value); + return constant_uint.size() - 1; + } + + public synchronized int addDouble(double value) { + constant_double.add(value); + return constant_double.size() - 1; + } + + public synchronized int addDecimal(Decimal value) { + constant_decimal.add(value); + return constant_decimal.size() - 1; + } + + public synchronized int addString(String value) { + constant_string.add(value); + return constant_string.size() - 1; + } + + public long setInt(int index, long value) { + constant_int.set(index, value); + return value; + } + + public Namespace setNamespace(int index, Namespace ns) { + constant_namespace.set(index, ns); + return ns; + } + + public NamespaceSet setNamespaceSet(int index, NamespaceSet nss) { + constant_namespace_set.set(index, nss); + return nss; + } + + public Multiname setMultiname(int index, Multiname m) { + constant_multiname.set(index, m); + return m; + } + + public long setUInt(int index, long value) { + constant_uint.set(index, value); + return value; + } + + public double setDouble(int index, double value) { + constant_double.set(index, value); + return value; + } + + public Decimal setDecimal(int index, Decimal value) { + constant_decimal.set(index, value); + return value; + } + + public String setString(int index, String value) { + constant_string.set(index, value); + return value; + } + + public long getInt(int index) { + return constant_int.get(index); + } + + public Namespace getNamespace(int index) { + return constant_namespace.get(index); + } + + public NamespaceSet getNamespaceSet(int index) { + return constant_namespace_set.get(index); + } + + public Multiname getMultiname(int index) { + return constant_multiname.get(index); + } + + public long getUInt(int index) { + return constant_uint.get(index); + } + + public double getDouble(int index) { + return constant_double.get(index); + } + + public Decimal getDecimal(int index) { + return constant_decimal.get(index); + } + + public String getString(int index) { + return constant_string.get(index); + } + + public int getIntCount() { + return constant_int.size(); + } + + public int getNamespaceCount() { + return constant_namespace.size(); + } + + public int getNamespaceSetCount() { + return constant_namespace_set.size(); + } + + public int getMultinameCount() { + return constant_multiname.size(); + } + + public int getUIntCount() { + return constant_uint.size(); + } + + public int getDoubleCount() { + return constant_double.size(); + } + + public int getDecimalCount() { + return constant_decimal.size(); + } + + public int getStringCount() { + return constant_string.size(); + } + + public int getNamespaceSubIndex(int namespaceId) { + Namespace ns = constant_namespace.get(namespaceId); + int index = 0; + for (int n = 1; n < namespaceId; n++) { + if (constant_namespace.get(n).name_index == ns.name_index && constant_namespace.get(n).kind == ns.kind) { + index++; + } + } + return index; + } + + public int getNamespaceId(Namespace val, int index) { + for (int n = 1; n < constant_namespace.size(); n++) { + Namespace ns = constant_namespace.get(n); + if (ns.name_index == val.name_index && (ns.kind == val.kind)) { + if (index == 0) { + return n; + } + index--; + } + } + return 0; + } + + public int getIntId(long value) { + for (int i = 1; i < constant_int.size(); i++) { + if (constant_int.get(i) == value) { + return i; + } + } + return 0; + } + + public int getUIntId(long value) { + for (int i = 1; i < constant_uint.size(); i++) { + if (constant_uint.get(i) == value) { + return i; + } + } + return 0; + } + + public int getDoubleId(double value) { + for (int i = 1; i < constant_double.size(); i++) { + if (Double.isNaN(value) && Double.isNaN(constant_double.get(i))) { + return i; + } + if (Double.compare(constant_double.get(i), value) == 0) { + return i; + } + } + return 0; + } + + public int getStringId(String val) { + if (val == null) { + return 0; + } + for (int i = 1; i < constant_string.size(); i++) { + if (constant_string.get(i).equals(val)) { + return i; + } + } + return 0; + } + + public int getMultinameId(Multiname val) { + loopm: + for (int m = 1; m < constant_multiname.size(); m++) { + Multiname mul = constant_multiname.get(m); + if (mul.kind == val.kind && mul.name_index == val.name_index && mul.namespace_index == val.namespace_index && mul.namespace_set_index == val.namespace_set_index && mul.qname_index == val.qname_index && mul.params.size() == val.params.size()) { + for (int p = 0; p < mul.params.size(); p++) { + if (mul.params.get(p) != val.params.get(p)) { + continue loopm; + } + } + return m; + } + } + return 0; + } + + public int getQnameId(String name, int namespaceKind, String namespaceName, boolean add) { + return getMultinameId(new Multiname(Multiname.QNAME, getStringId(name, add), getNamespaceId(new Namespace(namespaceKind, getStringId(namespaceName, add)), 0, add), -1, -1, new ArrayList()), add); + } + + public int getPublicQnameId(String name, boolean add) { + return getQnameId(name, Namespace.KIND_PACKAGE, "", add); + } + + public int getMultinameId(Multiname val, boolean add) { + int id = getMultinameId(val); + if (add && id == 0) { + id = addMultiname(val); + } + return id; + } + + public int getStringId(String val, boolean add) { + if (val == null) { + return 0; + } + int id = getStringId(val); + if (add && id == 0) { + id = addString(val); + } + return id; + } + + public int getIntId(long val, boolean add) { + int id = getIntId(val); + if (add && id == 0) { + id = addInt(val); + } + return id; + } + + public int getNamespaceId(Namespace val, int index, boolean add) { + int id = getNamespaceId(val, index); + if (add && id == 0) { + id = addNamespace(val); + } + return id; + } + + public int getNamespaceSetId(NamespaceSet val) { + loopi: + for (int i = 1; i < constant_namespace_set.size(); i++) { + NamespaceSet ts = constant_namespace_set.get(i); + if (ts.namespaces.length != val.namespaces.length) { + continue; + } + for (int j = 0; j < val.namespaces.length; j++) { + boolean found = false; + for (int k = 0; k < val.namespaces.length; k++) { + if (ts.namespaces[j] == val.namespaces[k]) { + found = true; + break; + } + } + if (!found) { + continue loopi; + } + } + return i; + } + return 0; + } + + public int getNamespaceSetId(NamespaceSet val, boolean add) { + int id = getNamespaceSetId(val); + if (add && id == 0) { + id = addNamespaceSet(val); + } + return id; + } + + public int getUIntId(long val, boolean add) { + int id = getUIntId(val); + if (add && id == 0) { + id = addUInt(val); + } + return id; + } + + public int getDoubleId(double val, boolean add) { + int id = getDoubleId(val); + if (add && id == 0) { + id = addDouble(val); + } + return id; + } + + public void dump(Utf8PrintWriter writer) { + String s = ""; + for (int i = 1; i < constant_int.size(); i++) { + writer.println("INT[" + i + "]=" + constant_int.get(i)); + } + for (int i = 1; i < constant_uint.size(); i++) { + writer.println("UINT[" + i + "]=" + constant_uint.get(i)); + } + for (int i = 1; i < constant_double.size(); i++) { + writer.println("Double[" + i + "]=" + constant_double.get(i)); + } + for (int i = 1; i < constant_string.size(); i++) { + writer.println("String[" + i + "]=" + constant_string.get(i)); + } + for (int i = 1; i < constant_namespace.size(); i++) { + writer.println("Namespace[" + i + "]=" + constant_namespace.get(i).toString(this)); + } + for (int i = 1; i < constant_namespace_set.size(); i++) { + writer.println("NamespaceSet[" + i + "]=" + constant_namespace_set.get(i).toString(this)); + } + + for (int i = 1; i < constant_multiname.size(); i++) { + writer.println("Multiname[" + i + "]=" + constant_multiname.get(i).toString(this, new ArrayList())); + } + } + + public String multinameToString(int index) { + if (index == 0) { + return "null"; + } + return constant_multiname.get(index).toString(this, new ArrayList()); + } + + public String namespaceToString(int index) { + if (index == 0) { + return "null"; + } + return constant_namespace.get(index).toString(this); + } + + public String namespaceSetToString(int index) { + if (index == 0) { + return "null"; + } + return constant_namespace_set.get(index).toString(this); + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java index c54b34e49..f8492db12 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/other/SetPropertyIns.java @@ -1,174 +1,171 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.instructions.other; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.SetTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class SetPropertyIns extends InstructionDefinition implements SetTypeIns { - - public SetPropertyIns() { - super(0x61, "setproperty", new int[]{AVM2Code.DAT_MULTINAME_INDEX}); - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - int multinameIndex = ins.operands[0]; - GraphTargetItem value = (GraphTargetItem) stack.pop(); - FullMultinameAVM2Item multiname = resolveMultiname(stack, constants, multinameIndex, ins); - GraphTargetItem obj = (GraphTargetItem) stack.pop(); - if (value.getThroughDuplicate().getThroughRegister().getThroughDuplicate() instanceof IncrementAVM2Item) { - GraphTargetItem inside = ((IncrementAVM2Item) value.getThroughDuplicate().getThroughRegister().getThroughDuplicate()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); - if (inside instanceof GetPropertyAVM2Item) { - GetPropertyAVM2Item insideProp = ((GetPropertyAVM2Item) inside); - if (((FullMultinameAVM2Item) insideProp.propertyName).compareSame(multiname)) { - GraphTargetItem insideObj = obj.getThroughDuplicate(); - if (insideObj instanceof LocalRegAVM2Item) { - if (((LocalRegAVM2Item) insideObj).computedValue != null) { - insideObj = ((LocalRegAVM2Item) insideObj).computedValue.getThroughNotCompilable().getThroughDuplicate(); - } - } - if (insideProp.object.getThroughDuplicate() == insideObj) { - if (stack.size() > 0) { - GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); - if (top == insideProp) { - stack.pop(); - stack.push(new PostIncrementAVM2Item(ins, insideProp)); - } else if ((top instanceof IncrementAVM2Item) && (((IncrementAVM2Item) top).value == inside)) { - stack.pop(); - stack.push(new PreIncrementAVM2Item(ins, insideProp)); - } else { - output.add(new PostIncrementAVM2Item(ins, insideProp)); - } - } else { - output.add(new PostIncrementAVM2Item(ins, insideProp)); - } - return; - } - } - } - } - - if (value.getThroughDuplicate().getThroughRegister().getThroughDuplicate() instanceof DecrementAVM2Item) { - GraphTargetItem inside = ((DecrementAVM2Item) value.getThroughDuplicate().getThroughRegister().getThroughDuplicate()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); - if (inside instanceof GetPropertyAVM2Item) { - GetPropertyAVM2Item insideProp = ((GetPropertyAVM2Item) inside); - if (((FullMultinameAVM2Item) insideProp.propertyName).compareSame(multiname)) { - GraphTargetItem insideObj = obj.getThroughDuplicate(); - if (insideObj instanceof LocalRegAVM2Item) { - if (((LocalRegAVM2Item) insideObj).computedValue != null) { - insideObj = ((LocalRegAVM2Item) insideObj).computedValue.getThroughNotCompilable().getThroughDuplicate(); - } - } - if (insideProp.object.getThroughDuplicate() == insideObj) { - if (stack.size() > 0) { - GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); - if (top == insideProp) { - stack.pop(); - stack.push(new PostDecrementAVM2Item(ins, insideProp)); - } else if ((top instanceof DecrementAVM2Item) && (((DecrementAVM2Item) top).value == inside)) { - stack.pop(); - stack.push(new PreDecrementAVM2Item(ins, insideProp)); - } else { - output.add(new PostDecrementAVM2Item(ins, insideProp)); - } - } else { - output.add(new PostDecrementAVM2Item(ins, insideProp)); - } - return; - } - } - } - } - - if(obj.getThroughDuplicate() instanceof ConstructAVM2Item){ - ConstructAVM2Item c = (ConstructAVM2Item)obj.getThroughDuplicate(); - if(c.object instanceof ApplyTypeAVM2Item){ - ApplyTypeAVM2Item at = (ApplyTypeAVM2Item)c.object; - c.args.clear(); - List vals=new ArrayList<>(); - vals.add(value); - c.object = new InitVectorAVM2Item(c.instruction,at.params.get(0),vals); - return; - } - else - if(c.object instanceof InitVectorAVM2Item){ - InitVectorAVM2Item iv = (InitVectorAVM2Item)c.object; - iv.arguments.add(value); - return; - } - } - - output.add(new SetPropertyAVM2Item(ins, obj, multiname, value)); - - - } - - @Override - public String getObject(Stack stack, ABC abc, AVM2Instruction ins, List output, MethodBody body, HashMap localRegNames, List fullyQualifiedNames) { - int multinameIndex = ins.operands[0]; - String multiname = resolveMultinameNoPop(0, stack, abc.constants, multinameIndex, ins, fullyQualifiedNames); - GraphTargetItem obj = stack.get(1 + resolvedCount(abc.constants, multinameIndex)); //pod vrcholem - if ((!obj.toString().isEmpty())) { - multiname = "." + multiname; - } - return obj + multiname; - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - int ret = -2; - int multinameIndex = ins.operands[0]; - //Note: In official compiler, the stack can be wrong(greater) for some MULTINAMEL/A, e.g. increments - /* - var arr=[1,2,3]; - trace(arr[2]++); - */ - if (abc.constants.getMultiname(multinameIndex).needsName()) { - ret--; - } - if (abc.constants.getMultiname(multinameIndex).needsNs()) { - ret--; - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.instructions.other; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.SetTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ConstructAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.DecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FullMultinameAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class SetPropertyIns extends InstructionDefinition implements SetTypeIns { + + public SetPropertyIns() { + super(0x61, "setproperty", new int[]{AVM2Code.DAT_MULTINAME_INDEX}); + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + int multinameIndex = ins.operands[0]; + GraphTargetItem value = (GraphTargetItem) stack.pop(); + FullMultinameAVM2Item multiname = resolveMultiname(stack, constants, multinameIndex, ins); + GraphTargetItem obj = (GraphTargetItem) stack.pop(); + if (value.getThroughDuplicate().getThroughRegister().getThroughDuplicate() instanceof IncrementAVM2Item) { + GraphTargetItem inside = ((IncrementAVM2Item) value.getThroughDuplicate().getThroughRegister().getThroughDuplicate()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); + if (inside instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item insideProp = ((GetPropertyAVM2Item) inside); + if (((FullMultinameAVM2Item) insideProp.propertyName).compareSame(multiname)) { + GraphTargetItem insideObj = obj.getThroughDuplicate(); + if (insideObj instanceof LocalRegAVM2Item) { + if (((LocalRegAVM2Item) insideObj).computedValue != null) { + insideObj = ((LocalRegAVM2Item) insideObj).computedValue.getThroughNotCompilable().getThroughDuplicate(); + } + } + if (insideProp.object.getThroughDuplicate() == insideObj) { + if (stack.size() > 0) { + GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); + if (top == insideProp) { + stack.pop(); + stack.push(new PostIncrementAVM2Item(ins, insideProp)); + } else if ((top instanceof IncrementAVM2Item) && (((IncrementAVM2Item) top).value == inside)) { + stack.pop(); + stack.push(new PreIncrementAVM2Item(ins, insideProp)); + } else { + output.add(new PostIncrementAVM2Item(ins, insideProp)); + } + } else { + output.add(new PostIncrementAVM2Item(ins, insideProp)); + } + return; + } + } + } + } + + if (value.getThroughDuplicate().getThroughRegister().getThroughDuplicate() instanceof DecrementAVM2Item) { + GraphTargetItem inside = ((DecrementAVM2Item) value.getThroughDuplicate().getThroughRegister().getThroughDuplicate()).value.getThroughRegister().getNotCoerced().getThroughDuplicate(); + if (inside instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item insideProp = ((GetPropertyAVM2Item) inside); + if (((FullMultinameAVM2Item) insideProp.propertyName).compareSame(multiname)) { + GraphTargetItem insideObj = obj.getThroughDuplicate(); + if (insideObj instanceof LocalRegAVM2Item) { + if (((LocalRegAVM2Item) insideObj).computedValue != null) { + insideObj = ((LocalRegAVM2Item) insideObj).computedValue.getThroughNotCompilable().getThroughDuplicate(); + } + } + if (insideProp.object.getThroughDuplicate() == insideObj) { + if (stack.size() > 0) { + GraphTargetItem top = stack.peek().getNotCoerced().getThroughDuplicate(); + if (top == insideProp) { + stack.pop(); + stack.push(new PostDecrementAVM2Item(ins, insideProp)); + } else if ((top instanceof DecrementAVM2Item) && (((DecrementAVM2Item) top).value == inside)) { + stack.pop(); + stack.push(new PreDecrementAVM2Item(ins, insideProp)); + } else { + output.add(new PostDecrementAVM2Item(ins, insideProp)); + } + } else { + output.add(new PostDecrementAVM2Item(ins, insideProp)); + } + return; + } + } + } + } + + if (obj.getThroughDuplicate() instanceof ConstructAVM2Item) { + ConstructAVM2Item c = (ConstructAVM2Item) obj.getThroughDuplicate(); + if (c.object instanceof ApplyTypeAVM2Item) { + ApplyTypeAVM2Item at = (ApplyTypeAVM2Item) c.object; + c.args.clear(); + List vals = new ArrayList<>(); + vals.add(value); + c.object = new InitVectorAVM2Item(c.instruction, at.params.get(0), vals); + return; + } else if (c.object instanceof InitVectorAVM2Item) { + InitVectorAVM2Item iv = (InitVectorAVM2Item) c.object; + iv.arguments.add(value); + return; + } + } + + output.add(new SetPropertyAVM2Item(ins, obj, multiname, value)); + + } + + @Override + public String getObject(Stack stack, ABC abc, AVM2Instruction ins, List output, MethodBody body, HashMap localRegNames, List fullyQualifiedNames) { + int multinameIndex = ins.operands[0]; + String multiname = resolveMultinameNoPop(0, stack, abc.constants, multinameIndex, ins, fullyQualifiedNames); + GraphTargetItem obj = stack.get(1 + resolvedCount(abc.constants, multinameIndex)); //pod vrcholem + if ((!obj.toString().isEmpty())) { + multiname = "." + multiname; + } + return obj + multiname; + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + int ret = -2; + int multinameIndex = ins.operands[0]; + //Note: In official compiler, the stack can be wrong(greater) for some MULTINAMEL/A, e.g. increments + /* + var arr=[1,2,3]; + trace(arr[2]++); + */ + if (abc.constants.getMultiname(multinameIndex).needsName()) { + ret--; + } + if (abc.constants.getMultiname(multinameIndex).needsNs()) { + ret--; + } + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PushShortIns.java b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PushShortIns.java index 15ce37587..345345337 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PushShortIns.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/stack/PushShortIns.java @@ -1,53 +1,53 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.instructions.stack; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class PushShortIns extends InstructionDefinition implements PushIntegerTypeIns { - - public PushShortIns() { - super(0x25, "pushshort", new int[]{AVM2Code.OPT_U30}); - } - - @Override - public void execute(LocalDataArea lda, ConstantPool constants, List arguments) { - lda.operandStack.push(arguments.get(0)); - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - stack.push(new IntegerValueAVM2Item(ins, Long.valueOf((long)(short)ins.operands[0]))); - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return 1; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.instructions.stack; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class PushShortIns extends InstructionDefinition implements PushIntegerTypeIns { + + public PushShortIns() { + super(0x25, "pushshort", new int[]{AVM2Code.OPT_U30}); + } + + @Override + public void execute(LocalDataArea lda, ConstantPool constants, List arguments) { + lda.operandStack.push(arguments.get(0)); + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + stack.push(new IntegerValueAVM2Item(ins, Long.valueOf((long) (short) ins.operands[0]))); + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return 1; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/CoerceIns.java b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/CoerceIns.java index 70541b7d3..9908d574f 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/CoerceIns.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/instructions/types/CoerceIns.java @@ -1,62 +1,62 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.instructions.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class CoerceIns extends InstructionDefinition implements CoerceOrConvertTypeIns { - - public CoerceIns() { - super(0x80, "coerce", new int[]{AVM2Code.DAT_MULTINAME_INDEX}); - } - - @Override - public void execute(LocalDataArea lda, ConstantPool constants, List arguments) { - //int multinameIndex = (int) ((Long) arguments.get(0)).longValue(); - //push and pop coerced value to specified type - } - - @Override - public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { - int multinameIndex = ins.operands[0]; - stack.push(new CoerceAVM2Item(ins, (GraphTargetItem) stack.pop(),PropertyAVM2Item.multinameToType(multinameIndex,constants))); - } - - @Override - public GraphTargetItem getTargetType(ConstantPool constants, AVM2Instruction ins, List fullyQualifiedNames) { - int multinameIndex = ins.operands[0]; - return PropertyAVM2Item.multinameToType(multinameIndex, constants); - } - - @Override - public int getStackDelta(AVM2Instruction ins, ABC abc) { - return -1 + 1; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.instructions.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.LocalDataArea; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class CoerceIns extends InstructionDefinition implements CoerceOrConvertTypeIns { + + public CoerceIns() { + super(0x80, "coerce", new int[]{AVM2Code.DAT_MULTINAME_INDEX}); + } + + @Override + public void execute(LocalDataArea lda, ConstantPool constants, List arguments) { + //int multinameIndex = (int) ((Long) arguments.get(0)).longValue(); + //push and pop coerced value to specified type + } + + @Override + public void translate(boolean isStatic, int scriptIndex, int classIndex, java.util.HashMap localRegs, Stack stack, java.util.Stack scopeStack, ConstantPool constants, AVM2Instruction ins, List method_info, List output, MethodBody body, ABC abc, HashMap localRegNames, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code) { + int multinameIndex = ins.operands[0]; + stack.push(new CoerceAVM2Item(ins, (GraphTargetItem) stack.pop(), PropertyAVM2Item.multinameToType(multinameIndex, constants))); + } + + @Override + public GraphTargetItem getTargetType(ConstantPool constants, AVM2Instruction ins, List fullyQualifiedNames) { + int multinameIndex = ins.operands[0]; + return PropertyAVM2Item.multinameToType(multinameIndex, constants); + } + + @Override + public int getStackDelta(AVM2Instruction ins, ABC abc) { + return -1 + 1; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java index bf387c3e9..fc3280cdc 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/AVM2Item.java @@ -1,148 +1,148 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public abstract class AVM2Item extends GraphTargetItem { - - public AVM2Instruction instruction; - public boolean hidden = false; - - public AVM2Item(GraphSourceItem instruction, int precedence) { - super(instruction, precedence); - } - - @Override - public boolean needsSemicolon() { - return true; - } - - protected GraphTextWriter formatProperty(GraphTextWriter writer, GraphTargetItem object, GraphTargetItem propertyName, LocalData localData) throws InterruptedException { - boolean empty = object instanceof FindPropertyAVM2Item; - if (object instanceof LocalRegAVM2Item) { - if (((LocalRegAVM2Item) object).computedValue != null) { - if (((LocalRegAVM2Item) object).computedValue.getThroughNotCompilable() instanceof FindPropertyAVM2Item) { - empty = true; - } - } - } - - if (object instanceof FindPropertyAVM2Item) { - FindPropertyAVM2Item fp = (FindPropertyAVM2Item) object; - if (fp.propertyName instanceof FullMultinameAVM2Item) { - propertyName = fp.propertyName; - } - } - - if (!empty) { - if (object.getPrecedence() > PRECEDENCE_PRIMARY) { - writer.append("("); - object.toString(writer, localData); - writer.append(")"); - empty = false; - } else { - int writerLength = writer.getLength(); - object.toString(writer, localData); - if (writerLength == writer.getLength()) { - empty = true; - } - } - } - - if (empty) { - return propertyName.toString(writer, localData); - } - if (propertyName instanceof FullMultinameAVM2Item) { - if (((FullMultinameAVM2Item) propertyName).name != null) { - return propertyName.toString(writer, localData); - } else { - writer.append("."); - return propertyName.toString(writer, localData); - } - } else { - writer.append("["); - propertyName.toString(writer, localData); - return writer.append("]"); - } - } - - public static String localRegName(HashMap localRegNames, int reg) { - if (localRegNames.containsKey(reg)) { - return localRegNames.get(reg); - } else { - if (reg == 0) { - return "this"; - } - return String.format(Configuration.registerNameFormat.get(), reg); - } - } - - /*@Override - public boolean hasReturnValue() { - return false; - }*/ - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if (!hasReturnValue()) { - return toSource(localData, generator); - } - List ret = toSource(localData, generator); - ret.add(new AVM2Instruction(0, new PopIns(), new int[]{}, new byte[0])); - return ret; - } - - public static AVM2Instruction ins(InstructionDefinition def, Integer... operands) { - List ops=new ArrayList<>(); - for(Integer o:operands){ - if(o!=null){ - ops.add(o); - } - } - int opArr[]=new int[ops.size()]; - for(int i=0;i. + */ +package com.jpexs.decompiler.flash.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public abstract class AVM2Item extends GraphTargetItem { + + public AVM2Instruction instruction; + public boolean hidden = false; + + public AVM2Item(GraphSourceItem instruction, int precedence) { + super(instruction, precedence); + } + + @Override + public boolean needsSemicolon() { + return true; + } + + protected GraphTextWriter formatProperty(GraphTextWriter writer, GraphTargetItem object, GraphTargetItem propertyName, LocalData localData) throws InterruptedException { + boolean empty = object instanceof FindPropertyAVM2Item; + if (object instanceof LocalRegAVM2Item) { + if (((LocalRegAVM2Item) object).computedValue != null) { + if (((LocalRegAVM2Item) object).computedValue.getThroughNotCompilable() instanceof FindPropertyAVM2Item) { + empty = true; + } + } + } + + if (object instanceof FindPropertyAVM2Item) { + FindPropertyAVM2Item fp = (FindPropertyAVM2Item) object; + if (fp.propertyName instanceof FullMultinameAVM2Item) { + propertyName = fp.propertyName; + } + } + + if (!empty) { + if (object.getPrecedence() > PRECEDENCE_PRIMARY) { + writer.append("("); + object.toString(writer, localData); + writer.append(")"); + empty = false; + } else { + int writerLength = writer.getLength(); + object.toString(writer, localData); + if (writerLength == writer.getLength()) { + empty = true; + } + } + } + + if (empty) { + return propertyName.toString(writer, localData); + } + if (propertyName instanceof FullMultinameAVM2Item) { + if (((FullMultinameAVM2Item) propertyName).name != null) { + return propertyName.toString(writer, localData); + } else { + writer.append("."); + return propertyName.toString(writer, localData); + } + } else { + writer.append("["); + propertyName.toString(writer, localData); + return writer.append("]"); + } + } + + public static String localRegName(HashMap localRegNames, int reg) { + if (localRegNames.containsKey(reg)) { + return localRegNames.get(reg); + } else { + if (reg == 0) { + return "this"; + } + return String.format(Configuration.registerNameFormat.get(), reg); + } + } + + /*@Override + public boolean hasReturnValue() { + return false; + }*/ + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (!hasReturnValue()) { + return toSource(localData, generator); + } + List ret = toSource(localData, generator); + ret.add(new AVM2Instruction(0, new PopIns(), new int[]{}, new byte[0])); + return ret; + } + + public static AVM2Instruction ins(InstructionDefinition def, Integer... operands) { + List ops = new ArrayList<>(); + for (Integer o : operands) { + if (o != null) { + ops.add(o); + } + } + int opArr[] = new int[ops.size()]; + for (int i = 0; i < ops.size(); i++) { + opArr[i] = ops.get(i); + } + + return new AVM2Instruction(0, def, opArr, new byte[0]); + } + + public static int getFreeRegister(SourceGeneratorLocalData localData, SourceGenerator generator) { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + return g.getFreeRegister(localData); + } + + public static void killRegister(SourceGeneratorLocalData localData, SourceGenerator generator, int regNumber) { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + g.killRegister(localData, regNumber); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/ApplyTypeAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/ApplyTypeAVM2Item.java index 3c8a49336..49a2f79f1 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/ApplyTypeAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/ApplyTypeAVM2Item.java @@ -1,94 +1,89 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -public class ApplyTypeAVM2Item extends AVM2Item { - - public GraphTargetItem object; - public List params; - - public ApplyTypeAVM2Item(AVM2Instruction instruction, GraphTargetItem object, List params) { - super(instruction, PRECEDENCE_PRIMARY); - this.params = params; - this.object = object; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - object.toString(writer, localData); - if (!params.isEmpty()) { - writer.append(".<"); - for (int i = 0; i < params.size(); i++) { - if (i > 0) { - writer.append(","); - } - GraphTargetItem p = params.get(i); - if (p instanceof NullAVM2Item) { - writer.append("*"); - } else { - p.toString(writer, localData); - } - } - writer.append(">"); - } - return writer; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - //int qname = AVM2SourceGenerator.resolveType(localData,object,((AVM2SourceGenerator)generator).abc,((AVM2SourceGenerator)generator).allABCs); - List nparams=new ArrayList<>(); - for(GraphTargetItem i:params){ - nparams.addAll(i.toSource(localData, generator));//ins(new GetLexIns(),AVM2SourceGenerator.resolveType(localData,i,((AVM2SourceGenerator)generator).abc,((AVM2SourceGenerator)generator).allABCs))); - } - return toSourceMerge(localData, generator, - object, - nparams, - ins(new ApplyTypeIns(),params.size()) - ); - } - - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +public class ApplyTypeAVM2Item extends AVM2Item { + + public GraphTargetItem object; + public List params; + + public ApplyTypeAVM2Item(AVM2Instruction instruction, GraphTargetItem object, List params) { + super(instruction, PRECEDENCE_PRIMARY); + this.params = params; + this.object = object; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + object.toString(writer, localData); + if (!params.isEmpty()) { + writer.append(".<"); + for (int i = 0; i < params.size(); i++) { + if (i > 0) { + writer.append(","); + } + GraphTargetItem p = params.get(i); + if (p instanceof NullAVM2Item) { + writer.append("*"); + } else { + p.toString(writer, localData); + } + } + writer.append(">"); + } + return writer; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + //int qname = AVM2SourceGenerator.resolveType(localData,object,((AVM2SourceGenerator)generator).abc,((AVM2SourceGenerator)generator).allABCs); + List nparams = new ArrayList<>(); + for (GraphTargetItem i : params) { + nparams.addAll(i.toSource(localData, generator));//ins(new GetLexIns(),AVM2SourceGenerator.resolveType(localData,i,((AVM2SourceGenerator)generator).abc,((AVM2SourceGenerator)generator).allABCs))); + } + return toSourceMerge(localData, generator, + object, + nparams, + ins(new ApplyTypeIns(), params.size()) + ); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/CoerceAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/CoerceAVM2Item.java index 7f3531778..53fd5c3cf 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/CoerceAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/CoerceAVM2Item.java @@ -1,142 +1,141 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.ecma.Undefined; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; -import java.util.Set; - -public class CoerceAVM2Item extends AVM2Item { - - //public GraphTargetItem value; - //public GraphTargetItem type; - public GraphTargetItem typeObj; - - /*public CoerceAVM2Item(AVM2Instruction instruction, GraphTargetItem value, String type) { - super(instruction, value.getPrecedence()); - this.value = value; - this.type = type; - }*/ - - public CoerceAVM2Item(AVM2Instruction instruction, GraphTargetItem value, GraphTargetItem typeObj) { - super(instruction, value.getPrecedence()); - this.value = value; - this.typeObj = typeObj; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - //return hilight("("+type+")", highlight)+ - return value.toString(writer, localData); - } - - @Override - public GraphTargetItem getNotCoerced() { - return value.getNotCoerced(); - } - - @Override - public boolean isCompileTime(Set dependencies) { - if (dependencies.contains(value)) { - return false; - } - dependencies.add(value); - return value.isCompileTime(dependencies); - } - - @Override - public Object getResult() { - Object ret = value.getResult(); - switch (typeObj.toString()) { - case "String": - if (ret instanceof Null) { - return ret; - } - if (ret instanceof Undefined) { - return new Null(); - } - return ret.toString(); - case "*": - break; - } - return ret; - - } - - @Override - public GraphTargetItem returnType() { - return new TypeItem(typeObj.toString()); - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - - /*if (value.returnType().toString().equals(type)) { - return toSourceMerge(localData, generator, value); - }*/ - AVM2Instruction ins; - switch (typeObj.toString()) { - case "*": - ins = new AVM2Instruction(0, new CoerceAIns(), new int[]{}, new byte[0]); - break; - case "String": - ins = new AVM2Instruction(0, new CoerceSIns(), new int[]{}, new byte[0]); - break; - case "Boolean": - ins = new AVM2Instruction(0, new ConvertBIns(), new int[]{}, new byte[0]); - break; - case "int": - ins = new AVM2Instruction(0, new ConvertIIns(), new int[]{}, new byte[0]); - break; - case "uint": - ins = new AVM2Instruction(0, new ConvertUIns(), new int[]{}, new byte[0]); - break; - case "Number": - ins = new AVM2Instruction(0, new ConvertDIns(), new int[]{}, new byte[0]); - break; - default: - int type_index = AVM2SourceGenerator.resolveType(localData, typeObj, ((AVM2SourceGenerator) generator).abc,(((AVM2SourceGenerator) generator).allABCs)); - ins = new AVM2Instruction(0, new CoerceIns(), new int[]{type_index}, new byte[0]); - break; - } - return toSourceMerge(localData, generator, value, ins); - } - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; +import java.util.Set; + +public class CoerceAVM2Item extends AVM2Item { + + //public GraphTargetItem value; + //public GraphTargetItem type; + public GraphTargetItem typeObj; + + /*public CoerceAVM2Item(AVM2Instruction instruction, GraphTargetItem value, String type) { + super(instruction, value.getPrecedence()); + this.value = value; + this.type = type; + }*/ + public CoerceAVM2Item(AVM2Instruction instruction, GraphTargetItem value, GraphTargetItem typeObj) { + super(instruction, value.getPrecedence()); + this.value = value; + this.typeObj = typeObj; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + //return hilight("("+type+")", highlight)+ + return value.toString(writer, localData); + } + + @Override + public GraphTargetItem getNotCoerced() { + return value.getNotCoerced(); + } + + @Override + public boolean isCompileTime(Set dependencies) { + if (dependencies.contains(value)) { + return false; + } + dependencies.add(value); + return value.isCompileTime(dependencies); + } + + @Override + public Object getResult() { + Object ret = value.getResult(); + switch (typeObj.toString()) { + case "String": + if (ret instanceof Null) { + return ret; + } + if (ret instanceof Undefined) { + return new Null(); + } + return ret.toString(); + case "*": + break; + } + return ret; + + } + + @Override + public GraphTargetItem returnType() { + return new TypeItem(typeObj.toString()); + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + + /*if (value.returnType().toString().equals(type)) { + return toSourceMerge(localData, generator, value); + }*/ + AVM2Instruction ins; + switch (typeObj.toString()) { + case "*": + ins = new AVM2Instruction(0, new CoerceAIns(), new int[]{}, new byte[0]); + break; + case "String": + ins = new AVM2Instruction(0, new CoerceSIns(), new int[]{}, new byte[0]); + break; + case "Boolean": + ins = new AVM2Instruction(0, new ConvertBIns(), new int[]{}, new byte[0]); + break; + case "int": + ins = new AVM2Instruction(0, new ConvertIIns(), new int[]{}, new byte[0]); + break; + case "uint": + ins = new AVM2Instruction(0, new ConvertUIns(), new int[]{}, new byte[0]); + break; + case "Number": + ins = new AVM2Instruction(0, new ConvertDIns(), new int[]{}, new byte[0]); + break; + default: + int type_index = AVM2SourceGenerator.resolveType(localData, typeObj, ((AVM2SourceGenerator) generator).abc, (((AVM2SourceGenerator) generator).allABCs)); + ins = new AVM2Instruction(0, new CoerceIns(), new int[]{type_index}, new byte[0]); + break; + } + return toSourceMerge(localData, generator, value, ins); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java index c9f8ffc52..f56c1ce7d 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/ConstructAVM2Item.java @@ -1,73 +1,73 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; - -public class ConstructAVM2Item extends AVM2Item { - - public GraphTargetItem object; - public List args; - - public ConstructAVM2Item(AVM2Instruction instruction, GraphTargetItem object, List args) { - super(instruction, PRECEDENCE_PRIMARY); - this.object = object; - this.args = args; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - if (object instanceof NewFunctionAVM2Item) { - writer.append("new "); - return object.toString(writer, localData); - } - writer.append("new "); - if(object.getPrecedence()>getPrecedence()){ - writer.append("("); - } - object.toString(writer, localData); - if(object.getPrecedence()>getPrecedence()){ - writer.append(")"); - } - writer.spaceBeforeCallParenthesies(args.size()); - if(object instanceof InitVectorAVM2Item){ - return writer; - } - writer.append("("); - for (int a = 0; a < args.size(); a++) { - if (a > 0) { - writer.append(","); - } - args.get(a).toString(writer, localData); - } - return writer.append(")"); - } - - @Override - public GraphTargetItem returnType() { - return object.returnType(); - } - - @Override - public boolean hasReturnValue() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; + +public class ConstructAVM2Item extends AVM2Item { + + public GraphTargetItem object; + public List args; + + public ConstructAVM2Item(AVM2Instruction instruction, GraphTargetItem object, List args) { + super(instruction, PRECEDENCE_PRIMARY); + this.object = object; + this.args = args; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + if (object instanceof NewFunctionAVM2Item) { + writer.append("new "); + return object.toString(writer, localData); + } + writer.append("new "); + if (object.getPrecedence() > getPrecedence()) { + writer.append("("); + } + object.toString(writer, localData); + if (object.getPrecedence() > getPrecedence()) { + writer.append(")"); + } + writer.spaceBeforeCallParenthesies(args.size()); + if (object instanceof InitVectorAVM2Item) { + return writer; + } + writer.append("("); + for (int a = 0; a < args.size(); a++) { + if (a > 0) { + writer.append(","); + } + args.get(a).toString(writer, localData); + } + return writer.append(")"); + } + + @Override + public GraphTargetItem returnType() { + return object.returnType(); + } + + @Override + public boolean hasReturnValue() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java index 84cf60db9..7edc921f0 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/ConvertAVM2Item.java @@ -1,86 +1,85 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.Set; - -public class ConvertAVM2Item extends AVM2Item { - - //public GraphTargetItem value; - public GraphTargetItem type; - - public ConvertAVM2Item(AVM2Instruction instruction, GraphTargetItem value, GraphTargetItem type) { - super(instruction, NOPRECEDENCE); - this.value = value; - this.type = type; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return value.toString(writer, localData); - } - - @Override - public GraphTargetItem getNotCoerced() { - return value.getNotCoerced(); - } - - @Override - public Object getResult() { - switch (type.toString()) { - case "Boolean": - return EcmaScript.toBoolean(value.getResult()); - case "Number": - return EcmaScript.toNumber(value.getResult()); - case "int": - return (int) (double) EcmaScript.toNumber(value.getResult()); - case "uint": - return (int) (double) EcmaScript.toUint32(value.getResult()); - case "String": - return value.getResult().toString(); - case "Object": - return value.getResult(); //if not object throw TypeError - default: - return new Object(); - } - } - - @Override - public boolean isCompileTime(Set dependencies) { - if (dependencies.contains(value)) { - return false; - } - dependencies.add(value); - return value.isCompileTime(dependencies); - } - - @Override - public GraphTargetItem returnType() { - return type; - } - - @Override - public boolean hasReturnValue() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.Set; + +public class ConvertAVM2Item extends AVM2Item { + + //public GraphTargetItem value; + public GraphTargetItem type; + + public ConvertAVM2Item(AVM2Instruction instruction, GraphTargetItem value, GraphTargetItem type) { + super(instruction, NOPRECEDENCE); + this.value = value; + this.type = type; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return value.toString(writer, localData); + } + + @Override + public GraphTargetItem getNotCoerced() { + return value.getNotCoerced(); + } + + @Override + public Object getResult() { + switch (type.toString()) { + case "Boolean": + return EcmaScript.toBoolean(value.getResult()); + case "Number": + return EcmaScript.toNumber(value.getResult()); + case "int": + return (int) (double) EcmaScript.toNumber(value.getResult()); + case "uint": + return (int) (double) EcmaScript.toUint32(value.getResult()); + case "String": + return value.getResult().toString(); + case "Object": + return value.getResult(); //if not object throw TypeError + default: + return new Object(); + } + } + + @Override + public boolean isCompileTime(Set dependencies) { + if (dependencies.contains(value)) { + return false; + } + dependencies.add(value); + return value.isCompileTime(dependencies); + } + + @Override + public GraphTargetItem returnType() { + return type; + } + + @Override + public boolean hasReturnValue() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/InAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/InAVM2Item.java index ea0592a89..22d91cd1b 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/InAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/InAVM2Item.java @@ -1,65 +1,64 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InIns; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; - -public class InAVM2Item extends AVM2Item { - - public GraphTargetItem object; - public GraphTargetItem collection; - - public InAVM2Item(AVM2Instruction instruction, GraphTargetItem object, GraphTargetItem collection) { - super(instruction, NOPRECEDENCE); - this.object = object; - this.collection = collection; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - object.toString(writer, localData); - writer.append(" in "); - return collection.toString(writer, localData); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, object,collection,ins(new InIns())); - } - - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InIns; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; + +public class InAVM2Item extends AVM2Item { + + public GraphTargetItem object; + public GraphTargetItem collection; + + public InAVM2Item(AVM2Instruction instruction, GraphTargetItem object, GraphTargetItem collection) { + super(instruction, NOPRECEDENCE); + this.object = object; + this.collection = collection; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + object.toString(writer, localData); + writer.append(" in "); + return collection.toString(writer, localData); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, object, collection, ins(new InIns())); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/InitVectorAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/InitVectorAVM2Item.java index e8e3c2f1d..1b4b81bdf 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/InitVectorAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/InitVectorAVM2Item.java @@ -1,126 +1,123 @@ -/* - * Copyright (C) 2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class InitVectorAVM2Item extends AVM2Item{ - - public GraphTargetItem subtype; - public List arguments; - List openedNamespaces; - - private int allNsSet(ABC abc) { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - public InitVectorAVM2Item(AVM2Instruction ins, GraphTargetItem subtype,List arguments){ - super(ins,PRECEDENCE_PRIMARY); - this.subtype = subtype; - this.arguments = arguments; - } - - public InitVectorAVM2Item(GraphTargetItem subtype,List arguments, List openedNamespaces) { - super(null, PRECEDENCE_PRIMARY); - this.subtype = subtype; - this.arguments = arguments; - this.openedNamespaces = openedNamespaces; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("<"); - subtype.appendTo(writer, localData); - writer.append(">"); - writer.append("["); - for(int i=0;i0){ - writer.append(","); - } - arguments.get(i).appendTo(writer, localData); - } - writer.append("]"); - return writer; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - List pars=new ArrayList<>(); - pars.add(subtype); - return new ApplyTypeAVM2Item(null,new TypeItem("__AS3__.vec.Vector"), pars); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - AVM2SourceGenerator g=(AVM2SourceGenerator)generator; - List ret= toSourceMerge(localData, generator, - ins(new FindPropertyStrictIns(),g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, g.abc.constants.getStringId("Vector", true), 0, g.abc.constants.getNamespaceSetId(new NamespaceSet(new int[]{g.abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, g.abc.constants.getStringId("__AS3__.vec", true)), 0,true)}), true), 0, new ArrayList()), true)), - ins(new GetPropertyIns(),g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, g.abc.constants.getStringId("Vector", true), 0, allNsSet(g.abc), 0, new ArrayList()), true)), - subtype, - ins(new ApplyTypeIns(),1), - new IntegerValueAVM2Item(null, (long)arguments.size()), - ins(new ConstructIns(),1) - ); - for(int i=0;i. + */ +package com.jpexs.decompiler.flash.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class InitVectorAVM2Item extends AVM2Item { + + public GraphTargetItem subtype; + public List arguments; + List openedNamespaces; + + private int allNsSet(ABC abc) { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + public InitVectorAVM2Item(AVM2Instruction ins, GraphTargetItem subtype, List arguments) { + super(ins, PRECEDENCE_PRIMARY); + this.subtype = subtype; + this.arguments = arguments; + } + + public InitVectorAVM2Item(GraphTargetItem subtype, List arguments, List openedNamespaces) { + super(null, PRECEDENCE_PRIMARY); + this.subtype = subtype; + this.arguments = arguments; + this.openedNamespaces = openedNamespaces; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("<"); + subtype.appendTo(writer, localData); + writer.append(">"); + writer.append("["); + for (int i = 0; i < arguments.size(); i++) { + if (i > 0) { + writer.append(","); + } + arguments.get(i).appendTo(writer, localData); + } + writer.append("]"); + return writer; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + List pars = new ArrayList<>(); + pars.add(subtype); + return new ApplyTypeAVM2Item(null, new TypeItem("__AS3__.vec.Vector"), pars); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + List ret = toSourceMerge(localData, generator, + ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, g.abc.constants.getStringId("Vector", true), 0, g.abc.constants.getNamespaceSetId(new NamespaceSet(new int[]{g.abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, g.abc.constants.getStringId("__AS3__.vec", true)), 0, true)}), true), 0, new ArrayList()), true)), + ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, g.abc.constants.getStringId("Vector", true), 0, allNsSet(g.abc), 0, new ArrayList()), true)), + subtype, + ins(new ApplyTypeIns(), 1), + new IntegerValueAVM2Item(null, (long) arguments.size()), + ins(new ConstructIns(), 1) + ); + for (int i = 0; i < arguments.size(); i++) { + ret.addAll(toSourceMerge(localData, generator, + ins(new DupIns()), + new IntegerValueAVM2Item(null, (long) i), + arguments.get(i), + ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, g.abc.constants.getNamespaceSetId(new NamespaceSet(new int[]{g.abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, g.abc.constants.getStringId("", true)), 0, true)}), true), precedence, openedNamespaces), true)) + )); + } + return ret; + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java index 6b46109b8..bfcb8114f 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/IntegerValueAVM2Item.java @@ -1,82 +1,82 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; -import java.util.Set; - -public class IntegerValueAVM2Item extends NumberValueAVM2Item { - - public Long value; - - public IntegerValueAVM2Item(AVM2Instruction instruction, Long value) { - super(instruction); - this.value = value; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { - return writer.append("" + value); - } - - @Override - public Object getResult() { - return (Double) (double) (long) value; - } - - @Override - public boolean isCompileTime(Set dependencies) { - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - AVM2Instruction ins = null; - if (value >= -128 && value <= 127) { - ins = new AVM2Instruction(0, new PushByteIns(), new int[]{(int)(long)value}, new byte[0]); - } else if (value >= -32768 && value <= 32767) { - ins = new AVM2Instruction(0, new PushShortIns(), new int[]{((int) (long) value) & 0xffff}, new byte[0]); - } else { - ins = new AVM2Instruction(0, new PushIntIns(), new int[]{((AVM2SourceGenerator) generator).abc.constants.getIntId(value, true)}, new byte[0]); - } - - return toSourceMerge(localData, generator, ins); - } - - @Override - public GraphTargetItem returnType() { - return new TypeItem("int"); - } - - @Override - public boolean hasReturnValue() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushIntIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushShortIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.AVM2SourceGenerator; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; +import java.util.Set; + +public class IntegerValueAVM2Item extends NumberValueAVM2Item { + + public Long value; + + public IntegerValueAVM2Item(AVM2Instruction instruction, Long value) { + super(instruction); + this.value = value; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { + return writer.append("" + value); + } + + @Override + public Object getResult() { + return (Double) (double) (long) value; + } + + @Override + public boolean isCompileTime(Set dependencies) { + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + AVM2Instruction ins = null; + if (value >= -128 && value <= 127) { + ins = new AVM2Instruction(0, new PushByteIns(), new int[]{(int) (long) value}, new byte[0]); + } else if (value >= -32768 && value <= 32767) { + ins = new AVM2Instruction(0, new PushShortIns(), new int[]{((int) (long) value) & 0xffff}, new byte[0]); + } else { + ins = new AVM2Instruction(0, new PushIntIns(), new int[]{((AVM2SourceGenerator) generator).abc.constants.getIntId(value, true)}, new byte[0]); + } + + return toSourceMerge(localData, generator, ins); + } + + @Override + public GraphTargetItem returnType() { + return new TypeItem("int"); + } + + @Override + public boolean hasReturnValue() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/DeletePropertyAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/DeletePropertyAVM2Item.java index 51722cda8..3ca18e4ba 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/DeletePropertyAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/model/operations/DeletePropertyAVM2Item.java @@ -1,96 +1,96 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.IndexAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.NamespacedAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.UnresolvedAVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -public class DeletePropertyAVM2Item extends AVM2Item { - - public GraphTargetItem object; - public GraphTargetItem propertyName; - - private int line; - - //Constructor for compiler - public DeletePropertyAVM2Item(GraphTargetItem property, int line) { - this(null, property, null); - this.line = line; - } - - public DeletePropertyAVM2Item(AVM2Instruction instruction, GraphTargetItem object, GraphTargetItem propertyName) { - super(instruction, PRECEDENCE_UNARY); - this.object = object; - this.propertyName = propertyName; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("delete "); - formatProperty(writer, object, propertyName, localData); - return writer; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - GraphTargetItem p = object; - if (p instanceof UnresolvedAVM2Item) { - p = ((UnresolvedAVM2Item) p).resolved; - } - if (p instanceof PropertyAVM2Item) { - PropertyAVM2Item prop=(PropertyAVM2Item)p; - return toSourceMerge(localData, generator, prop.resolveObject(localData, generator), - ins(new DeletePropertyIns(), prop.resolveProperty(localData)) - ); - } - if(p instanceof IndexAVM2Item){ - IndexAVM2Item ind=(IndexAVM2Item)p; - return ind.toSource(localData, generator, true, false, new ArrayList(), true,false); - } - if(p instanceof NamespacedAVM2Item){ - NamespacedAVM2Item n=(NamespacedAVM2Item)p; - return n.toSource(localData, generator, true, false, new ArrayList(), true,false); - } - - throw new CompilationException("Not a property", line); //TODO: handle line better way - } - - @Override - public GraphTargetItem returnType() { - return new TypeItem("Boolean"); - } - - @Override - public boolean hasReturnValue() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.IndexAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.NamespacedAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.PropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.UnresolvedAVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +public class DeletePropertyAVM2Item extends AVM2Item { + + public GraphTargetItem object; + public GraphTargetItem propertyName; + + private int line; + + //Constructor for compiler + public DeletePropertyAVM2Item(GraphTargetItem property, int line) { + this(null, property, null); + this.line = line; + } + + public DeletePropertyAVM2Item(AVM2Instruction instruction, GraphTargetItem object, GraphTargetItem propertyName) { + super(instruction, PRECEDENCE_UNARY); + this.object = object; + this.propertyName = propertyName; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("delete "); + formatProperty(writer, object, propertyName, localData); + return writer; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + GraphTargetItem p = object; + if (p instanceof UnresolvedAVM2Item) { + p = ((UnresolvedAVM2Item) p).resolved; + } + if (p instanceof PropertyAVM2Item) { + PropertyAVM2Item prop = (PropertyAVM2Item) p; + return toSourceMerge(localData, generator, prop.resolveObject(localData, generator), + ins(new DeletePropertyIns(), prop.resolveProperty(localData)) + ); + } + if (p instanceof IndexAVM2Item) { + IndexAVM2Item ind = (IndexAVM2Item) p; + return ind.toSource(localData, generator, true, false, new ArrayList(), true, false); + } + if (p instanceof NamespacedAVM2Item) { + NamespacedAVM2Item n = (NamespacedAVM2Item) p; + return n.toSource(localData, generator, true, false, new ArrayList(), true, false); + } + + throw new CompilationException("Not a property", line); //TODO: handle line better way + } + + @Override + public GraphTargetItem returnType() { + return new TypeItem("Boolean"); + } + + @Override + public boolean hasReturnValue() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java index 85865646e..facbb3535 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/pcode/ASM3Parser.java @@ -1,969 +1,969 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.pcode; - -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.configuration.Configuration; -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -public class ASM3Parser { - - private static class OffsetItem { - - public String label = ""; - public long insPosition; - public int insOperandIndex; - - public OffsetItem(String label, long insOffset, int insOperandIndex) { - this.label = label; - this.insPosition = insOffset; - this.insOperandIndex = insOperandIndex; - } - } - - private static class CaseOffsetItem extends OffsetItem { - - public CaseOffsetItem(String label, long insOffset, int insOperandIndex) { - super(label, insOffset, insOperandIndex); - } - } - - private static class LabelItem { - - public String label = ""; - public int offset; - - public LabelItem(String label, int offset) { - this.label = label; - this.offset = offset; - } - } - - public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { - return parse(reader, constants, trait, null, body, info); - } - - private static int checkMultinameIndex(ConstantPool constants, int index, int line) throws ParseException { - if ((index < 0) || (index >= constants.getMultinameCount())) { - throw new ParseException("Invalid multiname index", line); - } - return index; - } - - private static void expected(int type, String expStr, Flasm3Lexer lexer) throws IOException, ParseException { - ParsedSymbol s = lexer.lex(); - if (s.type != type) { - throw new ParseException(expStr + " expected", lexer.yyline()); - } - } - - private static void expected(ParsedSymbol s, int type, String expStr) throws IOException, ParseException { - if (s.type != type) { - throw new ParseException(expStr + " expected", 0); - } - } - - public static boolean parseSlotConst(Reader reader, ConstantPool constants, TraitSlotConst tsc) throws IOException, ParseException { - Flasm3Lexer lexer = new Flasm3Lexer(reader); - expected(ParsedSymbol.TYPE_KEYWORD_TRAIT, "trait", lexer); - int name_index = parseMultiName(constants, lexer); - - ParsedSymbol symb = lexer.lex(); - - int flags = 0; - while (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) { - symb = lexer.lex(); - switch (symb.type) { - case ParsedSymbol.TYPE_KEYWORD_FINAL: - flags |= Trait.ATTR_Final; - break; - case ParsedSymbol.TYPE_KEYWORD_OVERRIDE: - flags |= Trait.ATTR_Override; - break; - case ParsedSymbol.TYPE_KEYWORD_METADATA: - flags |= Trait.ATTR_Metadata; - break; - default: - throw new ParseException("Invalid trait flag", lexer.yyline()); - } - symb = lexer.lex(); - } - - switch (symb.type) { - case ParsedSymbol.TYPE_KEYWORD_SLOT: - case ParsedSymbol.TYPE_KEYWORD_CONST: - expected(ParsedSymbol.TYPE_KEYWORD_SLOTID, "slotid", lexer); - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - int slotid = (int) (long) (Long) symb.value; - expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "type", lexer); - int type = parseMultiName(constants, lexer); - expected(ParsedSymbol.TYPE_KEYWORD_VALUE, "value", lexer); - ValueKind val = parseValue(constants, lexer); - tsc.slot_id = slotid; - tsc.type_index = type; - tsc.value_kind = val.value_kind; - tsc.value_index = val.value_index; - tsc.kindFlags = flags; - break; - /*case ParsedSymbol.TYPE_KEYWORD_CLASS: - break; - case ParsedSymbol.TYPE_KEYWORD_FUNCTION: - break; - case ParsedSymbol.TYPE_KEYWORD_METHOD: - case ParsedSymbol.TYPE_KEYWORD_GETTER: - case ParsedSymbol.TYPE_KEYWORD_SETTER: - break;*/ - default: - throw new ParseException("Unexpected trait type", lexer.yyline()); - } - tsc.name_index = name_index; - return true; - } - - private static int parseNamespaceSet(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { - List namespaceList = new ArrayList<>(); - ParsedSymbol s = lexer.lex(); - if (s.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - return 0; - } - expected(s, ParsedSymbol.TYPE_BRACKET_OPEN, "["); - s = lexer.lex(); - if (s.type != ParsedSymbol.TYPE_BRACKET_CLOSE) { - lexer.pushback(s); - do { - namespaceList.add(parseNamespace(constants, lexer)); - s = lexer.lex(); - } while (s.type == ParsedSymbol.TYPE_COMMA); - expected(s, ParsedSymbol.TYPE_BRACKET_CLOSE, "]"); - } - loopn: - for (int n = 1; n < constants.getNamespaceSetCount(); n++) { - int nss[] = constants.getNamespaceSet(n).namespaces; - if (nss.length != namespaceList.size()) { - continue; - } - for (int i = 0; i < nss.length; i++) { - if (nss[i] != namespaceList.get(i)) { - continue loopn; - } - } - return n; - } - int nss[] = new int[namespaceList.size()]; - for (int i = 0; i < nss.length; i++) { - nss[i] = namespaceList.get(i); - } - return constants.addNamespaceSet(new NamespaceSet(nss)); - } - - private static int parseNamespace(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { - - ParsedSymbol type = lexer.lex(); - int kind = 0; - switch (type.type) { - case ParsedSymbol.TYPE_KEYWORD_NULL: - return 0; - case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: - kind = Namespace.KIND_NAMESPACE; - break; - case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: - kind = Namespace.KIND_PRIVATE; - break; - case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: - kind = Namespace.KIND_PACKAGE; - break; - case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: - kind = Namespace.KIND_PACKAGE_INTERNAL; - break; - case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: - kind = Namespace.KIND_PROTECTED; - break; - case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: - kind = Namespace.KIND_EXPLICIT; - break; - case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: - kind = Namespace.KIND_STATIC_PROTECTED; - break; - default: - throw new ParseException("Namespace kind expected", lexer.yyline()); - } - - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - ParsedSymbol name = lexer.lex(); - if (name.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - - } else if (name.type == ParsedSymbol.TYPE_STRING) { - - } else { - throw new ParseException("String or null expected", lexer.yyline()); - } - ParsedSymbol c = lexer.lex(); - int index = 0; - if (c.type == ParsedSymbol.TYPE_COMMA) { - ParsedSymbol extra = lexer.lex(); - expected(extra, ParsedSymbol.TYPE_STRING, "String"); - try { - index = Integer.parseInt((String) extra.value); - } catch (NumberFormatException nfe) { - throw new ParseException("Number expected", lexer.yyline()); - } - } else { - lexer.pushback(c); - } - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - - return constants.getNamespaceId(new Namespace(kind, name.type == ParsedSymbol.TYPE_KEYWORD_NULL ? 0 : constants.getStringId((String) name.value, true)), index, true); - } - - private static int parseMultiName(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { - ParsedSymbol s = lexer.lex(); - int kind = 0; - int name_index = -1; - int namespace_index = -1; - int namespace_set_index = -1; - int qname_index = -1; - List params = new ArrayList<>(); - - switch (s.type) { - case ParsedSymbol.TYPE_KEYWORD_NULL: - return 0; - case ParsedSymbol.TYPE_KEYWORD_QNAME: - kind = Multiname.QNAME; - break; - case ParsedSymbol.TYPE_KEYWORD_QNAMEA: - kind = Multiname.QNAMEA; - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAME: - kind = Multiname.RTQNAME; - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAMEA: - kind = Multiname.RTQNAMEA; - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAMEL: - kind = Multiname.RTQNAMEL; - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAMELA: - kind = Multiname.RTQNAMELA; - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAME: - kind = Multiname.MULTINAME; - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAMEA: - kind = Multiname.MULTINAMEA; - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAMEL: - kind = Multiname.MULTINAMEL; - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAMELA: - kind = Multiname.MULTINAMELA; - break; - case ParsedSymbol.TYPE_KEYWORD_TYPENAME: - kind = Multiname.TYPENAME; - break; - default: - throw new ParseException("Name expected", lexer.yyline()); - } - - switch (s.type) { - case ParsedSymbol.TYPE_KEYWORD_QNAME: - case ParsedSymbol.TYPE_KEYWORD_QNAMEA: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - namespace_index = parseNamespace(constants, lexer); - expected(ParsedSymbol.TYPE_COMMA, ",", lexer); - ParsedSymbol name = lexer.lex(); - if(name.type == ParsedSymbol.TYPE_KEYWORD_NULL){ - name_index = 0; - }else{ - expected(name, ParsedSymbol.TYPE_STRING, "String"); - name_index = constants.getStringId((String) name.value, true); - } - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAME: - case ParsedSymbol.TYPE_KEYWORD_RTQNAMEA: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - ParsedSymbol rtqName = lexer.lex(); - if(rtqName.type == ParsedSymbol.TYPE_KEYWORD_NULL){ - name_index = 0; - }else{ - expected(rtqName, ParsedSymbol.TYPE_STRING, "String"); - name_index = constants.getStringId((String) rtqName.value, true); - } - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_RTQNAMEL: - case ParsedSymbol.TYPE_KEYWORD_RTQNAMELA: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAME: - case ParsedSymbol.TYPE_KEYWORD_MULTINAMEA: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - ParsedSymbol mName = lexer.lex(); - if(mName.type == ParsedSymbol.TYPE_KEYWORD_NULL){ - name_index = 0; - }else{ - expected(mName, ParsedSymbol.TYPE_STRING, "String"); - name_index = constants.getStringId((String) mName.value, true); - } - expected(ParsedSymbol.TYPE_COMMA, ",", lexer); - namespace_set_index = parseNamespaceSet(constants, lexer); - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_MULTINAMEL: - case ParsedSymbol.TYPE_KEYWORD_MULTINAMELA: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - namespace_set_index = parseNamespaceSet(constants, lexer); - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_TYPENAME: - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - qname_index = parseMultiName(constants, lexer); - expected(ParsedSymbol.TYPE_LOWERTHAN, "<", lexer); - params.add(parseMultiName(constants, lexer)); - ParsedSymbol nt = lexer.lex(); - while (nt.type == ParsedSymbol.TYPE_COMMA) { - params.add(parseMultiName(constants, lexer)); - nt = lexer.lex(); - } - expected(nt, ParsedSymbol.TYPE_GREATERTHAN, ">"); - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - } - - return constants.getMultinameId(new Multiname(kind, name_index, namespace_index, namespace_set_index, qname_index, params), true); - } - - public static ValueKind parseValue(ConstantPool constants, Flasm3Lexer lexer) throws IOException, ParseException { - ParsedSymbol type = lexer.lex(); - ParsedSymbol value; - int value_index = 0; - int value_kind = 0; - switch (type.type) { - case ParsedSymbol.TYPE_KEYWORD_INTEGER: - value_kind = ValueKind.CONSTANT_Int; - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - value = lexer.lex(); - if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - value_index = 0; - } else { - expected(value, ParsedSymbol.TYPE_INTEGER, "Integer or null"); - value_index = constants.getIntId((Long) value.value, true); - } - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_UINTEGER: - value_kind = ValueKind.CONSTANT_UInt; - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - value = lexer.lex(); - if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - value_index = 0; - } else { - expected(value, ParsedSymbol.TYPE_INTEGER, "UInteger"); - value_index = constants.getUIntId((Long) value.value, true); - } - - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - case ParsedSymbol.TYPE_KEYWORD_DOUBLE: - value_kind = ValueKind.CONSTANT_Double; - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - value = lexer.lex(); - if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - value_index = 0; - } else { - expected(value, ParsedSymbol.TYPE_FLOAT, "Double or null"); - value_index = constants.getDoubleId((Double) value.value, true); - } - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - break; - /*case ParsedSymbol.TYPE_KEYWORD_DECIMAL: - value_kind = ValueKind.CONSTANT_Decimal; - break;*/ - case ParsedSymbol.TYPE_INTEGER: - value_kind = ValueKind.CONSTANT_Int; - value_index = constants.getIntId((Long) type.value, true); - break; - case ParsedSymbol.TYPE_FLOAT: - value_kind = ValueKind.CONSTANT_Double; - value_index = constants.getDoubleId((Double) type.value, true); - break; - case ParsedSymbol.TYPE_STRING: - value_kind = ValueKind.CONSTANT_Utf8; - value_index = constants.getStringId((String) type.value, true); - break; - case ParsedSymbol.TYPE_KEYWORD_UTF8: - value_kind = ValueKind.CONSTANT_Utf8; - expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); - value = lexer.lex(); - if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - value_index = 0; - } else { - expected(value, ParsedSymbol.TYPE_STRING, "String or null"); - expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); - value_index = constants.getStringId((String) value.value, true); - } - break; - case ParsedSymbol.TYPE_KEYWORD_TRUE: - value_kind = ValueKind.CONSTANT_True; - break; - case ParsedSymbol.TYPE_KEYWORD_FALSE: - value_kind = ValueKind.CONSTANT_False; - break; - case ParsedSymbol.TYPE_KEYWORD_NULL: - value_kind = ValueKind.CONSTANT_Null; - break; - case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: - case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: - case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: - case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: - case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: - case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: - case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: - - switch (type.type) { - case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: - value_kind = ValueKind.CONSTANT_Namespace; - break; - case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: - value_kind = ValueKind.CONSTANT_PackageInternalNs; - break; - case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: - value_kind = ValueKind.CONSTANT_ProtectedNamespace; - break; - case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: - value_kind = ValueKind.CONSTANT_ExplicitNamespace; - break; - case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: - value_kind = ValueKind.CONSTANT_StaticProtectedNs; - break; - case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: - value_kind = ValueKind.CONSTANT_PrivateNs; - break; - case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: - value_kind = ValueKind.CONSTANT_PackageNamespace; - break; - } - lexer.pushback(type); - value_index = parseNamespace(constants, lexer); - break; - default: - if (Configuration.debugMode.get()) { - throw new ParseException("Not supported valueType.", lexer.yyline()); - } - } - return new ValueKind(value_index, value_kind); - } - - public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { - AVM2Code code = new AVM2Code(); - - List offsetItems = new ArrayList<>(); - List labelItems = new ArrayList<>(); - List exceptions = new ArrayList<>(); - List exceptionIndices = new ArrayList<>(); - int offset = 0; - - Flasm3Lexer lexer = new Flasm3Lexer(reader); - - ParsedSymbol symb; - AVM2Instruction lastIns = null; - List exceptionsFrom = new ArrayList<>(); - List exceptionsTo = new ArrayList<>(); - List exceptionsTargets = new ArrayList<>(); - info.flags = 0; - info.name_index = 0; - List paramTypes = new ArrayList<>(); - List paramNames = new ArrayList<>(); - List optional = new ArrayList<>(); - do { - symb = lexer.lex(); - if (Arrays.asList(ParsedSymbol.TYPE_KEYWORD_BODY, ParsedSymbol.TYPE_KEYWORD_CODE, ParsedSymbol.TYPE_KEYWORD_METHOD).contains(symb.type)) { - continue; - } - if (symb.type == ParsedSymbol.TYPE_KEYWORD_TRAIT) { - if (trait == null) { - throw new ParseException("No trait expected", lexer.yyline()); - } - symb = lexer.lex(); - switch (symb.type) { - case ParsedSymbol.TYPE_KEYWORD_METHOD: - case ParsedSymbol.TYPE_KEYWORD_GETTER: - case ParsedSymbol.TYPE_KEYWORD_SETTER: - if (!(trait instanceof TraitMethodGetterSetter)) { - throw new ParseException("Unxpected trait type", lexer.yyline()); - } - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; - switch (symb.type) { - case ParsedSymbol.TYPE_KEYWORD_METHOD: - tm.kindType = Trait.TRAIT_METHOD; - break; - case ParsedSymbol.TYPE_KEYWORD_GETTER: - tm.kindType = Trait.TRAIT_GETTER; - break; - case ParsedSymbol.TYPE_KEYWORD_SETTER: - tm.kindType = Trait.TRAIT_SETTER; - break; - } - tm.name_index = parseMultiName(constants, lexer); - expected(ParsedSymbol.TYPE_KEYWORD_DISPID, "dispid", lexer); - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - tm.disp_id = (int) (long) (Long) symb.value; - - break; - case ParsedSymbol.TYPE_KEYWORD_FUNCTION: - if (!(trait instanceof TraitFunction)) { - throw new ParseException("Unxpected trait type", lexer.yyline()); - } - break; - - } - continue; - } - if (symb.type == ParsedSymbol.TYPE_KEYWORD_NAME) { - symb = lexer.lex(); - if (symb.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - info.name_index = 0; - } else { - expected(symb, ParsedSymbol.TYPE_STRING, "String or null"); - info.name_index = constants.getStringId((String) symb.value, true); - } - continue; - } - if (symb.type == ParsedSymbol.TYPE_KEYWORD_PARAM) { - paramTypes.add(parseMultiName(constants, lexer)); - continue; - } - if (symb.type == ParsedSymbol.TYPE_KEYWORD_PARAMNAME) { - symb = lexer.lex(); - if (symb.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - paramNames.add(0); - } else { - expected(symb, ParsedSymbol.TYPE_STRING, "String or null"); - paramNames.add(constants.getStringId((String) symb.value, true)); - } - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_OPTIONAL) { - optional.add(parseValue(constants, lexer)); - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_MAXSTACK) { - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - body.max_stack = (int) (long) (Long) symb.value; - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_LOCALCOUNT) { - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - body.max_regs = (int) (long) (Long) symb.value; - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_INITSCOPEDEPTH) { - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - body.init_scope_depth = (int) (long) (Long) symb.value; - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_MAXSCOPEDEPTH) { - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); - body.max_scope_depth = (int) (long) (Long) symb.value; - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_RETURNS) { - info.ret_type = parseMultiName(constants, lexer); - continue; - } - - if (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) { - symb = lexer.lex(); - switch (symb.type) { - case ParsedSymbol.TYPE_KEYWORD_EXPLICIT: - info.setFlagExplicit(); - break; - case ParsedSymbol.TYPE_KEYWORD_HAS_OPTIONAL: - info.setFlagHas_optional(); - break; - case ParsedSymbol.TYPE_KEYWORD_HAS_PARAM_NAMES: - info.setFlagHas_paramnames(); - break; - case ParsedSymbol.TYPE_KEYWORD_IGNORE_REST: - info.setFlagIgnore_Rest(); - break; - case ParsedSymbol.TYPE_KEYWORD_NEED_ACTIVATION: - info.setFlagNeed_activation(); - break; - case ParsedSymbol.TYPE_KEYWORD_NEED_ARGUMENTS: - info.setFlagNeed_Arguments(); - break; - case ParsedSymbol.TYPE_KEYWORD_NEED_REST: - info.setFlagNeed_rest(); - break; - case ParsedSymbol.TYPE_KEYWORD_SET_DXNS: - info.setFlagSetsdxns(); - break; - } - continue; - } - if (symb.type == ParsedSymbol.TYPE_KEYWORD_TRY) { - expected(ParsedSymbol.TYPE_KEYWORD_FROM, "From", lexer); - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); - exceptionsFrom.add((String) symb.value); - expected(ParsedSymbol.TYPE_KEYWORD_TO, "To", lexer); - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); - exceptionsTo.add((String) symb.value); - expected(ParsedSymbol.TYPE_KEYWORD_TARGET, "Target", lexer); - symb = lexer.lex(); - expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); - exceptionsTargets.add((String) symb.value); - expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "Type", lexer); - ABCException ex = new ABCException(); - ex.type_index = parseMultiName(constants, lexer); - expected(ParsedSymbol.TYPE_KEYWORD_NAME, "Name", lexer); - ex.name_index = parseMultiName(constants, lexer); - exceptions.add(ex); - continue; - } - if (symb.type == ParsedSymbol.TYPE_EXCEPTION_START) { - int exIndex = (Integer) symb.value; - int listIndex = exceptionIndices.indexOf(exIndex); - if (listIndex == -1) { - throw new ParseException("Undefinex exception index", lexer.yyline()); - } - exceptions.get(listIndex).start = offset; - continue; - } - if (symb.type == ParsedSymbol.TYPE_EXCEPTION_END) { - int exIndex = (Integer) symb.value; - int listIndex = exceptionIndices.indexOf(exIndex); - if (listIndex == -1) { - throw new ParseException("Undefinex exception index", lexer.yyline()); - } - exceptions.get(listIndex).end = offset; - continue; - } - if (symb.type == ParsedSymbol.TYPE_EXCEPTION_TARGET) { - int exIndex = (Integer) symb.value; - int listIndex = exceptionIndices.indexOf(exIndex); - if (listIndex == -1) { - throw new ParseException("Undefinex exception index", lexer.yyline()); - } - exceptions.get(listIndex).target = offset; - continue; - } - if (symb.type == ParsedSymbol.TYPE_EOF) { - break; - } - if (symb.type == ParsedSymbol.TYPE_COMMENT) { - if (lastIns != null) { - lastIns.comment = (String) symb.value; - } - continue; - } - if (symb.type == ParsedSymbol.TYPE_INSTRUCTION_NAME) { - if (((String) symb.value).toLowerCase(Locale.ENGLISH).equals("exception")) { - ParsedSymbol exIndex = lexer.lex(); - if (exIndex.type != ParsedSymbol.TYPE_INTEGER) { - throw new ParseException("Index expected", lexer.yyline()); - } - ParsedSymbol exName = lexer.lex(); - if (exName.type != ParsedSymbol.TYPE_MULTINAME) { - throw new ParseException("Multiname expected", lexer.yyline()); - } - ParsedSymbol exType = lexer.lex(); - if (exType.type != ParsedSymbol.TYPE_MULTINAME) { - throw new ParseException("Multiname expected", lexer.yyline()); - } - ABCException ex = new ABCException(); - - ex.name_index = checkMultinameIndex(constants, (int) (long) (Long) exName.value, lexer.yyline()); - ex.type_index = checkMultinameIndex(constants, (int) (long) (Long) exType.value, lexer.yyline()); - exceptions.add(ex); - exceptionIndices.add((int) (long) (Long) exIndex.value); - continue; - } - boolean insFound = false; - for (InstructionDefinition def : AVM2Code.instructionSet) { - if (def.instructionName.equals((String) symb.value)) { - insFound = true; - List operandsList = new ArrayList<>(); - - for (int i = 0; i < def.operands.length; i++) { - ParsedSymbol parsedOperand = lexer.lex(); - switch (def.operands[i]) { - case AVM2Code.DAT_MULTINAME_INDEX: - lexer.pushback(parsedOperand); - operandsList.add(parseMultiName(constants, lexer)); - /*if (parsedOperand.type == ParsedSymbol.TYPE_MULTINAME) { - operandsList.add(checkMultinameIndex(constants, (int) (long) (Long) parsedOperand.value, lexer.yyline())); - } else { - throw new ParseException("Multiname expected", lexer.yyline()); - }*/ - break; - case AVM2Code.DAT_STRING_INDEX: - if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - operandsList.add(0); - } else { - if (parsedOperand.type == ParsedSymbol.TYPE_STRING) { - int sid = constants.getStringId((String) parsedOperand.value); - if (sid == 0) { - if ((missingHandler != null) && (missingHandler.missingString((String) parsedOperand.value))) { - sid = constants.addString((String) parsedOperand.value); - } else { - throw new ParseException("Unknown String", lexer.yyline()); - } - } - operandsList.add(sid); - } else { - throw new ParseException("String or null expected", lexer.yyline()); - } - } - break; - case AVM2Code.DAT_INT_INDEX: - - if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - operandsList.add(0); - } else { - if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { - long intVal = (Long) parsedOperand.value; - int iid = constants.getIntId(intVal); - if (iid == 0) { - if ((missingHandler != null) && (missingHandler.missingInt(intVal))) { - iid = constants.addInt(intVal); - } else { - throw new ParseException("Unknown int", lexer.yyline()); - } - } - operandsList.add(iid); - } else { - throw new ParseException("Integer or null expected", lexer.yyline()); - } - } - break; - case AVM2Code.DAT_UINT_INDEX: - if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - operandsList.add(0); - } else { - if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { - long intVal = (Long) parsedOperand.value; - int iid = constants.getUIntId(intVal); - if (iid == 0) { - if ((missingHandler != null) && (missingHandler.missingUInt(intVal))) { - iid = constants.addUInt(intVal); - } else { - throw new ParseException("Unknown uint", lexer.yyline()); - } - } - operandsList.add(iid); - } else { - throw new ParseException("Integer or null expected", lexer.yyline()); - } - } - break; - case AVM2Code.DAT_DOUBLE_INDEX: - if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { - operandsList.add(0); - } else { - if ((parsedOperand.type == ParsedSymbol.TYPE_INTEGER) || (parsedOperand.type == ParsedSymbol.TYPE_FLOAT)) { - - double doubleVal = 0; - if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { - doubleVal = (Long) parsedOperand.value; - } - if (parsedOperand.type == ParsedSymbol.TYPE_FLOAT) { - doubleVal = (Double) parsedOperand.value; - } - int did = constants.getDoubleId(doubleVal); - if (did == 0) { - if ((missingHandler != null) && (missingHandler.missingDouble(doubleVal))) { - did = constants.addDouble(doubleVal); - } else { - throw new ParseException("Unknown double", lexer.yyline()); - } - } - operandsList.add(did); - } else { - throw new ParseException("Float or null expected", lexer.yyline()); - } - } - break; - case AVM2Code.DAT_OFFSET: - if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { - offsetItems.add(new OffsetItem((String) parsedOperand.value, code.code.size(), i)); - operandsList.add(0); - } else { - throw new ParseException("Offset expected", lexer.yyline()); - } - break; - case AVM2Code.DAT_CASE_BASEOFFSET: - if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { - offsetItems.add(new CaseOffsetItem((String) parsedOperand.value, code.code.size(), i)); - operandsList.add(0); - } else { - throw new ParseException("Offset expected", lexer.yyline()); - } - break; - case AVM2Code.OPT_CASE_OFFSETS: - - if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { - int patCount = (int) (long) (Long) parsedOperand.value; - operandsList.add(patCount); - - for (int c = 0; c <= patCount; c++) { - parsedOperand = lexer.lex(); - if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { - offsetItems.add(new CaseOffsetItem((String) parsedOperand.value, code.code.size(), i + (c + 1))); - operandsList.add(0); - } else { - throw new ParseException("Offset expected", lexer.yyline()); - } - } - } else { - throw new ParseException("Case count expected", lexer.yyline()); - } - break; - default: - if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { - operandsList.add((int) (long) (Long) parsedOperand.value); - } else { - throw new ParseException("Integer expected", lexer.yyline()); - } - } - } - - int[] operands = new int[operandsList.size()]; - for (int i = 0; i < operandsList.size(); i++) { - operands[i] = operandsList.get(i); - } - lastIns = new AVM2Instruction(offset, def, operands, new byte[0]); - code.code.add(lastIns); - offset += lastIns.getBytes().length; - break; - } - } - if (symb.value.toString().toLowerCase().equals("ffdec_deobfuscatepop")) { - lastIns = new AVM2Instruction(offset, new DeobfuscatePopIns(), new int[0], new byte[0]); - code.code.add(lastIns); - offset += lastIns.getBytes().length; - insFound = true; - } - if (!insFound) { - throw new ParseException("Invalid instruction name:" + (String) symb.value, lexer.yyline()); - } - } else if (symb.type == ParsedSymbol.TYPE_LABEL) { - labelItems.add(new LabelItem((String) symb.value, offset)); - - } else { - throw new ParseException("Unexpected symbol", lexer.yyline()); - } - } while (symb.type != ParsedSymbol.TYPE_EOF); - - code.compact(); - for (LabelItem li : labelItems) { - int ind; - ind = exceptionsFrom.indexOf(li.label); - if (ind > -1) { - exceptions.get(ind).start = li.offset; - } - - ind = exceptionsTo.indexOf(li.label); - if (ind > -1) { - exceptions.get(ind).end = li.offset; - } - - ind = exceptionsTargets.indexOf(li.label); - if (ind > -1) { - exceptions.get(ind).target = li.offset; - } - } - - for (OffsetItem oi : offsetItems) { - for (LabelItem li : labelItems) { - if (Thread.currentThread().isInterrupted()) { - throw new InterruptedException(); - } - if (oi.label.equals(li.label)) { - AVM2Instruction ins = code.code.get((int) oi.insPosition); - int relOffset; - if (oi instanceof CaseOffsetItem) { - relOffset = li.offset - (int) ins.offset; - } else { - relOffset = li.offset - ((int) ins.offset + ins.getBytes().length); - } - ins.operands[oi.insOperandIndex] = relOffset; - } - } - } - body.exceptions = new ABCException[exceptions.size()]; - for (int e = 0; e < exceptions.size(); e++) { - body.exceptions[e] = exceptions.get(e); - } - - info.param_types = new int[paramTypes.size()]; - for (int i = 0; i < paramTypes.size(); i++) { - info.param_types[i] = paramTypes.get(i); - } - - if (info.flagHas_paramnames()) { - info.paramNames = new int[paramNames.size()]; - for (int i = 0; i < paramNames.size(); i++) { - info.paramNames[i] = paramNames.get(i); - } - } - - if (info.flagHas_optional()) { - info.optional = new ValueKind[optional.size()]; - for (int i = 0; i < optional.size(); i++) { - info.optional[i] = optional.get(i); - } - } - return code; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.pcode; + +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.DeobfuscatePopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.configuration.Configuration; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +public class ASM3Parser { + + private static class OffsetItem { + + public String label = ""; + public long insPosition; + public int insOperandIndex; + + public OffsetItem(String label, long insOffset, int insOperandIndex) { + this.label = label; + this.insPosition = insOffset; + this.insOperandIndex = insOperandIndex; + } + } + + private static class CaseOffsetItem extends OffsetItem { + + public CaseOffsetItem(String label, long insOffset, int insOperandIndex) { + super(label, insOffset, insOperandIndex); + } + } + + private static class LabelItem { + + public String label = ""; + public int offset; + + public LabelItem(String label, int offset) { + this.label = label; + this.offset = offset; + } + } + + public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { + return parse(reader, constants, trait, null, body, info); + } + + private static int checkMultinameIndex(ConstantPool constants, int index, int line) throws ParseException { + if ((index < 0) || (index >= constants.getMultinameCount())) { + throw new ParseException("Invalid multiname index", line); + } + return index; + } + + private static void expected(int type, String expStr, Flasm3Lexer lexer) throws IOException, ParseException { + ParsedSymbol s = lexer.lex(); + if (s.type != type) { + throw new ParseException(expStr + " expected", lexer.yyline()); + } + } + + private static void expected(ParsedSymbol s, int type, String expStr) throws IOException, ParseException { + if (s.type != type) { + throw new ParseException(expStr + " expected", 0); + } + } + + public static boolean parseSlotConst(Reader reader, ConstantPool constants, TraitSlotConst tsc) throws IOException, ParseException { + Flasm3Lexer lexer = new Flasm3Lexer(reader); + expected(ParsedSymbol.TYPE_KEYWORD_TRAIT, "trait", lexer); + int name_index = parseMultiName(constants, lexer); + + ParsedSymbol symb = lexer.lex(); + + int flags = 0; + while (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) { + symb = lexer.lex(); + switch (symb.type) { + case ParsedSymbol.TYPE_KEYWORD_FINAL: + flags |= Trait.ATTR_Final; + break; + case ParsedSymbol.TYPE_KEYWORD_OVERRIDE: + flags |= Trait.ATTR_Override; + break; + case ParsedSymbol.TYPE_KEYWORD_METADATA: + flags |= Trait.ATTR_Metadata; + break; + default: + throw new ParseException("Invalid trait flag", lexer.yyline()); + } + symb = lexer.lex(); + } + + switch (symb.type) { + case ParsedSymbol.TYPE_KEYWORD_SLOT: + case ParsedSymbol.TYPE_KEYWORD_CONST: + expected(ParsedSymbol.TYPE_KEYWORD_SLOTID, "slotid", lexer); + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + int slotid = (int) (long) (Long) symb.value; + expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "type", lexer); + int type = parseMultiName(constants, lexer); + expected(ParsedSymbol.TYPE_KEYWORD_VALUE, "value", lexer); + ValueKind val = parseValue(constants, lexer); + tsc.slot_id = slotid; + tsc.type_index = type; + tsc.value_kind = val.value_kind; + tsc.value_index = val.value_index; + tsc.kindFlags = flags; + break; + /*case ParsedSymbol.TYPE_KEYWORD_CLASS: + break; + case ParsedSymbol.TYPE_KEYWORD_FUNCTION: + break; + case ParsedSymbol.TYPE_KEYWORD_METHOD: + case ParsedSymbol.TYPE_KEYWORD_GETTER: + case ParsedSymbol.TYPE_KEYWORD_SETTER: + break;*/ + default: + throw new ParseException("Unexpected trait type", lexer.yyline()); + } + tsc.name_index = name_index; + return true; + } + + private static int parseNamespaceSet(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { + List namespaceList = new ArrayList<>(); + ParsedSymbol s = lexer.lex(); + if (s.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + return 0; + } + expected(s, ParsedSymbol.TYPE_BRACKET_OPEN, "["); + s = lexer.lex(); + if (s.type != ParsedSymbol.TYPE_BRACKET_CLOSE) { + lexer.pushback(s); + do { + namespaceList.add(parseNamespace(constants, lexer)); + s = lexer.lex(); + } while (s.type == ParsedSymbol.TYPE_COMMA); + expected(s, ParsedSymbol.TYPE_BRACKET_CLOSE, "]"); + } + loopn: + for (int n = 1; n < constants.getNamespaceSetCount(); n++) { + int nss[] = constants.getNamespaceSet(n).namespaces; + if (nss.length != namespaceList.size()) { + continue; + } + for (int i = 0; i < nss.length; i++) { + if (nss[i] != namespaceList.get(i)) { + continue loopn; + } + } + return n; + } + int nss[] = new int[namespaceList.size()]; + for (int i = 0; i < nss.length; i++) { + nss[i] = namespaceList.get(i); + } + return constants.addNamespaceSet(new NamespaceSet(nss)); + } + + private static int parseNamespace(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { + + ParsedSymbol type = lexer.lex(); + int kind = 0; + switch (type.type) { + case ParsedSymbol.TYPE_KEYWORD_NULL: + return 0; + case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: + kind = Namespace.KIND_NAMESPACE; + break; + case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: + kind = Namespace.KIND_PRIVATE; + break; + case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: + kind = Namespace.KIND_PACKAGE; + break; + case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: + kind = Namespace.KIND_PACKAGE_INTERNAL; + break; + case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: + kind = Namespace.KIND_PROTECTED; + break; + case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: + kind = Namespace.KIND_EXPLICIT; + break; + case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: + kind = Namespace.KIND_STATIC_PROTECTED; + break; + default: + throw new ParseException("Namespace kind expected", lexer.yyline()); + } + + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + ParsedSymbol name = lexer.lex(); + if (name.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + + } else if (name.type == ParsedSymbol.TYPE_STRING) { + + } else { + throw new ParseException("String or null expected", lexer.yyline()); + } + ParsedSymbol c = lexer.lex(); + int index = 0; + if (c.type == ParsedSymbol.TYPE_COMMA) { + ParsedSymbol extra = lexer.lex(); + expected(extra, ParsedSymbol.TYPE_STRING, "String"); + try { + index = Integer.parseInt((String) extra.value); + } catch (NumberFormatException nfe) { + throw new ParseException("Number expected", lexer.yyline()); + } + } else { + lexer.pushback(c); + } + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + + return constants.getNamespaceId(new Namespace(kind, name.type == ParsedSymbol.TYPE_KEYWORD_NULL ? 0 : constants.getStringId((String) name.value, true)), index, true); + } + + private static int parseMultiName(ConstantPool constants, Flasm3Lexer lexer) throws ParseException, IOException { + ParsedSymbol s = lexer.lex(); + int kind = 0; + int name_index = -1; + int namespace_index = -1; + int namespace_set_index = -1; + int qname_index = -1; + List params = new ArrayList<>(); + + switch (s.type) { + case ParsedSymbol.TYPE_KEYWORD_NULL: + return 0; + case ParsedSymbol.TYPE_KEYWORD_QNAME: + kind = Multiname.QNAME; + break; + case ParsedSymbol.TYPE_KEYWORD_QNAMEA: + kind = Multiname.QNAMEA; + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAME: + kind = Multiname.RTQNAME; + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAMEA: + kind = Multiname.RTQNAMEA; + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAMEL: + kind = Multiname.RTQNAMEL; + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAMELA: + kind = Multiname.RTQNAMELA; + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAME: + kind = Multiname.MULTINAME; + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAMEA: + kind = Multiname.MULTINAMEA; + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAMEL: + kind = Multiname.MULTINAMEL; + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAMELA: + kind = Multiname.MULTINAMELA; + break; + case ParsedSymbol.TYPE_KEYWORD_TYPENAME: + kind = Multiname.TYPENAME; + break; + default: + throw new ParseException("Name expected", lexer.yyline()); + } + + switch (s.type) { + case ParsedSymbol.TYPE_KEYWORD_QNAME: + case ParsedSymbol.TYPE_KEYWORD_QNAMEA: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + namespace_index = parseNamespace(constants, lexer); + expected(ParsedSymbol.TYPE_COMMA, ",", lexer); + ParsedSymbol name = lexer.lex(); + if (name.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + name_index = 0; + } else { + expected(name, ParsedSymbol.TYPE_STRING, "String"); + name_index = constants.getStringId((String) name.value, true); + } + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAME: + case ParsedSymbol.TYPE_KEYWORD_RTQNAMEA: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + ParsedSymbol rtqName = lexer.lex(); + if (rtqName.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + name_index = 0; + } else { + expected(rtqName, ParsedSymbol.TYPE_STRING, "String"); + name_index = constants.getStringId((String) rtqName.value, true); + } + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_RTQNAMEL: + case ParsedSymbol.TYPE_KEYWORD_RTQNAMELA: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAME: + case ParsedSymbol.TYPE_KEYWORD_MULTINAMEA: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + ParsedSymbol mName = lexer.lex(); + if (mName.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + name_index = 0; + } else { + expected(mName, ParsedSymbol.TYPE_STRING, "String"); + name_index = constants.getStringId((String) mName.value, true); + } + expected(ParsedSymbol.TYPE_COMMA, ",", lexer); + namespace_set_index = parseNamespaceSet(constants, lexer); + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_MULTINAMEL: + case ParsedSymbol.TYPE_KEYWORD_MULTINAMELA: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + namespace_set_index = parseNamespaceSet(constants, lexer); + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_TYPENAME: + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + qname_index = parseMultiName(constants, lexer); + expected(ParsedSymbol.TYPE_LOWERTHAN, "<", lexer); + params.add(parseMultiName(constants, lexer)); + ParsedSymbol nt = lexer.lex(); + while (nt.type == ParsedSymbol.TYPE_COMMA) { + params.add(parseMultiName(constants, lexer)); + nt = lexer.lex(); + } + expected(nt, ParsedSymbol.TYPE_GREATERTHAN, ">"); + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + } + + return constants.getMultinameId(new Multiname(kind, name_index, namespace_index, namespace_set_index, qname_index, params), true); + } + + public static ValueKind parseValue(ConstantPool constants, Flasm3Lexer lexer) throws IOException, ParseException { + ParsedSymbol type = lexer.lex(); + ParsedSymbol value; + int value_index = 0; + int value_kind = 0; + switch (type.type) { + case ParsedSymbol.TYPE_KEYWORD_INTEGER: + value_kind = ValueKind.CONSTANT_Int; + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + value = lexer.lex(); + if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + value_index = 0; + } else { + expected(value, ParsedSymbol.TYPE_INTEGER, "Integer or null"); + value_index = constants.getIntId((Long) value.value, true); + } + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_UINTEGER: + value_kind = ValueKind.CONSTANT_UInt; + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + value = lexer.lex(); + if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + value_index = 0; + } else { + expected(value, ParsedSymbol.TYPE_INTEGER, "UInteger"); + value_index = constants.getUIntId((Long) value.value, true); + } + + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + case ParsedSymbol.TYPE_KEYWORD_DOUBLE: + value_kind = ValueKind.CONSTANT_Double; + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + value = lexer.lex(); + if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + value_index = 0; + } else { + expected(value, ParsedSymbol.TYPE_FLOAT, "Double or null"); + value_index = constants.getDoubleId((Double) value.value, true); + } + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + break; + /*case ParsedSymbol.TYPE_KEYWORD_DECIMAL: + value_kind = ValueKind.CONSTANT_Decimal; + break;*/ + case ParsedSymbol.TYPE_INTEGER: + value_kind = ValueKind.CONSTANT_Int; + value_index = constants.getIntId((Long) type.value, true); + break; + case ParsedSymbol.TYPE_FLOAT: + value_kind = ValueKind.CONSTANT_Double; + value_index = constants.getDoubleId((Double) type.value, true); + break; + case ParsedSymbol.TYPE_STRING: + value_kind = ValueKind.CONSTANT_Utf8; + value_index = constants.getStringId((String) type.value, true); + break; + case ParsedSymbol.TYPE_KEYWORD_UTF8: + value_kind = ValueKind.CONSTANT_Utf8; + expected(ParsedSymbol.TYPE_PARENT_OPEN, "(", lexer); + value = lexer.lex(); + if (value.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + value_index = 0; + } else { + expected(value, ParsedSymbol.TYPE_STRING, "String or null"); + expected(ParsedSymbol.TYPE_PARENT_CLOSE, ")", lexer); + value_index = constants.getStringId((String) value.value, true); + } + break; + case ParsedSymbol.TYPE_KEYWORD_TRUE: + value_kind = ValueKind.CONSTANT_True; + break; + case ParsedSymbol.TYPE_KEYWORD_FALSE: + value_kind = ValueKind.CONSTANT_False; + break; + case ParsedSymbol.TYPE_KEYWORD_NULL: + value_kind = ValueKind.CONSTANT_Null; + break; + case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: + case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: + case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: + case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: + case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: + case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: + case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: + + switch (type.type) { + case ParsedSymbol.TYPE_KEYWORD_NAMESPACE: + value_kind = ValueKind.CONSTANT_Namespace; + break; + case ParsedSymbol.TYPE_KEYWORD_PACKAGEINTERNALNS: + value_kind = ValueKind.CONSTANT_PackageInternalNs; + break; + case ParsedSymbol.TYPE_KEYWORD_PROTECTEDNAMESPACE: + value_kind = ValueKind.CONSTANT_ProtectedNamespace; + break; + case ParsedSymbol.TYPE_KEYWORD_EXPLICITNAMESPACE: + value_kind = ValueKind.CONSTANT_ExplicitNamespace; + break; + case ParsedSymbol.TYPE_KEYWORD_STATICPROTECTEDNS: + value_kind = ValueKind.CONSTANT_StaticProtectedNs; + break; + case ParsedSymbol.TYPE_KEYWORD_PRIVATENAMESPACE: + value_kind = ValueKind.CONSTANT_PrivateNs; + break; + case ParsedSymbol.TYPE_KEYWORD_PACKAGENAMESPACE: + value_kind = ValueKind.CONSTANT_PackageNamespace; + break; + } + lexer.pushback(type); + value_index = parseNamespace(constants, lexer); + break; + default: + if (Configuration.debugMode.get()) { + throw new ParseException("Not supported valueType.", lexer.yyline()); + } + } + return new ValueKind(value_index, value_kind); + } + + public static AVM2Code parse(Reader reader, ConstantPool constants, Trait trait, MissingSymbolHandler missingHandler, MethodBody body, MethodInfo info) throws IOException, ParseException, InterruptedException { + AVM2Code code = new AVM2Code(); + + List offsetItems = new ArrayList<>(); + List labelItems = new ArrayList<>(); + List exceptions = new ArrayList<>(); + List exceptionIndices = new ArrayList<>(); + int offset = 0; + + Flasm3Lexer lexer = new Flasm3Lexer(reader); + + ParsedSymbol symb; + AVM2Instruction lastIns = null; + List exceptionsFrom = new ArrayList<>(); + List exceptionsTo = new ArrayList<>(); + List exceptionsTargets = new ArrayList<>(); + info.flags = 0; + info.name_index = 0; + List paramTypes = new ArrayList<>(); + List paramNames = new ArrayList<>(); + List optional = new ArrayList<>(); + do { + symb = lexer.lex(); + if (Arrays.asList(ParsedSymbol.TYPE_KEYWORD_BODY, ParsedSymbol.TYPE_KEYWORD_CODE, ParsedSymbol.TYPE_KEYWORD_METHOD).contains(symb.type)) { + continue; + } + if (symb.type == ParsedSymbol.TYPE_KEYWORD_TRAIT) { + if (trait == null) { + throw new ParseException("No trait expected", lexer.yyline()); + } + symb = lexer.lex(); + switch (symb.type) { + case ParsedSymbol.TYPE_KEYWORD_METHOD: + case ParsedSymbol.TYPE_KEYWORD_GETTER: + case ParsedSymbol.TYPE_KEYWORD_SETTER: + if (!(trait instanceof TraitMethodGetterSetter)) { + throw new ParseException("Unxpected trait type", lexer.yyline()); + } + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) trait; + switch (symb.type) { + case ParsedSymbol.TYPE_KEYWORD_METHOD: + tm.kindType = Trait.TRAIT_METHOD; + break; + case ParsedSymbol.TYPE_KEYWORD_GETTER: + tm.kindType = Trait.TRAIT_GETTER; + break; + case ParsedSymbol.TYPE_KEYWORD_SETTER: + tm.kindType = Trait.TRAIT_SETTER; + break; + } + tm.name_index = parseMultiName(constants, lexer); + expected(ParsedSymbol.TYPE_KEYWORD_DISPID, "dispid", lexer); + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + tm.disp_id = (int) (long) (Long) symb.value; + + break; + case ParsedSymbol.TYPE_KEYWORD_FUNCTION: + if (!(trait instanceof TraitFunction)) { + throw new ParseException("Unxpected trait type", lexer.yyline()); + } + break; + + } + continue; + } + if (symb.type == ParsedSymbol.TYPE_KEYWORD_NAME) { + symb = lexer.lex(); + if (symb.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + info.name_index = 0; + } else { + expected(symb, ParsedSymbol.TYPE_STRING, "String or null"); + info.name_index = constants.getStringId((String) symb.value, true); + } + continue; + } + if (symb.type == ParsedSymbol.TYPE_KEYWORD_PARAM) { + paramTypes.add(parseMultiName(constants, lexer)); + continue; + } + if (symb.type == ParsedSymbol.TYPE_KEYWORD_PARAMNAME) { + symb = lexer.lex(); + if (symb.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + paramNames.add(0); + } else { + expected(symb, ParsedSymbol.TYPE_STRING, "String or null"); + paramNames.add(constants.getStringId((String) symb.value, true)); + } + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_OPTIONAL) { + optional.add(parseValue(constants, lexer)); + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_MAXSTACK) { + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + body.max_stack = (int) (long) (Long) symb.value; + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_LOCALCOUNT) { + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + body.max_regs = (int) (long) (Long) symb.value; + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_INITSCOPEDEPTH) { + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + body.init_scope_depth = (int) (long) (Long) symb.value; + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_MAXSCOPEDEPTH) { + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_INTEGER, "Integer"); + body.max_scope_depth = (int) (long) (Long) symb.value; + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_RETURNS) { + info.ret_type = parseMultiName(constants, lexer); + continue; + } + + if (symb.type == ParsedSymbol.TYPE_KEYWORD_FLAG) { + symb = lexer.lex(); + switch (symb.type) { + case ParsedSymbol.TYPE_KEYWORD_EXPLICIT: + info.setFlagExplicit(); + break; + case ParsedSymbol.TYPE_KEYWORD_HAS_OPTIONAL: + info.setFlagHas_optional(); + break; + case ParsedSymbol.TYPE_KEYWORD_HAS_PARAM_NAMES: + info.setFlagHas_paramnames(); + break; + case ParsedSymbol.TYPE_KEYWORD_IGNORE_REST: + info.setFlagIgnore_Rest(); + break; + case ParsedSymbol.TYPE_KEYWORD_NEED_ACTIVATION: + info.setFlagNeed_activation(); + break; + case ParsedSymbol.TYPE_KEYWORD_NEED_ARGUMENTS: + info.setFlagNeed_Arguments(); + break; + case ParsedSymbol.TYPE_KEYWORD_NEED_REST: + info.setFlagNeed_rest(); + break; + case ParsedSymbol.TYPE_KEYWORD_SET_DXNS: + info.setFlagSetsdxns(); + break; + } + continue; + } + if (symb.type == ParsedSymbol.TYPE_KEYWORD_TRY) { + expected(ParsedSymbol.TYPE_KEYWORD_FROM, "From", lexer); + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); + exceptionsFrom.add((String) symb.value); + expected(ParsedSymbol.TYPE_KEYWORD_TO, "To", lexer); + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); + exceptionsTo.add((String) symb.value); + expected(ParsedSymbol.TYPE_KEYWORD_TARGET, "Target", lexer); + symb = lexer.lex(); + expected(symb, ParsedSymbol.TYPE_IDENTIFIER, "Identifier"); + exceptionsTargets.add((String) symb.value); + expected(ParsedSymbol.TYPE_KEYWORD_TYPE, "Type", lexer); + ABCException ex = new ABCException(); + ex.type_index = parseMultiName(constants, lexer); + expected(ParsedSymbol.TYPE_KEYWORD_NAME, "Name", lexer); + ex.name_index = parseMultiName(constants, lexer); + exceptions.add(ex); + continue; + } + if (symb.type == ParsedSymbol.TYPE_EXCEPTION_START) { + int exIndex = (Integer) symb.value; + int listIndex = exceptionIndices.indexOf(exIndex); + if (listIndex == -1) { + throw new ParseException("Undefinex exception index", lexer.yyline()); + } + exceptions.get(listIndex).start = offset; + continue; + } + if (symb.type == ParsedSymbol.TYPE_EXCEPTION_END) { + int exIndex = (Integer) symb.value; + int listIndex = exceptionIndices.indexOf(exIndex); + if (listIndex == -1) { + throw new ParseException("Undefinex exception index", lexer.yyline()); + } + exceptions.get(listIndex).end = offset; + continue; + } + if (symb.type == ParsedSymbol.TYPE_EXCEPTION_TARGET) { + int exIndex = (Integer) symb.value; + int listIndex = exceptionIndices.indexOf(exIndex); + if (listIndex == -1) { + throw new ParseException("Undefinex exception index", lexer.yyline()); + } + exceptions.get(listIndex).target = offset; + continue; + } + if (symb.type == ParsedSymbol.TYPE_EOF) { + break; + } + if (symb.type == ParsedSymbol.TYPE_COMMENT) { + if (lastIns != null) { + lastIns.comment = (String) symb.value; + } + continue; + } + if (symb.type == ParsedSymbol.TYPE_INSTRUCTION_NAME) { + if (((String) symb.value).toLowerCase(Locale.ENGLISH).equals("exception")) { + ParsedSymbol exIndex = lexer.lex(); + if (exIndex.type != ParsedSymbol.TYPE_INTEGER) { + throw new ParseException("Index expected", lexer.yyline()); + } + ParsedSymbol exName = lexer.lex(); + if (exName.type != ParsedSymbol.TYPE_MULTINAME) { + throw new ParseException("Multiname expected", lexer.yyline()); + } + ParsedSymbol exType = lexer.lex(); + if (exType.type != ParsedSymbol.TYPE_MULTINAME) { + throw new ParseException("Multiname expected", lexer.yyline()); + } + ABCException ex = new ABCException(); + + ex.name_index = checkMultinameIndex(constants, (int) (long) (Long) exName.value, lexer.yyline()); + ex.type_index = checkMultinameIndex(constants, (int) (long) (Long) exType.value, lexer.yyline()); + exceptions.add(ex); + exceptionIndices.add((int) (long) (Long) exIndex.value); + continue; + } + boolean insFound = false; + for (InstructionDefinition def : AVM2Code.instructionSet) { + if (def.instructionName.equals((String) symb.value)) { + insFound = true; + List operandsList = new ArrayList<>(); + + for (int i = 0; i < def.operands.length; i++) { + ParsedSymbol parsedOperand = lexer.lex(); + switch (def.operands[i]) { + case AVM2Code.DAT_MULTINAME_INDEX: + lexer.pushback(parsedOperand); + operandsList.add(parseMultiName(constants, lexer)); + /*if (parsedOperand.type == ParsedSymbol.TYPE_MULTINAME) { + operandsList.add(checkMultinameIndex(constants, (int) (long) (Long) parsedOperand.value, lexer.yyline())); + } else { + throw new ParseException("Multiname expected", lexer.yyline()); + }*/ + break; + case AVM2Code.DAT_STRING_INDEX: + if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + operandsList.add(0); + } else { + if (parsedOperand.type == ParsedSymbol.TYPE_STRING) { + int sid = constants.getStringId((String) parsedOperand.value); + if (sid == 0) { + if ((missingHandler != null) && (missingHandler.missingString((String) parsedOperand.value))) { + sid = constants.addString((String) parsedOperand.value); + } else { + throw new ParseException("Unknown String", lexer.yyline()); + } + } + operandsList.add(sid); + } else { + throw new ParseException("String or null expected", lexer.yyline()); + } + } + break; + case AVM2Code.DAT_INT_INDEX: + + if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + operandsList.add(0); + } else { + if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { + long intVal = (Long) parsedOperand.value; + int iid = constants.getIntId(intVal); + if (iid == 0) { + if ((missingHandler != null) && (missingHandler.missingInt(intVal))) { + iid = constants.addInt(intVal); + } else { + throw new ParseException("Unknown int", lexer.yyline()); + } + } + operandsList.add(iid); + } else { + throw new ParseException("Integer or null expected", lexer.yyline()); + } + } + break; + case AVM2Code.DAT_UINT_INDEX: + if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + operandsList.add(0); + } else { + if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { + long intVal = (Long) parsedOperand.value; + int iid = constants.getUIntId(intVal); + if (iid == 0) { + if ((missingHandler != null) && (missingHandler.missingUInt(intVal))) { + iid = constants.addUInt(intVal); + } else { + throw new ParseException("Unknown uint", lexer.yyline()); + } + } + operandsList.add(iid); + } else { + throw new ParseException("Integer or null expected", lexer.yyline()); + } + } + break; + case AVM2Code.DAT_DOUBLE_INDEX: + if (parsedOperand.type == ParsedSymbol.TYPE_KEYWORD_NULL) { + operandsList.add(0); + } else { + if ((parsedOperand.type == ParsedSymbol.TYPE_INTEGER) || (parsedOperand.type == ParsedSymbol.TYPE_FLOAT)) { + + double doubleVal = 0; + if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { + doubleVal = (Long) parsedOperand.value; + } + if (parsedOperand.type == ParsedSymbol.TYPE_FLOAT) { + doubleVal = (Double) parsedOperand.value; + } + int did = constants.getDoubleId(doubleVal); + if (did == 0) { + if ((missingHandler != null) && (missingHandler.missingDouble(doubleVal))) { + did = constants.addDouble(doubleVal); + } else { + throw new ParseException("Unknown double", lexer.yyline()); + } + } + operandsList.add(did); + } else { + throw new ParseException("Float or null expected", lexer.yyline()); + } + } + break; + case AVM2Code.DAT_OFFSET: + if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { + offsetItems.add(new OffsetItem((String) parsedOperand.value, code.code.size(), i)); + operandsList.add(0); + } else { + throw new ParseException("Offset expected", lexer.yyline()); + } + break; + case AVM2Code.DAT_CASE_BASEOFFSET: + if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { + offsetItems.add(new CaseOffsetItem((String) parsedOperand.value, code.code.size(), i)); + operandsList.add(0); + } else { + throw new ParseException("Offset expected", lexer.yyline()); + } + break; + case AVM2Code.OPT_CASE_OFFSETS: + + if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { + int patCount = (int) (long) (Long) parsedOperand.value; + operandsList.add(patCount); + + for (int c = 0; c <= patCount; c++) { + parsedOperand = lexer.lex(); + if (parsedOperand.type == ParsedSymbol.TYPE_IDENTIFIER) { + offsetItems.add(new CaseOffsetItem((String) parsedOperand.value, code.code.size(), i + (c + 1))); + operandsList.add(0); + } else { + throw new ParseException("Offset expected", lexer.yyline()); + } + } + } else { + throw new ParseException("Case count expected", lexer.yyline()); + } + break; + default: + if (parsedOperand.type == ParsedSymbol.TYPE_INTEGER) { + operandsList.add((int) (long) (Long) parsedOperand.value); + } else { + throw new ParseException("Integer expected", lexer.yyline()); + } + } + } + + int[] operands = new int[operandsList.size()]; + for (int i = 0; i < operandsList.size(); i++) { + operands[i] = operandsList.get(i); + } + lastIns = new AVM2Instruction(offset, def, operands, new byte[0]); + code.code.add(lastIns); + offset += lastIns.getBytes().length; + break; + } + } + if (symb.value.toString().toLowerCase().equals("ffdec_deobfuscatepop")) { + lastIns = new AVM2Instruction(offset, new DeobfuscatePopIns(), new int[0], new byte[0]); + code.code.add(lastIns); + offset += lastIns.getBytes().length; + insFound = true; + } + if (!insFound) { + throw new ParseException("Invalid instruction name:" + (String) symb.value, lexer.yyline()); + } + } else if (symb.type == ParsedSymbol.TYPE_LABEL) { + labelItems.add(new LabelItem((String) symb.value, offset)); + + } else { + throw new ParseException("Unexpected symbol", lexer.yyline()); + } + } while (symb.type != ParsedSymbol.TYPE_EOF); + + code.compact(); + for (LabelItem li : labelItems) { + int ind; + ind = exceptionsFrom.indexOf(li.label); + if (ind > -1) { + exceptions.get(ind).start = li.offset; + } + + ind = exceptionsTo.indexOf(li.label); + if (ind > -1) { + exceptions.get(ind).end = li.offset; + } + + ind = exceptionsTargets.indexOf(li.label); + if (ind > -1) { + exceptions.get(ind).target = li.offset; + } + } + + for (OffsetItem oi : offsetItems) { + for (LabelItem li : labelItems) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } + if (oi.label.equals(li.label)) { + AVM2Instruction ins = code.code.get((int) oi.insPosition); + int relOffset; + if (oi instanceof CaseOffsetItem) { + relOffset = li.offset - (int) ins.offset; + } else { + relOffset = li.offset - ((int) ins.offset + ins.getBytes().length); + } + ins.operands[oi.insOperandIndex] = relOffset; + } + } + } + body.exceptions = new ABCException[exceptions.size()]; + for (int e = 0; e < exceptions.size(); e++) { + body.exceptions[e] = exceptions.get(e); + } + + info.param_types = new int[paramTypes.size()]; + for (int i = 0; i < paramTypes.size(); i++) { + info.param_types[i] = paramTypes.get(i); + } + + if (info.flagHas_paramnames()) { + info.paramNames = new int[paramNames.size()]; + for (int i = 0; i < paramNames.size(); i++) { + info.paramNames[i] = paramNames.get(i); + } + } + + if (info.flagHas_optional()) { + info.optional = new ValueKind[optional.size()]; + for (int i = 0; i < optional.size(); i++) { + info.optional[i] = optional.get(i); + } + } + return code; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java index fa28a187a..dcb8b66f6 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/AVM2SourceGenerator.java @@ -1,2427 +1,2424 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.IfCondition; -import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.ClassInfo; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.Loop; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.CommaExpressionItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.DoWhileItem; -import com.jpexs.decompiler.graph.model.DuplicateItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class AVM2SourceGenerator implements SourceGenerator { - - public final ABC abc; - public List allABCs; - - public static final int MARK_E_START = 0; - public static final int MARK_E_END = 1; - public static final int MARK_E_TARGET = 2; - public static final int MARK_E_FINALLYPART = 3; - - private AVM2Instruction ins(InstructionDefinition def, int... operands) { - return new AVM2Instruction(0, def, operands, new byte[0]); - } - - public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { - int nssa[] = new int[item.openedNamespaces.size()]; - for (int i = 0; i < item.openedNamespaces.size(); i++) { - nssa[i] = item.openedNamespaces.get(i); - } - int nsset = abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - - return GraphTargetItem.toSourceMerge(localData, this, - item.object, - ins(new GetDescendantsIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.getStringId(item.nameStr, true), 0, nsset, 0, new ArrayList()), true)) - ); - } - - @Override - public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(new ConvertBIns())); - } - ret.add(ins(new DupIns())); - List andExpr = generateToActionList(localData, item.rightSide); - andExpr.add(0, ins(new PopIns())); - int andExprLen = insToBytes(andExpr).length; - ret.add(ins(new IfFalseIns(), andExprLen)); - ret.addAll(andExpr); - return ret; - - } - - private byte[] insToBytes(List code) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (AVM2Instruction instruction : code) { - - baos.write(instruction.getBytes()); - - } - return baos.toByteArray(); - } catch (IOException ex) { - Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); - } - return new byte[0]; - } - - @Override - public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - if (!("" + item.leftSide.returnType()).equals("Boolean")) { - ret.add(ins(new ConvertBIns())); - } - ret.add(ins(new DupIns())); - List orExpr = generateToActionList(localData, item.rightSide); - orExpr.add(0, ins(new PopIns())); - int orExprLen = insToBytes(orExpr).length; - ret.add(ins(new IfTrueIns(), orExprLen)); - ret.addAll(orExpr); - return ret; - } - - public List toInsList(List items) { - List ret = new ArrayList<>(); - for (GraphSourceItem s : items) { - if (s instanceof AVM2Instruction) { - ret.add((AVM2Instruction) s); - } - } - return ret; - } - - private List nonempty(List list) { - if (list == null) { - return new ArrayList<>(); - } - return list; - } - - private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfTrueIns(), offset)); - } - - private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { - if (t instanceof IfCondition) { - IfCondition ic = (IfCondition) t; - return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); - } - return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfFalseIns(), offset)); - } - - private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { - List ret = new ArrayList<>(); - //ret.addAll(notCondition(localData, expression)); - List onTrue = null; - List onFalse = null; - if (ternar) { - onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); - } else { - onTrue = generateToInsList(localData, onTrueCmds); - } - - if (onFalseCmds != null && !onFalseCmds.isEmpty()) { - if (ternar) { - onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); - } else { - onFalse = generateToInsList(localData, onFalseCmds); - } - } - AVM2Instruction ajmp = null; - if (onFalse != null) { - if (!((!nonempty(onTrue).isEmpty()) - && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) - || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { - ajmp = ins(new JumpIns(), 0); - onTrue.add(ajmp); - } - } - - byte[] onTrueBytes = insToBytes(onTrue); - int onTrueLen = onTrueBytes.length; - - ret.addAll(notCondition(localData, expression, onTrueLen)); - ret.addAll(onTrue); - - if (onFalse != null) { - byte[] onFalseBytes = insToBytes(onFalse); - int onFalseLen = onFalseBytes.length; - if (ajmp != null) { - ajmp.operands[0] = onFalseLen; - } - ret.addAll(onFalse); - } - return ret; - } - - public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - final Reference xmlListReg = new Reference<>(0); - List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(new PushByteIns(), 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - item.object, - ins(new CheckFilterIns()), - NameAVM2Item.generateCoerce(localData,this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg), - ins(new GetLexIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId("XMLList", true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true)), - ins(new PushStringIns(), abc.constants.getStringId("", true)), - ins(new ConstructIns(), 1), - xmlListSetTemp - )); - final Reference tempVal1 = new Reference<>(0); - final Reference tempVal2 = new Reference<>(0); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(new LabelIns()), - AssignableAVM2Item.getTemp(localData, this, collectionReg), - AssignableAVM2Item.getTemp(localData, this, counterReg), - ins(new NextValueIns()), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), - AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), - ins(new PushWithIns()) - )); - localData.scopeStack.add(new LocalRegAVM2Item(null, tempVal2.getVal(), null)); - forBody.addAll(toInsList(item.value.toSource(localData, this))); - List trueBody = new ArrayList<>(); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); - trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); - int nss[] = new int[item.openedNamespaces.size()]; - for (int i = 0; i < item.openedNamespaces.size(); i++) { - nss[i] = item.openedNamespaces.get(i); - } - trueBody.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true))); - forBody.add(ins(new IfFalseIns(), insToBytes(trueBody).length)); - forBody.addAll(trueBody); - forBody.add(ins(new PopScopeIns())); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); - - int forBodyLen = insToBytes(forBody).length; - AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(new IfTrueIns(), 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { - return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); - } - - private void fixSwitch(List code, int breakOffset, long loopId) { - fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); - } - - private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { - int pos = 0; - for (int a = 0; a < code.size(); a++) { - AVM2Instruction ins = code.get(a); - pos += ins.getBytes().length; - if (ins.definition instanceof JumpIns) { - if (ins.definition instanceof ContinueJumpIns) { - if (continueOffset != Integer.MAX_VALUE) { - ins.operands[0] = (-pos + continueOffset); - code.get(a).definition = new JumpIns(); - } - } - if (ins.definition instanceof BreakJumpIns) { - ins.operands[0] = (-pos + breakOffset); - code.get(a).definition = new JumpIns(); - } - } - } - } - - @Override - public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { - List onTrue = new ArrayList<>(); - onTrue.add(item.onTrue); - List onFalse = new ArrayList<>(); - onFalse.add(item.onFalse); - return generateIf(localData, item.expression, onTrue, onFalse, true); - } - - @Override - public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List whileBody = generateToInsList(localData, item.commands); - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - whileBody.add(0, ins(new LabelIns())); - ret.addAll(whileBody); - int whileBodyLen = insToBytes(whileBody).length; - forwardJump.operands[0] = whileBodyLen; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int whileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); - } - - public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { - return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); - } - - public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { - List ret = new ArrayList<>(); - final Reference counterReg = new Reference<>(0); - final Reference collectionReg = new Reference<>(0); - - if (assignable instanceof UnresolvedAVM2Item) { - assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; - } - - ret.addAll(GraphTargetItem.toSourceMerge(localData, this, - ins(new PushByteIns(), 0), - AssignableAVM2Item.setTemp(localData, this, counterReg), - collection, - NameAVM2Item.generateCoerce(localData,this, TypeItem.UNBOUNDED), - AssignableAVM2Item.setTemp(localData, this, collectionReg) - )); - - GraphTargetItem assigned = new GraphTargetItem() { - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return null; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, - AssignableAVM2Item.getTemp(localData, generator, collectionReg), - AssignableAVM2Item.getTemp(localData, generator, counterReg), - ins(each ? new NextValueIns() : new NextNameIns()) - ); - } - }; - assignable.setAssignedValue(assigned); - - List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, - ins(new LabelIns()), - assignable.toSourceIgnoreReturnValue(localData, this) - )); - - forBody.addAll(generateToInsList(localData, commands)); - int forBodyLen = insToBytes(forBody).length; - - AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); - ret.add(forwardJump); - - List expr = new ArrayList<>(); - expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); - AVM2Instruction backIf = ins(new IfTrueIns(), 0); - expr.add(backIf); - - int exprLen = insToBytes(expr).length; - backIf.operands[0] = -(exprLen + forBodyLen); - - fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); - ret.addAll(forBody); - ret.addAll(expr); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - whileExpr.addAll(generateToInsList(localData, ex)); - } - List dowhileBody = generateToInsList(localData, item.commands); - List labelBody = new ArrayList<>(); - labelBody.add(ins(new LabelIns())); - int labelBodyLen = insToBytes(labelBody).length; - - AVM2Instruction forwardJump = ins(new JumpIns(), labelBodyLen); - ret.add(forwardJump); - ret.addAll(labelBody); - ret.addAll(dowhileBody); - int dowhileBodyLen = insToBytes(dowhileBody).length; - whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int dowhileExprLen = insToBytes(whileExpr).length; - whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction - ret.addAll(whileExpr); - fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { - - List ret = new ArrayList<>(); - ret.addAll(item.scope.toSource(localData, this)); - Reference tempReg = new Reference<>(0); - ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); - localData.scopeStack.add(new WithObjectAVM2Item(null, new LocalRegAVM2Item(null, tempReg.getVal(), null))); - ret.add(ins(new PushWithIns())); - ret.addAll(generate(localData, item.items)); - ret.add(ins(new PopScopeIns())); - ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { - List ret = new ArrayList<>(); - List forExpr = new ArrayList<>(); - - List ex = new ArrayList<>(); - ex.add(item.expression); - - GraphTargetItem lastItem = null; - if (!ex.isEmpty()) { - lastItem = ex.remove(ex.size() - 1); - while (lastItem instanceof CommaExpressionItem) { - CommaExpressionItem cei = (CommaExpressionItem) lastItem; - ex.addAll(cei.commands); - lastItem = ex.remove(ex.size() - 1); - } - forExpr.addAll(generateToInsList(localData, ex)); - } - List forBody = generateToInsList(localData, item.commands); - List forFinalCommands = generateToInsList(localData, item.finalCommands); - - ret.addAll(generateToInsList(localData, item.firstCommands)); - - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - forBody.add(0, ins(new LabelIns())); - ret.addAll(forBody); - ret.addAll(forFinalCommands); - int forBodyLen = insToBytes(forBody).length; - int forFinalCLen = insToBytes(forFinalCommands).length; - forwardJump.operands[0] = forBodyLen + forFinalCLen; - forExpr.addAll(toInsList(condition(localData, lastItem, 0))); - int forExprLen = insToBytes(forExpr).length; - forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction - ret.addAll(forExpr); - fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); - return ret; - } - private long uniqLast = 0; - - public String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - @Override - public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { - List ret = new ArrayList<>(); - Reference switchedReg = new Reference<>(0); - AVM2Instruction forwardJump = ins(new JumpIns(), 0); - ret.add(forwardJump); - - List cases = new ArrayList<>(); - cases.addAll(toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); - int cLen = insToBytes(cases).length; - List caseLast = new ArrayList<>(); - caseLast.add(0, ins(new JumpIns(), cLen)); - caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); - int cLastLen = insToBytes(caseLast).length; - caseLast.add(0, ins(new JumpIns(), cLastLen)); - cases.addAll(0, caseLast); - - List preCases = new ArrayList<>(); - preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); - preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); - - for (int i = item.caseValues.size() - 1; i >= 0; i--) { - List sub = new ArrayList<>(); - sub.addAll(toInsList(new IntegerValueAVM2Item(null, (long) i).toSource(localData, this))); - sub.add(ins(new JumpIns(), insToBytes(cases).length)); - int subLen = insToBytes(sub).length; - - cases.addAll(0, sub); - cases.add(0, ins(new IfStrictNeIns(), subLen)); - cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); - cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); - } - cases.addAll(0, preCases); - - AVM2Instruction lookupOp = new AVM2Instruction(0, new LookupSwitchIns(), new int[item.caseValues.size() + 1 + 1 + 1], new byte[0]); - cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); - List bodies = new ArrayList<>(); - List bodiesOffsets = new ArrayList<>(); - int defOffset; - int casesLen = insToBytes(cases).length; - bodies.addAll(generateToInsList(localData, item.defaultCommands)); - bodies.add(0, ins(new LabelIns())); - bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... - defOffset = -(insToBytes(bodies).length + casesLen); - for (int i = item.caseCommands.size() - 1; i >= 0; i--) { - bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); - bodies.add(0, ins(new LabelIns())); - bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); - } - lookupOp.operands[0] = defOffset; - lookupOp.operands[1] = item.valuesMapping.size(); - lookupOp.operands[2 + item.caseValues.size()] = defOffset; - for (int i = 0; i < item.valuesMapping.size(); i++) { - lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); - } - - forwardJump.operands[0] = insToBytes(bodies).length; - ret.addAll(bodies); - ret.addAll(cases); - ret.add(lookupOp); - fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { - /*if (item.getOriginal() instanceof Inverted) { - GraphTargetItem norig = ((Inverted) item).invert(); - return norig.toSource(localData, this); - }*/ - List ret = new ArrayList<>(); - ret.addAll(item.getOriginal().toSource(localData, this)); - ret.add(ins(new NotIns())); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { - List ret = new ArrayList<>(); - ret.add(ins(new DupIns())); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, BreakItem item) { - List ret = new ArrayList<>(); - AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); - ret.add(abreak); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - int scope = 0; - if (!item.functionName.isEmpty()) { - ret.add(ins(new NewObjectIns(), 0)); - ret.add(ins(new PushWithIns())); - scope = localData.scopeStack.size(); - localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abc, allABCs, new ArrayList(), localData.callStack)); - } - ret.add(ins(new NewFunctionIns(), method(false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, null, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); - if (!item.functionName.isEmpty()) { - ret.add(ins(new DupIns())); - ret.add(ins(new GetScopeObjectIns(), scope)); - ret.add(ins(new SwapIns())); - ret.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(item.functionName, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(localData.pkg, true)), 0, true), 0, 0, new ArrayList()), true))); - ret.add(ins(new PopScopeIns())); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - } - return ret; - } - - private static int currentFinId = 1; - - private static int finId() { - return currentFinId++; - } - - public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - - boolean newFinallyReg = false; - List newex = new ArrayList<>(); - int aloneFinallyEx = -1; - int finallyEx = -1; - for (NameAVM2Item e : item.catchExceptions2) { - ABCException aex = new ABCException(); - aex.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(e.getVariableName(), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true); - aex.type_index = typeName(localData, e.type); - newex.add(aex); - } - int finId = 0; - if (item.finallyCommands != null) { - if (item.catchExceptions2.isEmpty()) { - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - aloneFinallyEx = newex.size() - 1; - } - ABCException aex = new ABCException(); - aex.name_index = 0; - aex.type_index = 0; - newex.add(aex); - finallyEx = newex.size() - 1; - if (localData.finallyRegister == -1) { - localData.finallyRegister = getFreeRegister(localData); - killRegister(localData, localData.finallyRegister); //reuse for catches - newFinallyReg = true; - } - finId = finId(); - } - - if (finallyEx > -1) { - localData.finallyCatches.add(finId); - } - List tryCmds = generateToInsList(localData, item.tryCommands); - - //int i = firstId + item.catchCommands.size() - 1; - List catches = new ArrayList<>(); - Reference tempReg = new Reference<>(0); - - List currentExceptionIds = new ArrayList<>(); - List> catchCmds = new ArrayList<>(); - for (int c = 0; c < item.catchCommands.size(); c++) { - int i = localData.exceptions.size(); - localData.exceptions.add(newex.get(c)); - - currentExceptionIds.add(i); - - //Reference tempReg=new Reference<>(0); - List catchCmd = new ArrayList<>(); - catchCmd.add(ins(new NewCatchIns(), i)); - catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - catchCmd.add(ins(new DupIns())); - catchCmd.add(ins(new PushScopeIns())); - catchCmd.add(ins(new SwapIns())); - catchCmd.add(ins(new SetSlotIns(), 1)); - - for (AssignableAVM2Item a : item.catchVariables.get(c)) { - GraphTargetItem r = a; - if (r instanceof UnresolvedAVM2Item) { - r = ((UnresolvedAVM2Item) r).resolvedRoot; - } - if (r instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) r; - if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { - n.setSlotScope(localData.scopeStack.size()); - } - } - } - localData.scopeStack.add(new LocalRegAVM2Item(null, tempReg.getVal(), null)); - catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); - localData.scopeStack.remove(localData.scopeStack.size() - 1); - catchCmd.add(ins(new PopScopeIns())); - catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catchCmds.add(catchCmd); - } - for (int c = item.catchCommands.size() - 1; c >= 0; c--) { - List preCatches = new ArrayList<>(); - /*preCatches.add(ins(new GetLocal0Ins())); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); - preCatches.add(ins(new PushScopeIns()));*/ - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - - //catchCmds.add(catchCmd); - preCatches.addAll(catchCmds.get(c)); - catches.addAll(0, preCatches); - catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); - catches.add(0, ins(new JumpIns(), insToBytes(catches).length)); - } - - if (aloneFinallyEx > -1) { - localData.exceptions.add(newex.get(aloneFinallyEx)); - aloneFinallyEx = localData.exceptions.size() - 1; - - } - if (finallyEx > -1) { - localData.exceptions.add(newex.get(finallyEx)); - finallyEx = localData.exceptions.size() - 1; - } - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); - } - if (finallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); - } - - ret.addAll(tryCmds); - - for (int i : currentExceptionIds) { - ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); - } - if (aloneFinallyEx > -1) { - ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); - } - - if (aloneFinallyEx > -1) { - List preCatches = new ArrayList<>(); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - preCatches.add(ins(new NewCatchIns(), aloneFinallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(ins(new ThrowIns())); - preCatches.add(ins(new PopScopeIns())); - preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); - catches.add(ins(new JumpIns(), insToBytes(preCatches).length)); - catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); - catches.addAll(preCatches); - } - AVM2Instruction finSwitch = null; - AVM2Instruction pushDefIns = ins(new PushByteIns(), 0); - - int defPos = 0; - if (finallyEx > -1) { - List preCatches = new ArrayList<>(); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); - for (GraphTargetItem s : localData.scopeStack) { - preCatches.addAll(toInsList(s.toSource(localData, this))); - if (s instanceof WithObjectAVM2Item) { - preCatches.add(ins(new PushWithIns())); - } else { - preCatches.add(ins(new PushScopeIns())); - } - } - preCatches.add(ins(new NewCatchIns(), finallyEx)); - preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); - preCatches.add(ins(new PushScopeIns())); - preCatches.add(ins(new PopScopeIns())); - Reference tempReg2 = new Reference<>(0); - preCatches.add(ins(new KillIns(), tempReg.getVal())); - preCatches.add(ins(new CoerceAIns())); - preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); - preCatches.add(pushDefIns); - - List finallySwitchCmds = new ArrayList<>(); - - finSwitch = new AVM2Instruction(0, new LookupSwitchIns(), new int[1 + 1 + 1], new byte[0]); - finSwitch.operands[0] = finSwitch.getBytes().length; - finSwitch.operands[1] = 0; //switch cnt - - List preFinallySwitch = new ArrayList<>(); - preFinallySwitch.add(ins(new LabelIns())); - preFinallySwitch.add(ins(new PopIns())); - int preFinallySwitchLen = insToBytes(preFinallySwitch).length; - - finallySwitchCmds.add(ins(new LabelIns())); - finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); - finallySwitchCmds.add(ins(new KillIns(), tempReg2.getVal())); - finallySwitchCmds.add(ins(new ThrowIns())); - finallySwitchCmds.add(ins(new PushByteIns(), 255)); - finallySwitchCmds.add(ins(new PopScopeIns())); - finallySwitchCmds.add(ins(new KillIns(), tempReg.getVal())); - - int finSwitchLen = insToBytes(finallySwitchCmds).length; - - preCatches.add(ins(new JumpIns(), preFinallySwitchLen + finSwitchLen)); - AVM2Instruction fjump = ins(new JumpIns(), 0); - fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; - - preCatches.add(0, fjump); - preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); - preCatches.add(0, ins(new PushByteIns(), 255)); - - finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); - - int oldReg = localData.finallyRegister; - localData.finallyRegister = getFreeRegister(localData); - Integer cnt = localData.finallyCounter.get(finId); - if (cnt == null) { - cnt = -1; - } - defPos = cnt; - cnt++; //Skip default clause (throw) - localData.finallyCounter.put(finId, cnt); - finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); - killRegister(localData, localData.finallyRegister); - localData.finallyRegister = oldReg; - finSwitchLen = insToBytes(finallySwitchCmds).length; - - finSwitch.operands[2] = -finSwitchLen; - preCatches.addAll(preFinallySwitch); - preCatches.addAll(finallySwitchCmds); - preCatches.add(finSwitch); - - catches.addAll(preCatches); - AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); - } - - ret.addAll(catches); - //localData.exceptions.addAll(newex); - - if (finallyEx > -1) { - localData.finallyCatches.remove(localData.finallyCatches.size() - 1); - } - if (newFinallyReg) { - localData.finallyRegister = -1; - killRegister(localData, localData.finallyRegister); - } - int pos = 0; - int finallyPos = 0; - int switchPos = 0; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src == finSwitch) { - switchPos = pos; - } - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; - if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { - finallyPos = pos; - ret.remove(s); - s--; - continue; - } - } - pos += ins.getBytes().length; - } - - } - - if (finSwitch != null) { - pos = 0; - int defLoc = finSwitch.operands[2]; - List switchLoc = new ArrayList<>(); - boolean wasDef = false; - for (int s = 0; s < ret.size(); s++) { - GraphSourceItem src = ret.get(s); - if (src instanceof AVM2Instruction) { - AVM2Instruction ins = (AVM2Instruction) src; - if (ins.definition instanceof FinallyJumpIns) { - FinallyJumpIns fji = (FinallyJumpIns) ins.definition; - if (fji.getClauseId() == finId) { - List bet = new ArrayList<>(); - bet.add(ins(new LabelIns())); - bet.add(ins(new PopIns())); - int betLen = insToBytes(bet).length; - if (wasDef) { - ins.operands[0] = 0; - } else { - ins.operands[0] = finallyPos - (pos + ins.getBytes().length); - } - ins.definition = new JumpIns(); - switchLoc.add(pos + ins.getBytes().length + betLen - switchPos); - } - } - pos += ins.getBytes().length; - } - if (defPos == switchLoc.size() - 1) { - switchLoc.add(defLoc); - wasDef = true; - } - } - finSwitch.operands = new int[1 + 1 + switchLoc.size()]; - pushDefIns.operands[0] = defPos + 1; - int afterLoc = finSwitch.getBytes().length; - finSwitch.operands[0] = afterLoc; - finSwitch.operands[1] = switchLoc.size() - 1; - for (int j = 0; j < switchLoc.size(); j++) { - finSwitch.operands[2 + j] = switchLoc.get(j); - } - } - - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ContinueItem item) { - List ret = new ArrayList<>(); - AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); - ret.add(acontinue); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - if (!localData.finallyCatches.isEmpty()) { - ret.add(ins(new CoerceAIns())); - ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); - for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { - if (i < localData.finallyCatches.size() - 1) { - ret.add(ins(new LabelIns())); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(new LabelIns())); - ret.add(ins(new PopIns())); - } - ret.add(ins(new LabelIns())); - ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); - ret.add(ins(new KillIns(), localData.finallyRegister)); - } - ret.add(ins(new ReturnValueIns())); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - if (!localData.finallyCatches.isEmpty()) { - - for (int i = 0; i < localData.finallyCatches.size(); i++) { - if (i > 0) { - ret.add(ins(new LabelIns())); - } - int clauseId = localData.finallyCatches.get(i); - Integer cnt = localData.finallyCounter.get(clauseId); - if (cnt == null) { - cnt = -1; - } - cnt++; - localData.finallyCounter.put(clauseId, cnt); - ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); - ret.add(ins(new FinallyJumpIns(clauseId), 0)); - ret.add(ins(new LabelIns())); - ret.add(ins(new PopIns())); - } - ret.add(ins(new LabelIns())); - } - ret.add(ins(new ReturnVoidIns())); - return ret; - } - - public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(item.value.toSource(localData, this)); - ret.add(ins(new ThrowIns())); - return ret; - } - - private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { - return toInsList(generate(localData, commands)); - } - - private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { - return toInsList(command.toSource(localData, this)); - } - - @Override - public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { - List ret = new ArrayList<>(); - for (GraphTargetItem item : commands) { - ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); - } - return ret; - } - - public HashMap getRegisterVars(SourceGeneratorLocalData localData) { - return localData.registerVars; - } - - public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { - localData.registerVars = value; - } - - public void setInFunction(SourceGeneratorLocalData localData, int value) { - localData.inFunction = value; - } - - public int isInFunction(SourceGeneratorLocalData localData) { - return localData.inFunction; - } - - public boolean isInMethod(SourceGeneratorLocalData localData) { - return localData.inMethod; - } - - public void setInMethod(SourceGeneratorLocalData localData, boolean value) { - localData.inMethod = value; - } - - public int getForInLevel(SourceGeneratorLocalData localData) { - return localData.forInLevel; - } - - public void setForInLevel(SourceGeneratorLocalData localData, int value) { - localData.forInLevel = value; - } - - public int getTempRegister(SourceGeneratorLocalData localData) { - HashMap registerVars = getRegisterVars(localData); - int tmpReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - tmpReg = i; - break; - } - } - return tmpReg; - } - - public AVM2SourceGenerator(ABC abc, List allABCs) { - this.abc = abc; - this.allABCs = allABCs; - } - - public ABC getABC() { - return abc; - } - - public void generateClass(List importedClasses,List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems) throws ParseException, CompilationException { - localData.currentClass = name; - localData.pkg = pkg; - List ret = new ArrayList<>(); - if (extendsVal == null && !isInterface) { - extendsVal = new TypeItem("Object"); - } - ParsedSymbol s = null; - - instanceInfo.name_index = traitName(namespace, name); - - Trait[] it = generateTraitsPhase1(name, superName, false, localData, traitItems, instanceInfo.instance_traits); - Trait[] st = generateTraitsPhase1(name, superName, true, localData, traitItems, classInfo.static_traits); - generateTraitsPhase2(importedClasses,pkg, traitItems, it, openedNamespaces, localData); - generateTraitsPhase2(importedClasses,pkg, traitItems, st, openedNamespaces, localData); - generateTraitsPhase3(initScope,isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it,new HashMap()); - generateTraitsPhase3(initScope,isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st,new HashMap()); - int init = 0; - if (constructor == null || isInterface) { - instanceInfo.iinit_index = init = method(isInterface, new ArrayList(), pkg, false, new ArrayList(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), TypeItem.UNBOUNDED/*?? FIXME*/); - } else { - MethodAVM2Item m = (MethodAVM2Item) constructor; - instanceInfo.iinit_index = init = method(false, new ArrayList(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); - } - - //Class initializer - int staticMi = method(false, new ArrayList(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList(), new ArrayList(), new ArrayList(), staticInit, TypeItem.UNBOUNDED); - MethodBody sinitBody = abc.findBody(staticMi); - - List sinitcode = new ArrayList<>(); - List initcode = new ArrayList<>(); - for (GraphTargetItem ti : traitItems) { - if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { - GraphTargetItem val = null; - boolean isStatic = false; - int ns = -1; - String tname = null; - boolean isConst = false; - if (ti instanceof SlotAVM2Item) { - val = ((SlotAVM2Item) ti).value; - isStatic = ((SlotAVM2Item) ti).isStatic(); - ns = ((SlotAVM2Item) ti).getNamespace(); - tname = ((SlotAVM2Item) ti).var; - } - if (ti instanceof ConstAVM2Item) { - val = ((ConstAVM2Item) ti).value; - isStatic = ((ConstAVM2Item) ti).isStatic(); - ns = ((ConstAVM2Item) ti).getNamespace(); - tname = ((ConstAVM2Item) ti).var; - isConst = true; - } - if (isStatic && val != null) { - sinitcode.add(ins(new FindPropertyIns(), traitName(ns, tname))); - sinitcode.addAll(toInsList(val.toSource(localData, this))); - sinitcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); - } - if (!isStatic && val != null) { - //do not init basic values, that can be stored in trait - if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { - initcode.add(ins(new GetLocal0Ins())); - initcode.addAll(toInsList(val.toSource(localData, this))); - initcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); - } - } - } - } - MethodBody initBody = null; - if (!isInterface) { - initBody = abc.findBody(init); - initBody.code.code.addAll(constructor == null ? 0 : 2, initcode);//after getlocal0,pushscope - - if (sinitBody.code.code.get(sinitBody.code.code.size() - 1).definition instanceof ReturnVoidIns) { - sinitBody.code.code.addAll(2, sinitcode); //after getlocal0,pushscope - } - } - sinitBody.markOffsets(); - sinitBody.autoFillStats(abc, initScope + (implementsStr.isEmpty() ? 0 : 1), true); - - classInfo.cinit_index = staticMi; - if (!isInterface) { - initBody.autoFillStats(abc, initScope + 1, true); - } - instanceInfo.interfaces = new int[implementsStr.size()]; - for (int i = 0; i < implementsStr.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData,implementsStr.get(i)); - } - } - - @Override - public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { - if (item.commands.isEmpty()) { - return new ArrayList<>(); - } - - //We need to handle commands and last expression separately, otherwise last expression result will be popped - List cmds = new ArrayList<>(item.commands); - GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); - List ret = new ArrayList<>(); - ret.addAll(generate(localData, cmds)); - ret.addAll(lastExpr.toSource(localData, this)); - return ret; - } - - public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, String pkg, SourceGeneratorLocalData localData, AVM2Item cls) throws ParseException, CompilationException { - /*ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - abc.class_info.add(ci); - abc.instance_info.add(ii); - */ - if (cls instanceof ClassAVM2Item) { - ClassAVM2Item cai = (ClassAVM2Item) cls; - generateClass(cai.importedClasses,cai.sinitVariables, cai.staticInitActivation, cai.staticInit, cai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, false, cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), cai.extendsOp, cai.implementsOp, cai.constructor, cai.traits); - if (!cai.isDynamic) { - ii.flags |= InstanceInfo.CLASS_SEALED; - } - if (cai.isFinal) { - ii.flags |= InstanceInfo.CLASS_FINAL; - } - ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; - ii.protectedNS = cai.protectedNs; - } - if (cls instanceof InterfaceAVM2Item) { - InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; - ii.flags |= InstanceInfo.CLASS_INTERFACE; - ii.flags |= InstanceInfo.CLASS_SEALED; - generateClass(iai.importedClasses,new ArrayList(), false, new ArrayList(), iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, iai.methods); - } - - return abc.instance_info.size() - 1; - } - - public int traitName(int namespace, String var) { - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(var), namespace, 0, 0, new ArrayList()), true); - } - - public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { - if (type instanceof UnboundedTypeItem) { - return 0; - } - - - return resolveType(localData,type, abc,allABCs); - /* - TypeItem nameItem = (TypeItem) type; - name = nameItem.fullTypeName; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName - List params = new ArrayList<>(); - for (GraphTargetItem p : nameItem.subtypes) { - params.add(typeName(localData, p));//abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); - } - int qname = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); - } else { - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); - }*/ - } - - public int ident(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - return str(((NameAVM2Item) name).getVariableName()); - } - throw new RuntimeException("no ident"); //FIXME - } - - public int namespace(int nsKind, String name) { - return abc.constants.getNamespaceId(new Namespace(nsKind, str(name)), 0, true); - } - - public int str(String name) { - return abc.constants.getStringId(name, true); - } - - public int propertyName(GraphTargetItem name) { - if (name instanceof NameAVM2Item) { - NameAVM2Item va = (NameAVM2Item) name; - return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - } - throw new RuntimeException("no prop"); //FIXME - } - - public int getFreeRegister(SourceGeneratorLocalData localData) { - for (int i = 0;; i++) { - if (!localData.registerVars.containsValue(i)) { - localData.registerVars.put("__TEMP__" + i, i); - return i; - } - } - } - - public boolean killRegister(SourceGeneratorLocalData localData, int i) { - String key = null; - for (String k : localData.registerVars.keySet()) { - if (localData.registerVars.get(k) == i) { - key = k; - break; - } - } - if (key != null) { - localData.registerVars.remove(key); - return true; - } - return false; - } - - public int method(boolean isInterface, List callStack, String pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { - //Reference hasArgs = new Reference<>(Boolean.FALSE); - //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); - SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap(), 1, true, 0); - newlocalData.currentClass = className; - newlocalData.pkg = localData.pkg; - newlocalData.callStack.addAll(localData.callStack); - newlocalData.traitUsages = localData.traitUsages; - newlocalData.currentScript = localData.currentScript; - newlocalData.documentClass = localData.documentClass; - localData = newlocalData; - - localData.activationReg = 0; - - for (int i = 0; i < subvariables.size(); i++) { - AssignableAVM2Item an = subvariables.get(i); - if (an instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; - if (n.resolved == null) { - String fullClass = localData.getFullClass(); - GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); - if (res instanceof AssignableAVM2Item) { - subvariables.set(i, (AssignableAVM2Item) res); - } else { - subvariables.remove(i); - i--; - } - } - } - } - - boolean hasArguments = false; - List slotNames = new ArrayList<>(); - List slotTypes = new ArrayList<>(); - slotNames.add("--first"); - slotTypes.add("-"); - - List registerNames = new ArrayList<>(); - List registerTypes = new ArrayList<>(); - if (className != null) { - String fullClassName = pkg==null||pkg.isEmpty() ? className : pkg + "." + className; - registerTypes.add(fullClassName); - localData.scopeStack.add(new LocalRegAVM2Item(null, registerNames.size(), null)); - registerNames.add("this"); - - } else { - registerTypes.add("global"); - registerNames.add("this"); - } - for (GraphTargetItem t : paramTypes) { - registerTypes.add(t.toString()); - slotTypes.add(t.toString()); - } - registerNames.addAll(paramNames); - slotNames.addAll(paramNames); - for (GraphTargetItem p : paramTypes) { - slotTypes.add("" + p); - } - if (hasRest) { - slotTypes.add("*"); - } - localData.registerVars.clear(); - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.getVariableName().equals("arguments") & !n.isDefinition()) { - registerNames.add("arguments"); - registerTypes.add("Object"); - hasArguments = true; - break; - } - } - } - int paramRegCount = registerNames.size(); - - if (needsActivation) { - registerNames.add("+$activation"); - localData.activationReg = registerNames.size() - 1; - registerTypes.add("Object"); - localData.scopeStack.add(new LocalRegAVM2Item(null, localData.activationReg, null)); - } - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.isDefinition()) { - if (!needsActivation || (n.getSlotScope() <= 0)) { - registerNames.add(n.getVariableName()); - registerTypes.add(n.type.toString()); - slotNames.add(n.getVariableName()); - slotTypes.add(n.type.toString()); - } - } - } - } - - int slotScope = className == null ? 0 : 1; - - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - if (n.getVariableName() != null) { - if (!n.getVariableName().equals("this") && needsActivation) { - if (n.getSlotNumber() <= 0) { - n.setSlotNumber(slotNames.indexOf(n.getVariableName())); - n.setSlotScope(slotScope); - } - } else { - n.setRegNumber(registerNames.indexOf(n.getVariableName())); - } - } - } - } - - for (int i = 0; i < registerNames.size(); i++) { - if (needsActivation && i > localData.activationReg) { - break; - } - localData.registerVars.put(registerNames.get(i), i); - } - List declarations = new ArrayList<>(); - loopn: - for (AssignableAVM2Item an : subvariables) { - if (an instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) an; - - if (needsActivation) { - if (n.getSlotScope() != slotScope) { - continue; - } else { - if (n.getSlotNumber() < paramRegCount) { - continue; - } - } - } - for (NameAVM2Item d : declarations) { - if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { - continue loopn; - } - } - - for (GraphTargetItem it : body) { //search first level of commands - if (it instanceof NameAVM2Item) { - NameAVM2Item n2 = (NameAVM2Item) it; - if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { - continue loopn; - } - if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined - break; - } - } - } - if (n.unresolved) { - continue; - } - if (n.redirect != null) { - continue; - } - if (n.getNs() != null) { - continue; - } - - if ("this".equals(n.getVariableName()) || paramNames.contains(n.getVariableName()) || "argmuments".equals(n.getVariableName())) { - continue; - } - - NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); - //no index - if (needsActivation) { - if (d.getSlotNumber() <= 0) { - d.setSlotNumber(n.getSlotNumber()); - d.setSlotScope(n.getSlotScope()); - } - } else { - d.setRegNumber(n.getRegNumber()); - } - declarations.add(d); - } - } - - int param_types[] = new int[paramTypes.size()]; - ValueKind optional[] = new ValueKind[paramValues.size()]; - //int param_names[] = new int[paramNames.size()]; - for (int i = 0; i < paramTypes.size(); i++) { - param_types[i] = typeName(localData, paramTypes.get(i)); - //param_names[i] = str(paramNames.get(i)); - } - - for (int i = 0; i < paramValues.size(); i++) { - optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); - if (optional[i] == null) { - throw new CompilationException("Default value must be compiletime constant", line); - } - } - - MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), 0/*name_index*/, 0, optional, new int[0]/*no param_names*/); - if (hasArguments) { - mi.setFlagNeed_Arguments(); - } - //No param names like in official - /* - if (!paramNames.isEmpty()) { - mi.setFlagHas_paramnames(); - }*/ - if (!paramValues.isEmpty()) { - mi.setFlagHas_optional(); - } - if (hasRest) { - mi.setFlagNeed_rest(); - } - - int mindex; - if (!isInterface) { - MethodBody mbody = new MethodBody(); - - if (needsActivation) { - mbody.traits = new Traits(); - int slotId = 1; - for (int i = 1; i < slotNames.size(); i++) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.slot_id = slotId++; - tsc.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(slotNames.get(i), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); - mbody.traits.traits.add(tsc); - } - for (int i = 1; i < paramRegCount; i++) { - NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList()); - param.setRegNumber(i); - NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList()); - d.setSlotScope(slotScope); - d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); - declarations.add(d); - } - } - if (body != null) { - body.addAll(0, declarations); - } - - localData.exceptions = new ArrayList<>(); - localData.callStack.add(mbody); - List src = body == null ? new ArrayList() : generate(localData, body); - - mbody.method_info = abc.addMethodInfo(mi); - mi.setBody(mbody); - mbody.code = new AVM2Code(); - mbody.code.code = toInsList(src); - - if (needsActivation) { - if (localData.traitUsages.containsKey(mbody)) { - List usages = localData.traitUsages.get(mbody); - for (int i = 0; i < mbody.traits.traits.size(); i++) { - if (usages.contains(i)) { - TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); - GraphTargetItem type = TypeItem.UNBOUNDED; - if (tsc.type_index > 0) { - type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants)); - } - NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abc).getName(abc.constants, new ArrayList()), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList()); - d.setSlotNumber(tsc.slot_id); - d.setSlotScope(slotScope); - mbody.code.code.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); - } - } - } - - List acts = new ArrayList<>(); - acts.add(ins(new NewActivationIns())); - acts.add(ins(new DupIns())); - acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); - acts.add(ins(new PushScopeIns())); - - mbody.code.code.addAll(0, acts); - } - - if (constructor) { - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - - int parentConstMinAC = 0; - - for (ABC a : abcs) { - int ci = a.findClassByName(superType); - if (ci > -1) { - MethodInfo pmi = a.method_info.get(a.instance_info.get(ci).iinit_index); - parentConstMinAC = pmi.param_types.length; - if (pmi.flagHas_optional()) { - parentConstMinAC -= pmi.optional.length; - } - - } - } - int ac = -1; - for (AVM2Instruction ins : mbody.code.code) { - if (ins.definition instanceof ConstructSuperIns) { - ac = ins.operands[0]; - if (parentConstMinAC > ac) { - throw new CompilationException("Parent constructor call requires different number of arguments", line); - } - - } - } - if (ac == -1) { - if (parentConstMinAC == 0) { - mbody.code.code.add(0, new AVM2Instruction(0, new GetLocal0Ins(), new int[]{}, new byte[0])); - mbody.code.code.add(1, new AVM2Instruction(0, new ConstructSuperIns(), new int[]{0}, new byte[0])); - - } else { - throw new CompilationException("Parent constructor must be called", line); - } - } - } - if (className != null) {//It's method, not (inner) function - mbody.code.code.add(0, new AVM2Instruction(0, new GetLocal0Ins(), new int[]{}, new byte[0])); - mbody.code.code.add(1, new AVM2Instruction(0, new PushScopeIns(), new int[]{}, new byte[0])); - } - boolean addRet = false; - if (!mbody.code.code.isEmpty()) { - InstructionDefinition lastDef = mbody.code.code.get(mbody.code.code.size() - 1).definition; - if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { - addRet = true; - } - } else { - addRet = true; - } - if (addRet) { - if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { - mbody.code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[]{}, new byte[0])); - } else { - mbody.code.code.add(new AVM2Instruction(0, new PushUndefinedIns(), new int[]{}, new byte[0])); - mbody.code.code.add(new AVM2Instruction(0, new ReturnValueIns(), new int[]{}, new byte[0])); - } - } - mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); - int offset = 0; - for (int i = 0; i < mbody.code.code.size(); i++) { - AVM2Instruction ins = mbody.code.code.get(i); - if (ins instanceof ExceptionMarkAVM2Instruction) { - ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; - switch (m.markType) { - case MARK_E_START: - mbody.exceptions[m.exceptionId].start = offset; - break; - case MARK_E_END: - mbody.exceptions[m.exceptionId].end = offset; - break; - case MARK_E_TARGET: - mbody.exceptions[m.exceptionId].target = offset; - break; - } - mbody.code.code.remove(i); - i--; - continue; - } - offset += ins.getBytes().length; - } - - mbody.markOffsets(); - mbody.autoFillStats(abc, initScope, className != null); - abc.addMethodBody(mbody); - mindex = mbody.method_info; - } else { - mindex = abc.addMethodInfo(mi); - } - - return mindex; - } - - public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { - - if (val instanceof BooleanAVM2Item) { - BooleanAVM2Item bi = (BooleanAVM2Item) val; - if (bi.value) { - return new ValueKind(0, ValueKind.CONSTANT_True); - } else { - return new ValueKind(0, ValueKind.CONSTANT_False); - } - } - - boolean isNs = false; - if (type instanceof NameAVM2Item) { - if (((NameAVM2Item) type).getVariableName().equals("namespace")) { - isNs = true; - } - } - - if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals("Namespace"))) { - isNs = true; - } - - if (val instanceof StringAVM2Item) { - StringAVM2Item sval = (StringAVM2Item) val; - if (isNs) { - return new ValueKind(namespace(abc.constants.constant_namespace.get(ns).kind, sval.value), Namespace.KIND_NAMESPACE); - } else { - return new ValueKind(str(sval.value), ValueKind.CONSTANT_Utf8); - } - } - if (val instanceof IntegerValueAVM2Item) { - return new ValueKind(abc.constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); - } - if (val instanceof FloatValueAVM2Item) { - return new ValueKind(abc.constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); - } - if(val instanceof NanAVM2Item){ - return new ValueKind(abc.constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); - } - if (val instanceof NullAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Null); - } - if (val instanceof UndefinedAVM2Item) { - return new ValueKind(0, ValueKind.CONSTANT_Undefined); - } - return null; - } - - private int genNs(List importedClasses,String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { - if (custom != null) { - PropertyAVM2Item prop = new PropertyAVM2Item(null, custom, abc, allABCs, openedNamespaces, new ArrayList()); - Reference value = new Reference<>(null); - prop.resolve(localData, new Reference(null), new Reference(null), new Reference(0), value); - boolean resolved = true; - if (value.getVal() == null) { - resolved = false; - } - if (!resolved) { - - String customPkg = ""; - String fullCustom = ""; - for(String imp:importedClasses){ - if(imp.endsWith("."+custom)){ - customPkg = imp.substring(0,imp.lastIndexOf(".")); - fullCustom = imp; - break; - } - } - - List aas=new ArrayList<>(); - aas.add(abc); - aas.addAll(allABCs); - for(ABC a:aas){ - for(ScriptInfo si:a.script_info){ - for(Trait t:si.traits.traits){ - Multiname m = t.getName(a); - if(fullCustom.equals(m.getNameWithNamespace(a.constants))){ - if(t instanceof TraitSlotConst){ - if(((TraitSlotConst)t).isNamespace()){ - return ((TraitSlotConst)t).value_index; - } - } - } - } - } - } - - throw new CompilationException("Namespace not defined", line); - } - namespace = value.getVal().value_index; - } - return namespace; - } - - public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } else if (item instanceof InterfaceAVM2Item) { - traits[k].name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - } else if (item instanceof ClassAVM2Item) { - traits[k].name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - traits[k].name_index = traitName(genNs(importedClasses,pkg, ((MethodAVM2Item) item).customNamespace, ((MethodAVM2Item) item).namespace, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); - } else if (item instanceof FunctionAVM2Item) { - traits[k].name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); - } else if (item instanceof ConstAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses,pkg, ((ConstAVM2Item) item).customNamespace, ((ConstAVM2Item) item).getNamespace(), openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); - } else if (item instanceof SlotAVM2Item) { - traits[k].name_index = traitName(genNs(importedClasses,pkg, ((SlotAVM2Item) item).customNamespace, ((SlotAVM2Item) item).getNamespace(), openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); - } - } - - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof ClassAVM2Item) { - InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList())); - - if (((ClassAVM2Item) item).extendsOp != null) { - instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); - } else { - instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - } - instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; - for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { - instanceInfo.interfaces[i] = superIntName(localData,((ClassAVM2Item) item).implementsOp.get(i)); - } - } - if (item instanceof InterfaceAVM2Item) { - InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); - instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((InterfaceAVM2Item) item).name, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList())); - - instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; - for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { - GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); - instanceInfo.interfaces[i] = superIntName(localData,un); - } - } - } - } - - public int superIntName(SourceGeneratorLocalData localData,GraphTargetItem un) throws CompilationException { - if (un instanceof UnresolvedAVM2Item) { - ((UnresolvedAVM2Item) un).resolve(null, new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - un = ((UnresolvedAVM2Item) un).resolved; - } - if (!(un instanceof TypeItem)) { //not applyType - throw new CompilationException("Invalid type", 0); - } - TypeItem sup = (TypeItem) un; - int propId = resolveType(localData,sup, abc,allABCs); - int nss[] = new int[]{abc.constants.constant_multiname.get(propId).namespace_index}; - return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(propId).name_index, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true); - - } - - public void generateTraitsPhase3(int methodInitScope,boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits,Map initScopes) throws ParseException, CompilationException { - //Note: Names must be generated first before accesed in inner subs - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (traits[k] == null) { - continue; - } - if (item instanceof InterfaceAVM2Item) { - generateClass(((InterfaceAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item)item).pkg, localData, (InterfaceAVM2Item) item); - } - - if (item instanceof ClassAVM2Item) { - generateClass(((ClassAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item)item).pkg, localData, (ClassAVM2Item) item); - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - ((TraitMethodGetterSetter) traits[k]).method_info = method(isInterface, new ArrayList(), mai.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); - } else if (item instanceof FunctionAVM2Item) { - FunctionAVM2Item fai = (FunctionAVM2Item) item; - ((TraitFunction) traits[k]).method_info = method(isInterface, new ArrayList(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); - } - } - } - - public Trait[] generateTraitsPhase1(String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts) throws ParseException, CompilationException { - Trait[] traits = new Trait[items.size()]; - int slot_id = 1; - int disp_id = 3; //1 and 2 are for constructor - for (int k = 0; k < items.size(); k++) { - GraphTargetItem item = items.get(k); - if (item instanceof InterfaceAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo ii = new InstanceInfo(); - abc.class_info.add(ci); - abc.instance_info.add(ii); - ii.flags |= InstanceInfo.CLASS_INTERFACE; - tc.class_info = abc.instance_info.size() - 1; - tc.kindType = Trait.TRAIT_CLASS; - //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); - tc.slot_id = 0; //? - ts.traits.add(tc); - traits[k] = tc; - } - - if (item instanceof ClassAVM2Item) { - TraitClass tc = new TraitClass(); - ClassInfo ci = new ClassInfo(); - InstanceInfo instanceInfo = new InstanceInfo(); - abc.class_info.add(ci); - abc.instance_info.add(instanceInfo); - tc.class_info = abc.instance_info.size() - 1; - - /*instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), - abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg.packageName, true)), 0, true), 0, 0, new ArrayList())); - */ - - /*if (((ClassAVM2Item) item).extendsOp != null) { - instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); - } else { - instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); - }*/ - tc.kindType = Trait.TRAIT_CLASS; - // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); - tc.slot_id = slot_id++; - ts.traits.add(tc); - traits[k] = tc; - - } - if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { - TraitSlotConst tsc = new TraitSlotConst(); - tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; - String var = null; - GraphTargetItem val = null; - GraphTargetItem type = null; - boolean isNamespace = false; - int namespace = 0; - boolean isStatic = false; - if (item instanceof SlotAVM2Item) { - SlotAVM2Item sai = (SlotAVM2Item) item; - if (sai.isStatic() != generateStatic) { - continue; - } - var = sai.var; - val = sai.value; - type = sai.type; - isStatic = sai.isStatic(); - namespace = sai.getNamespace(); - } - if (item instanceof ConstAVM2Item) { - ConstAVM2Item cai = (ConstAVM2Item) item; - if (cai.isStatic() != generateStatic) { - continue; - } - var = cai.var; - val = cai.value; - type = cai.type; - namespace = cai.getNamespace(); - isNamespace = type.toString().equals("Namespace"); - isStatic = cai.isStatic(); - } - if (isNamespace) { - tsc.name_index = traitName(namespace, var); - } - tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); - - ValueKind vk = getValueKind(namespace, type, val); - if (vk == null) { - tsc.value_kind = ValueKind.CONSTANT_Undefined; - } else { - tsc.value_kind = vk.value_kind; - tsc.value_index = vk.value_index; - } - tsc.slot_id = isStatic ? slot_id++ : 0; - ts.traits.add(tsc); - traits[k] = tsc; - } - if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { - MethodAVM2Item mai = (MethodAVM2Item) item; - if (mai.isStatic() != generateStatic) { - continue; - } - TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); - tmgs.kindType = (item instanceof MethodAVM2Item) ? Trait.TRAIT_METHOD : ((item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : Trait.TRAIT_SETTER); - //tmgs.name_index = traitName(((MethodAVM2Item) item).namespace, ((MethodAVM2Item) item).functionName); - tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) - if (mai.isFinal() || mai.isStatic()) { - tmgs.kindFlags |= Trait.ATTR_Final; - } - if (mai.isOverride()) { - tmgs.kindFlags |= Trait.ATTR_Override; - } - ts.traits.add(tmgs); - - traits[k] = tmgs; - } else if (item instanceof FunctionAVM2Item) { - TraitFunction tf = new TraitFunction(); - tf.slot_id = slot_id++; - tf.kindType = Trait.TRAIT_FUNCTION; - //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); - ts.traits.add(tf); - traits[k] = tf; - } - } - - return traits; - } - - public ScriptInfo generateScriptInfo(SourceGeneratorLocalData localData, List commands) throws ParseException, CompilationException { - ScriptInfo si = new ScriptInfo(); - localData.currentScript = si; - Trait[] traitArr = generateTraitsPhase1(null, null, false, localData, commands, si.traits); - generateTraitsPhase2(new ArrayList(),null/*FIXME*/, commands, traitArr, new ArrayList(), localData); - MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); - MethodBody mb = new MethodBody(); - mb.method_info = abc.addMethodInfo(mi); - mb.code = new AVM2Code(); - mb.code.code.add(ins(new GetLocal0Ins())); - mb.code.code.add(ins(new PushScopeIns())); - - int traitScope = 2; - - Map initScopes = new HashMap<>(); - - for (Trait t : si.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - List parents = new ArrayList<>(); - if (localData.documentClass) { - mb.code.code.add(ins(new GetScopeObjectIns(), 0)); - traitScope++; - } else { - NamespaceSet nsset = new NamespaceSet(new int[]{abc.constants.constant_multiname.get(tc.name_index).namespace_index}); - mb.code.code.add(ins(new FindPropertyStrictIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(tc.name_index).name_index, 0, abc.constants.getNamespaceSetId(nsset, true), 0, new ArrayList()), true))); - } - if (abc.instance_info.get(tc.class_info).isInterface()) { - mb.code.code.add(ins(new PushNullIns())); - } else { - - parentNamesAddNames(abc, allABCs, abc.instance_info.get(tc.class_info).name_index, parents, new ArrayList(), new ArrayList()); - for (int i = parents.size() - 1; i >= 1; i--) { - mb.code.code.add(ins(new GetLexIns(), parents.get(i))); - mb.code.code.add(ins(new PushScopeIns())); - traitScope++; - } - mb.code.code.add(ins(new GetLexIns(), parents.get(1))); - } - mb.code.code.add(ins(new NewClassIns(), tc.class_info)); - if (!abc.instance_info.get(tc.class_info).isInterface()) { - for (int i = parents.size() - 1; i >= 1; i--) { - mb.code.code.add(ins(new PopScopeIns())); - } - } - mb.code.code.add(ins(new InitPropertyIns(), tc.name_index)); - initScopes.put(t, traitScope); - traitScope = 1; - } - } - - mb.code.code.add(ins(new ReturnVoidIns())); - mb.autoFillStats(abc, 1, false); - abc.addMethodBody(mb); - si.init_index = mb.method_info; - localData.pkg = null; //FIXME: pkg.packageName; - generateTraitsPhase3(1/*??*/,false, null, null, false, localData, commands, si.traits, traitArr,initScopes); - return si; - } - - public static void parentNamesAddNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces) { - List cindices = new ArrayList<>(); - - List outABCs = new ArrayList<>(); - parentNames(abc, allABCs, name_index, cindices, names, namespaces, outABCs); - for (int i = 0; i < cindices.size(); i++) { - ABC a = outABCs.get(i); - int m = cindices.get(i); - if (a == abc) { - indices.add(m); - continue; - } - Multiname superName = a.constants.constant_multiname.get(m); - indices.add( - abc.constants.getMultinameId( - new Multiname(Multiname.QNAME, - abc.constants.getStringId(superName.getName(a.constants, new ArrayList()), true), - abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true) - ); - } - } - - public static GraphTargetItem getTraitReturnType(ABC abc, Trait t) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - if (tsc.type_index == 0) { - return TypeItem.UNBOUNDED; - } - return PropertyAVM2Item.multinameToType(tsc.type_index,abc.constants); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.kindType == Trait.TRAIT_GETTER) { - return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).ret_type,abc.constants); - } - if (tmgs.kindType == Trait.TRAIT_SETTER) { - if(abc.method_info.get(tmgs.method_info).param_types.length>0){ - return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).param_types[0],abc.constants); - }else{ - return TypeItem.UNBOUNDED; - } - } - } - if (t instanceof TraitFunction) { - return new TypeItem("Function"); - } - return TypeItem.UNBOUNDED; - } - - private static boolean eq(Object a,Object b){ - if(a==null && b==null){ - return true; - } - if(a == null){ - return false; - } - if(b == null){ - return false; - } - return a.equals(b); - } - - public static boolean searchPrototypeChain(boolean instanceOnly, List abcs, String pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue) { - - for (ABC abc : abcs) { - if (!instanceOnly) { - for (ScriptInfo ii : abc.script_info) { - if(ii.deleted){ - continue; - } - for (Trait t : ii.traits.traits) { - if (eq(pkg,t.getName(abc).getNamespace(abc.constants).getName(abc.constants))) { - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - } - } - } - for (int i = 0; i < abc.instance_info.size(); i++) { - InstanceInfo ii = abc.instance_info.get(i); - if(ii.deleted){ - continue; - } - Multiname clsName = ii.getName(abc.constants); - if (obj.equals(clsName.getName(abc.constants, new ArrayList()))) { - if (eq(pkg,clsName.getNamespace(abc.constants).getName(abc.constants))) { - //class found - - for (Trait t : ii.instance_traits.traits) { - if (t.getName(abc) == null) { //in traits phase 2 - continue; - } - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - - if (!instanceOnly) { - for (Trait t : abc.class_info.get(i).static_traits.traits) { - if (t.getName(abc) == null) { //in traits phase 2 - continue; - } - if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { - outName.setVal(obj); - outNs.setVal(pkg); - outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); - outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); - outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); - outPropType.setVal(getTraitReturnType(abc, t)); - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); - } - return true; - } - } - } - - Multiname superName = abc.constants.constant_multiname.get(ii.super_index); - if (superName != null) { - return searchPrototypeChain(instanceOnly, abcs, superName.getNamespace(abc.constants).getName(abc.constants), superName.getName(abc.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind,outPropNsIndex, outPropType, outPropValue); - } else { - return false; - } - } - } - } - } - return false; - } - - public static void parentNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces, List outABCs) { - indices.add(name_index); - names.add(abc.constants.constant_multiname.get(name_index).getName(abc.constants, new ArrayList())); - namespaces.add(abc.constants.constant_multiname.get(name_index).getNamespace(abc.constants).getName(abc.constants)); - Multiname mname = abc.constants.constant_multiname.get(name_index); - - outABCs.add(abc); - - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - - for (ABC a : abcs) { - for (int i = 0; i < a.instance_info.size(); i++) { - Multiname m = a.constants.constant_multiname.get(a.instance_info.get(i).name_index); - if (m.getName(a.constants, new ArrayList()).equals(mname.getName(abc.constants, new ArrayList()))) { - - if (m.getNamespace(a.constants).hasName(a.constants,mname.getNamespace(abc.constants).getName(abc.constants))) { - //Multiname superName = a.constants.constant_multiname.get(a.instance_info.get(i).super_index); - abcs.remove(a); - if (a.instance_info.get(i).super_index != 0) { - parentNames(a, abcs, a.instance_info.get(i).super_index, indices, names, namespaces, outABCs); - } - /*parentNames(abc,allABCs,abc.constants.getMultinameId( - new Multiname(superName.kind, - abc.constants.getStringId(superName.getName(a.constants, new ArrayList()),true), - abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants),true)),0,true), 0, 0, new ArrayList()), true),indices,names,namespaces,outABCs);*/ - return; - } - } - } - } - } - - /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { - - }*/ - /*public int resolveType(String objType) { - if (objType.equals("*")) { - return 0; - } - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(allABCs); - for (ABC a : abcs) { - int ci = a.findClassByName(objType); - if (ci != -1) { - Multiname tname = a.instance_info.get(ci).getName(a.constants); - return abc.constants.getMultinameId(new Multiname(tname.kind, - abc.constants.getStringId(tname.getName(a.constants, new ArrayList()), true), - abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - return 0; - }*/ - - @Override - public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { - String currentFullClassName = localData.getFullClass(); - - if (localData.documentClass && item.toString().equals(currentFullClassName)) { - int slotId = 0; - int c = abc.findClassByName(currentFullClassName); - for (Trait t : localData.currentScript.traits.traits) { - if (t instanceof TraitClass) { - TraitClass tc = (TraitClass) t; - if (tc.class_info == c) { - slotId = tc.slot_id; - break; - } - } - } - return GraphTargetItem.toSourceMerge(localData, this, ins(new GetGlobalScopeIns()), ins(new GetSlotIns(), slotId)); - } else { - return GraphTargetItem.toSourceMerge(localData, this, ins(new GetLexIns(), resolveType(localData,item, abc,allABCs))); - } - } - - public static int resolveType(SourceGeneratorLocalData localData,GraphTargetItem item, ABC abc,List allABCs) throws CompilationException { - int name_index = 0; - GraphTargetItem typeItem = null; - - - if (item instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - } - if (item instanceof TypeItem) { - typeItem = item; - } else if (item instanceof ApplyTypeAVM2Item) { - typeItem = ((ApplyTypeAVM2Item) item).object; - } else { - throw new CompilationException("Invalid type:"+item.getClass().getName(), 0/*??*/); - } - if (typeItem instanceof UnresolvedAVM2Item) { - String fullClass = localData.getFullClass(); - typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); - } - - if (!(typeItem instanceof TypeItem)) { - throw new CompilationException("Invalid type", 0/*??*/); - } - - TypeItem type = (TypeItem) typeItem; - - String name = type.fullTypeName; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - for (InstanceInfo ii : abc.instance_info) { - Multiname mname = abc.constants.constant_multiname.get(ii.name_index); - if (mname!=null && name.equals(mname.getName(abc.constants, new ArrayList()))) { - if (mname.getNamespace(abc.constants).hasName(pkg, abc.constants)) { - name_index = ii.name_index; - break; - } - } - } - for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { - Multiname mname = abc.constants.constant_multiname.get(i); - if (mname!=null && name.equals(mname.getName(abc.constants, new ArrayList()))) { - if (mname.getNamespace(abc.constants) != null && pkg.equals(mname.getNamespace(abc.constants).getName(abc.constants))) { - name_index = i; - break; - } - } - } - if (name_index == 0) { - if(pkg.equals("") && localData.currentScript!=null /*FIXME!*/){ - for(Trait t:localData.currentScript.traits.traits){ - if(t.getName(abc).getName(abc.constants, new ArrayList()).equals(name)){ - name_index = t.name_index; - break; - } - } - } - if(name_index == 0){ - name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); - } - } - - if (item instanceof ApplyTypeAVM2Item) { - ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; - List params = new ArrayList<>(); - for (GraphTargetItem s : atype.params) { - params.add(resolveType(localData, s, abc,allABCs)); - } - return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, name_index, params), true); - } - - return name_index; - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.NotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructSuperIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewActivationIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewClassIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfFalseIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictNeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfTrueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.LookupSwitchIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetDescendantsIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.InitPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextNameIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.NextValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnValueIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ThrowIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushNullIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushUndefinedIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushWithIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.SwapIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.xml.CheckFilterIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.IfCondition; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.ClassInfo; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.Loop; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.DoWhileItem; +import com.jpexs.decompiler.graph.model.DuplicateItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class AVM2SourceGenerator implements SourceGenerator { + + public final ABC abc; + public List allABCs; + + public static final int MARK_E_START = 0; + public static final int MARK_E_END = 1; + public static final int MARK_E_TARGET = 2; + public static final int MARK_E_FINALLYPART = 3; + + private AVM2Instruction ins(InstructionDefinition def, int... operands) { + return new AVM2Instruction(0, def, operands, new byte[0]); + } + + public List generate(SourceGeneratorLocalData localData, GetDescendantsAVM2Item item) throws CompilationException { + int nssa[] = new int[item.openedNamespaces.size()]; + for (int i = 0; i < item.openedNamespaces.size(); i++) { + nssa[i] = item.openedNamespaces.get(i); + } + int nsset = abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + + return GraphTargetItem.toSourceMerge(localData, this, + item.object, + ins(new GetDescendantsIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.getStringId(item.nameStr, true), 0, nsset, 0, new ArrayList()), true)) + ); + } + + @Override + public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(new ConvertBIns())); + } + ret.add(ins(new DupIns())); + List andExpr = generateToActionList(localData, item.rightSide); + andExpr.add(0, ins(new PopIns())); + int andExprLen = insToBytes(andExpr).length; + ret.add(ins(new IfFalseIns(), andExprLen)); + ret.addAll(andExpr); + return ret; + + } + + private byte[] insToBytes(List code) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (AVM2Instruction instruction : code) { + + baos.write(instruction.getBytes()); + + } + return baos.toByteArray(); + } catch (IOException ex) { + Logger.getLogger(AVM2SourceGenerator.class.getName()).log(Level.SEVERE, null, ex); + } + return new byte[0]; + } + + @Override + public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + if (!("" + item.leftSide.returnType()).equals("Boolean")) { + ret.add(ins(new ConvertBIns())); + } + ret.add(ins(new DupIns())); + List orExpr = generateToActionList(localData, item.rightSide); + orExpr.add(0, ins(new PopIns())); + int orExprLen = insToBytes(orExpr).length; + ret.add(ins(new IfTrueIns(), orExprLen)); + ret.addAll(orExpr); + return ret; + } + + public List toInsList(List items) { + List ret = new ArrayList<>(); + for (GraphSourceItem s : items) { + if (s instanceof AVM2Instruction) { + ret.add((AVM2Instruction) s); + } + } + return ret; + } + + private List nonempty(List list) { + if (list == null) { + return new ArrayList<>(); + } + return list; + } + + private List condition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfTrueIns(), offset)); + } + + private List notCondition(SourceGeneratorLocalData localData, GraphTargetItem t, int offset) throws CompilationException { + if (t instanceof IfCondition) { + IfCondition ic = (IfCondition) t; + return GraphTargetItem.toSourceMerge(localData, this, ic.getLeftSide(), ic.getRightSide(), ins(ic.getIfNotDefinition(), offset)); + } + return GraphTargetItem.toSourceMerge(localData, this, t, ins(new IfFalseIns(), offset)); + } + + private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { + List ret = new ArrayList<>(); + //ret.addAll(notCondition(localData, expression)); + List onTrue = null; + List onFalse = null; + if (ternar) { + onTrue = toInsList(onTrueCmds.get(0).toSource(localData, this)); + } else { + onTrue = generateToInsList(localData, onTrueCmds); + } + + if (onFalseCmds != null && !onFalseCmds.isEmpty()) { + if (ternar) { + onFalse = toInsList(onFalseCmds.get(0).toSource(localData, this)); + } else { + onFalse = generateToInsList(localData, onFalseCmds); + } + } + AVM2Instruction ajmp = null; + if (onFalse != null) { + if (!((!nonempty(onTrue).isEmpty()) + && ((onTrue.get(onTrue.size() - 1).definition instanceof ContinueJumpIns) + || ((onTrue.get(onTrue.size() - 1).definition instanceof BreakJumpIns))))) { + ajmp = ins(new JumpIns(), 0); + onTrue.add(ajmp); + } + } + + byte[] onTrueBytes = insToBytes(onTrue); + int onTrueLen = onTrueBytes.length; + + ret.addAll(notCondition(localData, expression, onTrueLen)); + ret.addAll(onTrue); + + if (onFalse != null) { + byte[] onFalseBytes = insToBytes(onFalse); + int onFalseLen = onFalseBytes.length; + if (ajmp != null) { + ajmp.operands[0] = onFalseLen; + } + ret.addAll(onFalse); + } + return ret; + } + + public List generate(SourceGeneratorLocalData localData, XMLFilterAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + final Reference xmlListReg = new Reference<>(0); + List xmlListSetTemp = AssignableAVM2Item.setTemp(localData, this, xmlListReg); + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(new PushByteIns(), 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + item.object, + ins(new CheckFilterIns()), + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg), + ins(new GetLexIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId("XMLList", true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true)), + ins(new PushStringIns(), abc.constants.getStringId("", true)), + ins(new ConstructIns(), 1), + xmlListSetTemp + )); + final Reference tempVal1 = new Reference<>(0); + final Reference tempVal2 = new Reference<>(0); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(new LabelIns()), + AssignableAVM2Item.getTemp(localData, this, collectionReg), + AssignableAVM2Item.getTemp(localData, this, counterReg), + ins(new NextValueIns()), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal1), + AssignableAVM2Item.dupSetTemp(localData, this, tempVal2), + ins(new PushWithIns()) + )); + localData.scopeStack.add(new LocalRegAVM2Item(null, tempVal2.getVal(), null)); + forBody.addAll(toInsList(item.value.toSource(localData, this))); + List trueBody = new ArrayList<>(); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, xmlListReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, counterReg))); + trueBody.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempVal1))); + int nss[] = new int[item.openedNamespaces.size()]; + for (int i = 0; i < item.openedNamespaces.size(); i++) { + nss[i] = item.openedNamespaces.get(i); + } + trueBody.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true))); + forBody.add(ins(new IfFalseIns(), insToBytes(trueBody).length)); + forBody.addAll(trueBody); + forBody.add(ins(new PopScopeIns())); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + forBody.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempVal2, tempVal1)))); + + int forBodyLen = insToBytes(forBody).length; + AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(new IfTrueIns(), 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + ret.addAll(AssignableAVM2Item.getTemp(localData, this, xmlListReg)); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(xmlListReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { + return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); + } + + private void fixSwitch(List code, int breakOffset, long loopId) { + fixLoop(code, breakOffset, Integer.MAX_VALUE, loopId); + } + + private void fixLoop(List code, int breakOffset, int continueOffset, long loopId) { + int pos = 0; + for (int a = 0; a < code.size(); a++) { + AVM2Instruction ins = code.get(a); + pos += ins.getBytes().length; + if (ins.definition instanceof JumpIns) { + if (ins.definition instanceof ContinueJumpIns) { + if (continueOffset != Integer.MAX_VALUE) { + ins.operands[0] = (-pos + continueOffset); + code.get(a).definition = new JumpIns(); + } + } + if (ins.definition instanceof BreakJumpIns) { + ins.operands[0] = (-pos + breakOffset); + code.get(a).definition = new JumpIns(); + } + } + } + } + + @Override + public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { + List onTrue = new ArrayList<>(); + onTrue.add(item.onTrue); + List onFalse = new ArrayList<>(); + onFalse.add(item.onFalse); + return generateIf(localData, item.expression, onTrue, onFalse, true); + } + + @Override + public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List whileBody = generateToInsList(localData, item.commands); + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + whileBody.add(0, ins(new LabelIns())); + ret.addAll(whileBody); + int whileBodyLen = insToBytes(whileBody).length; + forwardJump.operands[0] = whileBodyLen; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int whileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(whileExprLen + whileBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(whileBody, whileBodyLen + whileExprLen, whileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ForEachInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, true); + } + + public List generate(SourceGeneratorLocalData localData, ForInAVM2Item item) throws CompilationException { + return generateForIn(localData, item.loop, item.expression.collection, (AssignableAVM2Item) item.expression.object, item.commands, false); + } + + public List generateForIn(SourceGeneratorLocalData localData, Loop loop, GraphTargetItem collection, AssignableAVM2Item assignable, List commands, final boolean each) throws CompilationException { + List ret = new ArrayList<>(); + final Reference counterReg = new Reference<>(0); + final Reference collectionReg = new Reference<>(0); + + if (assignable instanceof UnresolvedAVM2Item) { + assignable = (AssignableAVM2Item) ((UnresolvedAVM2Item) assignable).resolved; + } + + ret.addAll(GraphTargetItem.toSourceMerge(localData, this, + ins(new PushByteIns(), 0), + AssignableAVM2Item.setTemp(localData, this, counterReg), + collection, + NameAVM2Item.generateCoerce(localData, this, TypeItem.UNBOUNDED), + AssignableAVM2Item.setTemp(localData, this, collectionReg) + )); + + GraphTargetItem assigned = new GraphTargetItem() { + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return null; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, + AssignableAVM2Item.getTemp(localData, generator, collectionReg), + AssignableAVM2Item.getTemp(localData, generator, counterReg), + ins(each ? new NextValueIns() : new NextNameIns()) + ); + } + }; + assignable.setAssignedValue(assigned); + + List forBody = toInsList(GraphTargetItem.toSourceMerge(localData, this, + ins(new LabelIns()), + assignable.toSourceIgnoreReturnValue(localData, this) + )); + + forBody.addAll(generateToInsList(localData, commands)); + int forBodyLen = insToBytes(forBody).length; + + AVM2Instruction forwardJump = ins(new JumpIns(), forBodyLen); + ret.add(forwardJump); + + List expr = new ArrayList<>(); + expr.add(ins(new HasNext2Ins(), collectionReg.getVal(), counterReg.getVal())); + AVM2Instruction backIf = ins(new IfTrueIns(), 0); + expr.add(backIf); + + int exprLen = insToBytes(expr).length; + backIf.operands[0] = -(exprLen + forBodyLen); + + fixLoop(forBody, forBodyLen + exprLen, forBodyLen, loop.id); + ret.addAll(forBody); + ret.addAll(expr); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(collectionReg, counterReg))); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + whileExpr.addAll(generateToInsList(localData, ex)); + } + List dowhileBody = generateToInsList(localData, item.commands); + List labelBody = new ArrayList<>(); + labelBody.add(ins(new LabelIns())); + int labelBodyLen = insToBytes(labelBody).length; + + AVM2Instruction forwardJump = ins(new JumpIns(), labelBodyLen); + ret.add(forwardJump); + ret.addAll(labelBody); + ret.addAll(dowhileBody); + int dowhileBodyLen = insToBytes(dowhileBody).length; + whileExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int dowhileExprLen = insToBytes(whileExpr).length; + whileExpr.get(whileExpr.size() - 1).operands[0] = -(dowhileExprLen + dowhileBodyLen + labelBodyLen); //Assuming last is if instruction + ret.addAll(whileExpr); + fixLoop(dowhileBody, dowhileBodyLen + dowhileExprLen, dowhileBodyLen, item.loop.id); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, WithAVM2Item item) throws CompilationException { + + List ret = new ArrayList<>(); + ret.addAll(item.scope.toSource(localData, this)); + Reference tempReg = new Reference<>(0); + ret.addAll(AssignableAVM2Item.dupSetTemp(localData, this, tempReg)); + localData.scopeStack.add(new WithObjectAVM2Item(null, new LocalRegAVM2Item(null, tempReg.getVal(), null))); + ret.add(ins(new PushWithIns())); + ret.addAll(generate(localData, item.items)); + ret.add(ins(new PopScopeIns())); + ret.addAll(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { + List ret = new ArrayList<>(); + List forExpr = new ArrayList<>(); + + List ex = new ArrayList<>(); + ex.add(item.expression); + + GraphTargetItem lastItem = null; + if (!ex.isEmpty()) { + lastItem = ex.remove(ex.size() - 1); + while (lastItem instanceof CommaExpressionItem) { + CommaExpressionItem cei = (CommaExpressionItem) lastItem; + ex.addAll(cei.commands); + lastItem = ex.remove(ex.size() - 1); + } + forExpr.addAll(generateToInsList(localData, ex)); + } + List forBody = generateToInsList(localData, item.commands); + List forFinalCommands = generateToInsList(localData, item.finalCommands); + + ret.addAll(generateToInsList(localData, item.firstCommands)); + + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + forBody.add(0, ins(new LabelIns())); + ret.addAll(forBody); + ret.addAll(forFinalCommands); + int forBodyLen = insToBytes(forBody).length; + int forFinalCLen = insToBytes(forFinalCommands).length; + forwardJump.operands[0] = forBodyLen + forFinalCLen; + forExpr.addAll(toInsList(condition(localData, lastItem, 0))); + int forExprLen = insToBytes(forExpr).length; + forExpr.get(forExpr.size() - 1).operands[0] = -(forExprLen + forBodyLen + forFinalCLen); //Assuming last is if instruction + ret.addAll(forExpr); + fixLoop(forBody, forBodyLen + forFinalCLen + forExprLen, forBodyLen, item.loop.id); + return ret; + } + private long uniqLast = 0; + + public String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + @Override + public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { + List ret = new ArrayList<>(); + Reference switchedReg = new Reference<>(0); + AVM2Instruction forwardJump = ins(new JumpIns(), 0); + ret.add(forwardJump); + + List cases = new ArrayList<>(); + cases.addAll(toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); + int cLen = insToBytes(cases).length; + List caseLast = new ArrayList<>(); + caseLast.add(0, ins(new JumpIns(), cLen)); + caseLast.addAll(0, toInsList(new IntegerValueAVM2Item(null, (long) item.caseValues.size()).toSource(localData, this))); + int cLastLen = insToBytes(caseLast).length; + caseLast.add(0, ins(new JumpIns(), cLastLen)); + cases.addAll(0, caseLast); + + List preCases = new ArrayList<>(); + preCases.addAll(toInsList(item.switchedObject.toSource(localData, this))); + preCases.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, switchedReg))); + + for (int i = item.caseValues.size() - 1; i >= 0; i--) { + List sub = new ArrayList<>(); + sub.addAll(toInsList(new IntegerValueAVM2Item(null, (long) i).toSource(localData, this))); + sub.add(ins(new JumpIns(), insToBytes(cases).length)); + int subLen = insToBytes(sub).length; + + cases.addAll(0, sub); + cases.add(0, ins(new IfStrictNeIns(), subLen)); + cases.addAll(0, toInsList(AssignableAVM2Item.getTemp(localData, this, switchedReg))); + cases.addAll(0, toInsList(item.caseValues.get(i).toSource(localData, this))); + } + cases.addAll(0, preCases); + + AVM2Instruction lookupOp = new AVM2Instruction(0, new LookupSwitchIns(), new int[item.caseValues.size() + 1 + 1 + 1], new byte[0]); + cases.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(switchedReg)))); + List bodies = new ArrayList<>(); + List bodiesOffsets = new ArrayList<>(); + int defOffset; + int casesLen = insToBytes(cases).length; + bodies.addAll(generateToInsList(localData, item.defaultCommands)); + bodies.add(0, ins(new LabelIns())); + bodies.add(ins(new BreakJumpIns(item.loop.id), 0)); //There could be two breaks when default clause ends with break, but official compiler does this too, so who cares... + defOffset = -(insToBytes(bodies).length + casesLen); + for (int i = item.caseCommands.size() - 1; i >= 0; i--) { + bodies.addAll(0, generateToInsList(localData, item.caseCommands.get(i))); + bodies.add(0, ins(new LabelIns())); + bodiesOffsets.add(0, -(insToBytes(bodies).length + casesLen)); + } + lookupOp.operands[0] = defOffset; + lookupOp.operands[1] = item.valuesMapping.size(); + lookupOp.operands[2 + item.caseValues.size()] = defOffset; + for (int i = 0; i < item.valuesMapping.size(); i++) { + lookupOp.operands[2 + i] = bodiesOffsets.get(item.valuesMapping.get(i)); + } + + forwardJump.operands[0] = insToBytes(bodies).length; + ret.addAll(bodies); + ret.addAll(cases); + ret.add(lookupOp); + fixSwitch(toInsList(ret), insToBytes(toInsList(ret)).length, uniqLast); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { + /*if (item.getOriginal() instanceof Inverted) { + GraphTargetItem norig = ((Inverted) item).invert(); + return norig.toSource(localData, this); + }*/ + List ret = new ArrayList<>(); + ret.addAll(item.getOriginal().toSource(localData, this)); + ret.add(ins(new NotIns())); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { + List ret = new ArrayList<>(); + ret.add(ins(new DupIns())); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, BreakItem item) { + List ret = new ArrayList<>(); + AVM2Instruction abreak = ins(new BreakJumpIns(item.loopId), 0); + ret.add(abreak); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, FunctionAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + int scope = 0; + if (!item.functionName.isEmpty()) { + ret.add(ins(new NewObjectIns(), 0)); + ret.add(ins(new PushWithIns())); + scope = localData.scopeStack.size(); + localData.scopeStack.add(new PropertyAVM2Item(null, item.functionName, abc, allABCs, new ArrayList(), localData.callStack)); + } + ret.add(ins(new NewFunctionIns(), method(false, localData.callStack, localData.pkg, item.needsActivation, item.subvariables, 0 /*Set later*/, item.hasRest, item.line, null, null, false, localData, item.paramTypes, item.paramNames, item.paramValues, item.body, item.retType))); + if (!item.functionName.isEmpty()) { + ret.add(ins(new DupIns())); + ret.add(ins(new GetScopeObjectIns(), scope)); + ret.add(ins(new SwapIns())); + ret.add(ins(new SetPropertyIns(), abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(item.functionName, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(localData.pkg, true)), 0, true), 0, 0, new ArrayList()), true))); + ret.add(ins(new PopScopeIns())); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + } + return ret; + } + + private static int currentFinId = 1; + + private static int finId() { + return currentFinId++; + } + + public List generate(SourceGeneratorLocalData localData, TryAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + + boolean newFinallyReg = false; + List newex = new ArrayList<>(); + int aloneFinallyEx = -1; + int finallyEx = -1; + for (NameAVM2Item e : item.catchExceptions2) { + ABCException aex = new ABCException(); + aex.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(e.getVariableName(), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true), 0, 0, new ArrayList()), true); + aex.type_index = typeName(localData, e.type); + newex.add(aex); + } + int finId = 0; + if (item.finallyCommands != null) { + if (item.catchExceptions2.isEmpty()) { + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + aloneFinallyEx = newex.size() - 1; + } + ABCException aex = new ABCException(); + aex.name_index = 0; + aex.type_index = 0; + newex.add(aex); + finallyEx = newex.size() - 1; + if (localData.finallyRegister == -1) { + localData.finallyRegister = getFreeRegister(localData); + killRegister(localData, localData.finallyRegister); //reuse for catches + newFinallyReg = true; + } + finId = finId(); + } + + if (finallyEx > -1) { + localData.finallyCatches.add(finId); + } + List tryCmds = generateToInsList(localData, item.tryCommands); + + //int i = firstId + item.catchCommands.size() - 1; + List catches = new ArrayList<>(); + Reference tempReg = new Reference<>(0); + + List currentExceptionIds = new ArrayList<>(); + List> catchCmds = new ArrayList<>(); + for (int c = 0; c < item.catchCommands.size(); c++) { + int i = localData.exceptions.size(); + localData.exceptions.add(newex.get(c)); + + currentExceptionIds.add(i); + + //Reference tempReg=new Reference<>(0); + List catchCmd = new ArrayList<>(); + catchCmd.add(ins(new NewCatchIns(), i)); + catchCmd.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + catchCmd.add(ins(new DupIns())); + catchCmd.add(ins(new PushScopeIns())); + catchCmd.add(ins(new SwapIns())); + catchCmd.add(ins(new SetSlotIns(), 1)); + + for (AssignableAVM2Item a : item.catchVariables.get(c)) { + GraphTargetItem r = a; + if (r instanceof UnresolvedAVM2Item) { + r = ((UnresolvedAVM2Item) r).resolvedRoot; + } + if (r instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) r; + if (item.catchExceptions2.get(c).getVariableName().equals(n.getVariableName())) { + n.setSlotScope(localData.scopeStack.size()); + } + } + } + localData.scopeStack.add(new LocalRegAVM2Item(null, tempReg.getVal(), null)); + catchCmd.addAll(generateToInsList(localData, item.catchCommands.get(c))); + localData.scopeStack.remove(localData.scopeStack.size() - 1); + catchCmd.add(ins(new PopScopeIns())); + catchCmd.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catchCmds.add(catchCmd); + } + for (int c = item.catchCommands.size() - 1; c >= 0; c--) { + List preCatches = new ArrayList<>(); + /*preCatches.add(ins(new GetLocal0Ins())); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(AssignableAVM2Item.generateGetLoc(localData.activationReg)); + preCatches.add(ins(new PushScopeIns()));*/ + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + + //catchCmds.add(catchCmd); + preCatches.addAll(catchCmds.get(c)); + catches.addAll(0, preCatches); + catches.add(0, new ExceptionMarkAVM2Instruction(currentExceptionIds.get(c), MARK_E_TARGET)); + catches.add(0, ins(new JumpIns(), insToBytes(catches).length)); + } + + if (aloneFinallyEx > -1) { + localData.exceptions.add(newex.get(aloneFinallyEx)); + aloneFinallyEx = localData.exceptions.size() - 1; + + } + if (finallyEx > -1) { + localData.exceptions.add(newex.get(finallyEx)); + finallyEx = localData.exceptions.size() - 1; + } + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_START)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_START)); + } + if (finallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_START)); + } + + ret.addAll(tryCmds); + + for (int i : currentExceptionIds) { + ret.add(new ExceptionMarkAVM2Instruction(i, MARK_E_END)); + } + if (aloneFinallyEx > -1) { + ret.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_END)); + } + + if (aloneFinallyEx > -1) { + List preCatches = new ArrayList<>(); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + preCatches.add(ins(new NewCatchIns(), aloneFinallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(ins(new ThrowIns())); + preCatches.add(ins(new PopScopeIns())); + preCatches.addAll(toInsList(AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg)))); + catches.add(ins(new JumpIns(), insToBytes(preCatches).length)); + catches.add(new ExceptionMarkAVM2Instruction(aloneFinallyEx, MARK_E_TARGET)); + catches.addAll(preCatches); + } + AVM2Instruction finSwitch = null; + AVM2Instruction pushDefIns = ins(new PushByteIns(), 0); + + int defPos = 0; + if (finallyEx > -1) { + List preCatches = new ArrayList<>(); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_TARGET)); + for (GraphTargetItem s : localData.scopeStack) { + preCatches.addAll(toInsList(s.toSource(localData, this))); + if (s instanceof WithObjectAVM2Item) { + preCatches.add(ins(new PushWithIns())); + } else { + preCatches.add(ins(new PushScopeIns())); + } + } + preCatches.add(ins(new NewCatchIns(), finallyEx)); + preCatches.addAll(toInsList(AssignableAVM2Item.dupSetTemp(localData, this, tempReg))); + preCatches.add(ins(new PushScopeIns())); + preCatches.add(ins(new PopScopeIns())); + Reference tempReg2 = new Reference<>(0); + preCatches.add(ins(new KillIns(), tempReg.getVal())); + preCatches.add(ins(new CoerceAIns())); + preCatches.addAll(toInsList(AssignableAVM2Item.setTemp(localData, this, tempReg2))); + preCatches.add(pushDefIns); + + List finallySwitchCmds = new ArrayList<>(); + + finSwitch = new AVM2Instruction(0, new LookupSwitchIns(), new int[1 + 1 + 1], new byte[0]); + finSwitch.operands[0] = finSwitch.getBytes().length; + finSwitch.operands[1] = 0; //switch cnt + + List preFinallySwitch = new ArrayList<>(); + preFinallySwitch.add(ins(new LabelIns())); + preFinallySwitch.add(ins(new PopIns())); + int preFinallySwitchLen = insToBytes(preFinallySwitch).length; + + finallySwitchCmds.add(ins(new LabelIns())); + finallySwitchCmds.addAll(toInsList(AssignableAVM2Item.getTemp(localData, this, tempReg2))); + finallySwitchCmds.add(ins(new KillIns(), tempReg2.getVal())); + finallySwitchCmds.add(ins(new ThrowIns())); + finallySwitchCmds.add(ins(new PushByteIns(), 255)); + finallySwitchCmds.add(ins(new PopScopeIns())); + finallySwitchCmds.add(ins(new KillIns(), tempReg.getVal())); + + int finSwitchLen = insToBytes(finallySwitchCmds).length; + + preCatches.add(ins(new JumpIns(), preFinallySwitchLen + finSwitchLen)); + AVM2Instruction fjump = ins(new JumpIns(), 0); + fjump.operands[0] = insToBytes(preCatches).length + preFinallySwitchLen + finSwitchLen; + + preCatches.add(0, fjump); + preCatches.add(0, new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_END)); + preCatches.add(0, ins(new PushByteIns(), 255)); + + finallySwitchCmds.add(new ExceptionMarkAVM2Instruction(finallyEx, MARK_E_FINALLYPART)); + + int oldReg = localData.finallyRegister; + localData.finallyRegister = getFreeRegister(localData); + Integer cnt = localData.finallyCounter.get(finId); + if (cnt == null) { + cnt = -1; + } + defPos = cnt; + cnt++; //Skip default clause (throw) + localData.finallyCounter.put(finId, cnt); + finallySwitchCmds.addAll(generateToInsList(localData, item.finallyCommands)); + killRegister(localData, localData.finallyRegister); + localData.finallyRegister = oldReg; + finSwitchLen = insToBytes(finallySwitchCmds).length; + + finSwitch.operands[2] = -finSwitchLen; + preCatches.addAll(preFinallySwitch); + preCatches.addAll(finallySwitchCmds); + preCatches.add(finSwitch); + + catches.addAll(preCatches); + AssignableAVM2Item.killTemp(localData, this, Arrays.asList(tempReg, tempReg2)); + } + + ret.addAll(catches); + //localData.exceptions.addAll(newex); + + if (finallyEx > -1) { + localData.finallyCatches.remove(localData.finallyCatches.size() - 1); + } + if (newFinallyReg) { + localData.finallyRegister = -1; + killRegister(localData, localData.finallyRegister); + } + int pos = 0; + int finallyPos = 0; + int switchPos = 0; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src == finSwitch) { + switchPos = pos; + } + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction em = (ExceptionMarkAVM2Instruction) ins; + if (em.exceptionId == finallyEx && em.markType == MARK_E_FINALLYPART) { + finallyPos = pos; + ret.remove(s); + s--; + continue; + } + } + pos += ins.getBytes().length; + } + + } + + if (finSwitch != null) { + pos = 0; + int defLoc = finSwitch.operands[2]; + List switchLoc = new ArrayList<>(); + boolean wasDef = false; + for (int s = 0; s < ret.size(); s++) { + GraphSourceItem src = ret.get(s); + if (src instanceof AVM2Instruction) { + AVM2Instruction ins = (AVM2Instruction) src; + if (ins.definition instanceof FinallyJumpIns) { + FinallyJumpIns fji = (FinallyJumpIns) ins.definition; + if (fji.getClauseId() == finId) { + List bet = new ArrayList<>(); + bet.add(ins(new LabelIns())); + bet.add(ins(new PopIns())); + int betLen = insToBytes(bet).length; + if (wasDef) { + ins.operands[0] = 0; + } else { + ins.operands[0] = finallyPos - (pos + ins.getBytes().length); + } + ins.definition = new JumpIns(); + switchLoc.add(pos + ins.getBytes().length + betLen - switchPos); + } + } + pos += ins.getBytes().length; + } + if (defPos == switchLoc.size() - 1) { + switchLoc.add(defLoc); + wasDef = true; + } + } + finSwitch.operands = new int[1 + 1 + switchLoc.size()]; + pushDefIns.operands[0] = defPos + 1; + int afterLoc = finSwitch.getBytes().length; + finSwitch.operands[0] = afterLoc; + finSwitch.operands[1] = switchLoc.size() - 1; + for (int j = 0; j < switchLoc.size(); j++) { + finSwitch.operands[2 + j] = switchLoc.get(j); + } + } + + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ContinueItem item) { + List ret = new ArrayList<>(); + AVM2Instruction acontinue = ins(new ContinueJumpIns(item.loopId), 0); + ret.add(acontinue); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnValueAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + if (!localData.finallyCatches.isEmpty()) { + ret.add(ins(new CoerceAIns())); + ret.add(AssignableAVM2Item.generateSetLoc(localData.finallyRegister)); + for (int i = localData.finallyCatches.size() - 1; i >= 0; i--) { + if (i < localData.finallyCatches.size() - 1) { + ret.add(ins(new LabelIns())); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(new LabelIns())); + ret.add(ins(new PopIns())); + } + ret.add(ins(new LabelIns())); + ret.add(AssignableAVM2Item.generateGetLoc(localData.finallyRegister)); + ret.add(ins(new KillIns(), localData.finallyRegister)); + } + ret.add(ins(new ReturnValueIns())); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ReturnVoidAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + if (!localData.finallyCatches.isEmpty()) { + + for (int i = 0; i < localData.finallyCatches.size(); i++) { + if (i > 0) { + ret.add(ins(new LabelIns())); + } + int clauseId = localData.finallyCatches.get(i); + Integer cnt = localData.finallyCounter.get(clauseId); + if (cnt == null) { + cnt = -1; + } + cnt++; + localData.finallyCounter.put(clauseId, cnt); + ret.addAll(new IntegerValueAVM2Item(null, (long) cnt).toSource(localData, this)); + ret.add(ins(new FinallyJumpIns(clauseId), 0)); + ret.add(ins(new LabelIns())); + ret.add(ins(new PopIns())); + } + ret.add(ins(new LabelIns())); + } + ret.add(ins(new ReturnVoidIns())); + return ret; + } + + public List generate(SourceGeneratorLocalData localData, ThrowAVM2Item item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(item.value.toSource(localData, this)); + ret.add(ins(new ThrowIns())); + return ret; + } + + private List generateToInsList(SourceGeneratorLocalData localData, List commands) throws CompilationException { + return toInsList(generate(localData, commands)); + } + + private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { + return toInsList(command.toSource(localData, this)); + } + + @Override + public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { + List ret = new ArrayList<>(); + for (GraphTargetItem item : commands) { + ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); + } + return ret; + } + + public HashMap getRegisterVars(SourceGeneratorLocalData localData) { + return localData.registerVars; + } + + public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { + localData.registerVars = value; + } + + public void setInFunction(SourceGeneratorLocalData localData, int value) { + localData.inFunction = value; + } + + public int isInFunction(SourceGeneratorLocalData localData) { + return localData.inFunction; + } + + public boolean isInMethod(SourceGeneratorLocalData localData) { + return localData.inMethod; + } + + public void setInMethod(SourceGeneratorLocalData localData, boolean value) { + localData.inMethod = value; + } + + public int getForInLevel(SourceGeneratorLocalData localData) { + return localData.forInLevel; + } + + public void setForInLevel(SourceGeneratorLocalData localData, int value) { + localData.forInLevel = value; + } + + public int getTempRegister(SourceGeneratorLocalData localData) { + HashMap registerVars = getRegisterVars(localData); + int tmpReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + tmpReg = i; + break; + } + } + return tmpReg; + } + + public AVM2SourceGenerator(ABC abc, List allABCs) { + this.abc = abc; + this.allABCs = allABCs; + } + + public ABC getABC() { + return abc; + } + + public void generateClass(List importedClasses, List sinitVariables, boolean staticNeedsActivation, List staticInit, List openedNamespaces, int namespace, int initScope, String pkg, ClassInfo classInfo, InstanceInfo instanceInfo, SourceGeneratorLocalData localData, boolean isInterface, String name, String superName, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List traitItems) throws ParseException, CompilationException { + localData.currentClass = name; + localData.pkg = pkg; + List ret = new ArrayList<>(); + if (extendsVal == null && !isInterface) { + extendsVal = new TypeItem("Object"); + } + ParsedSymbol s = null; + + instanceInfo.name_index = traitName(namespace, name); + + Trait[] it = generateTraitsPhase1(name, superName, false, localData, traitItems, instanceInfo.instance_traits); + Trait[] st = generateTraitsPhase1(name, superName, true, localData, traitItems, classInfo.static_traits); + generateTraitsPhase2(importedClasses, pkg, traitItems, it, openedNamespaces, localData); + generateTraitsPhase2(importedClasses, pkg, traitItems, st, openedNamespaces, localData); + generateTraitsPhase3(initScope, isInterface, name, superName, false, localData, traitItems, instanceInfo.instance_traits, it, new HashMap()); + generateTraitsPhase3(initScope, isInterface, name, superName, true, localData, traitItems, classInfo.static_traits, st, new HashMap()); + int init = 0; + if (constructor == null || isInterface) { + instanceInfo.iinit_index = init = method(isInterface, new ArrayList(), pkg, false, new ArrayList(), initScope + 1, false, 0, isInterface ? null : name, extendsVal != null ? extendsVal.toString() : null, true, localData, new ArrayList(), new ArrayList(), new ArrayList(), new ArrayList(), TypeItem.UNBOUNDED/*?? FIXME*/); + } else { + MethodAVM2Item m = (MethodAVM2Item) constructor; + instanceInfo.iinit_index = init = method(false, new ArrayList(), pkg, m.needsActivation, m.subvariables, initScope + 1, m.hasRest, m.line, name, extendsVal != null ? extendsVal.toString() : null, true, localData, m.paramTypes, m.paramNames, m.paramValues, m.body, TypeItem.UNBOUNDED/*?? FIXME*/); + } + + //Class initializer + int staticMi = method(false, new ArrayList(), pkg, staticNeedsActivation, sinitVariables, initScope + (implementsStr.isEmpty() ? 0 : 1), false, 0, isInterface ? null : name, superName, false, localData, new ArrayList(), new ArrayList(), new ArrayList(), staticInit, TypeItem.UNBOUNDED); + MethodBody sinitBody = abc.findBody(staticMi); + + List sinitcode = new ArrayList<>(); + List initcode = new ArrayList<>(); + for (GraphTargetItem ti : traitItems) { + if ((ti instanceof SlotAVM2Item) || (ti instanceof ConstAVM2Item)) { + GraphTargetItem val = null; + boolean isStatic = false; + int ns = -1; + String tname = null; + boolean isConst = false; + if (ti instanceof SlotAVM2Item) { + val = ((SlotAVM2Item) ti).value; + isStatic = ((SlotAVM2Item) ti).isStatic(); + ns = ((SlotAVM2Item) ti).getNamespace(); + tname = ((SlotAVM2Item) ti).var; + } + if (ti instanceof ConstAVM2Item) { + val = ((ConstAVM2Item) ti).value; + isStatic = ((ConstAVM2Item) ti).isStatic(); + ns = ((ConstAVM2Item) ti).getNamespace(); + tname = ((ConstAVM2Item) ti).var; + isConst = true; + } + if (isStatic && val != null) { + sinitcode.add(ins(new FindPropertyIns(), traitName(ns, tname))); + sinitcode.addAll(toInsList(val.toSource(localData, this))); + sinitcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); + } + if (!isStatic && val != null) { + //do not init basic values, that can be stored in trait + if (!(val instanceof IntegerValueAVM2Item) && !(val instanceof StringAVM2Item) && !(val instanceof BooleanAVM2Item) && !(val instanceof NullAVM2Item) && !(val instanceof UndefinedAVM2Item)) { + initcode.add(ins(new GetLocal0Ins())); + initcode.addAll(toInsList(val.toSource(localData, this))); + initcode.add(ins(isConst ? new InitPropertyIns() : new SetPropertyIns(), traitName(ns, tname))); + } + } + } + } + MethodBody initBody = null; + if (!isInterface) { + initBody = abc.findBody(init); + initBody.code.code.addAll(constructor == null ? 0 : 2, initcode);//after getlocal0,pushscope + + if (sinitBody.code.code.get(sinitBody.code.code.size() - 1).definition instanceof ReturnVoidIns) { + sinitBody.code.code.addAll(2, sinitcode); //after getlocal0,pushscope + } + } + sinitBody.markOffsets(); + sinitBody.autoFillStats(abc, initScope + (implementsStr.isEmpty() ? 0 : 1), true); + + classInfo.cinit_index = staticMi; + if (!isInterface) { + initBody.autoFillStats(abc, initScope + 1, true); + } + instanceInfo.interfaces = new int[implementsStr.size()]; + for (int i = 0; i < implementsStr.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, implementsStr.get(i)); + } + } + + @Override + public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { + if (item.commands.isEmpty()) { + return new ArrayList<>(); + } + + //We need to handle commands and last expression separately, otherwise last expression result will be popped + List cmds = new ArrayList<>(item.commands); + GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); + List ret = new ArrayList<>(); + ret.addAll(generate(localData, cmds)); + ret.addAll(lastExpr.toSource(localData, this)); + return ret; + } + + public int generateClass(int namespace, ClassInfo ci, InstanceInfo ii, int initScope, String pkg, SourceGeneratorLocalData localData, AVM2Item cls) throws ParseException, CompilationException { + /*ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + abc.class_info.add(ci); + abc.instance_info.add(ii); + */ + if (cls instanceof ClassAVM2Item) { + ClassAVM2Item cai = (ClassAVM2Item) cls; + generateClass(cai.importedClasses, cai.sinitVariables, cai.staticInitActivation, cai.staticInit, cai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, false, cai.className, cai.extendsOp == null ? "Object" : cai.extendsOp.toString(), cai.extendsOp, cai.implementsOp, cai.constructor, cai.traits); + if (!cai.isDynamic) { + ii.flags |= InstanceInfo.CLASS_SEALED; + } + if (cai.isFinal) { + ii.flags |= InstanceInfo.CLASS_FINAL; + } + ii.flags |= InstanceInfo.CLASS_PROTECTEDNS; + ii.protectedNS = cai.protectedNs; + } + if (cls instanceof InterfaceAVM2Item) { + InterfaceAVM2Item iai = (InterfaceAVM2Item) cls; + ii.flags |= InstanceInfo.CLASS_INTERFACE; + ii.flags |= InstanceInfo.CLASS_SEALED; + generateClass(iai.importedClasses, new ArrayList(), false, new ArrayList(), iai.openedNamespaces, namespace, initScope, pkg, ci, ii, localData, true, iai.name, null, null, iai.superInterfaces, null, iai.methods); + } + + return abc.instance_info.size() - 1; + } + + public int traitName(int namespace, String var) { + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(var), namespace, 0, 0, new ArrayList()), true); + } + + public int typeName(SourceGeneratorLocalData localData, GraphTargetItem type) throws CompilationException { + if (type instanceof UnboundedTypeItem) { + return 0; + } + + return resolveType(localData, type, abc, allABCs); + /* + TypeItem nameItem = (TypeItem) type; + name = nameItem.fullTypeName; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + if (!nameItem.subtypes.isEmpty()) { //It's vector => TypeName + List params = new ArrayList<>(); + for (GraphTargetItem p : nameItem.subtypes) { + params.add(typeName(localData, p));//abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(p), namespace(Namespace.KIND_PACKAGE, ppkg), 0, 0, new ArrayList()), true)); + } + int qname = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, qname, params), true); + } else { + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(name), namespace(Namespace.KIND_PACKAGE, pkg), 0, 0, new ArrayList()), true); + }*/ + } + + public int ident(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + return str(((NameAVM2Item) name).getVariableName()); + } + throw new RuntimeException("no ident"); //FIXME + } + + public int namespace(int nsKind, String name) { + return abc.constants.getNamespaceId(new Namespace(nsKind, str(name)), 0, true); + } + + public int str(String name) { + return abc.constants.getStringId(name, true); + } + + public int propertyName(GraphTargetItem name) { + if (name instanceof NameAVM2Item) { + NameAVM2Item va = (NameAVM2Item) name; + return abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str(va.getVariableName()), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + } + throw new RuntimeException("no prop"); //FIXME + } + + public int getFreeRegister(SourceGeneratorLocalData localData) { + for (int i = 0;; i++) { + if (!localData.registerVars.containsValue(i)) { + localData.registerVars.put("__TEMP__" + i, i); + return i; + } + } + } + + public boolean killRegister(SourceGeneratorLocalData localData, int i) { + String key = null; + for (String k : localData.registerVars.keySet()) { + if (localData.registerVars.get(k) == i) { + key = k; + break; + } + } + if (key != null) { + localData.registerVars.remove(key); + return true; + } + return false; + } + + public int method(boolean isInterface, List callStack, String pkg, boolean needsActivation, List subvariables, int initScope, boolean hasRest, int line, String className, String superType, boolean constructor, SourceGeneratorLocalData localData, List paramTypes, List paramNames, List paramValues, List body, GraphTargetItem retType) throws CompilationException { + //Reference hasArgs = new Reference<>(Boolean.FALSE); + //calcRegisters(localData,needsActivation,paramNames,subvariables,body, hasArgs); + SourceGeneratorLocalData newlocalData = new SourceGeneratorLocalData(new HashMap(), 1, true, 0); + newlocalData.currentClass = className; + newlocalData.pkg = localData.pkg; + newlocalData.callStack.addAll(localData.callStack); + newlocalData.traitUsages = localData.traitUsages; + newlocalData.currentScript = localData.currentScript; + newlocalData.documentClass = localData.documentClass; + localData = newlocalData; + + localData.activationReg = 0; + + for (int i = 0; i < subvariables.size(); i++) { + AssignableAVM2Item an = subvariables.get(i); + if (an instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item n = (UnresolvedAVM2Item) an; + if (n.resolved == null) { + String fullClass = localData.getFullClass(); + GraphTargetItem res = n.resolve(new TypeItem(fullClass), paramTypes, paramNames, abc, allABCs, callStack, subvariables); + if (res instanceof AssignableAVM2Item) { + subvariables.set(i, (AssignableAVM2Item) res); + } else { + subvariables.remove(i); + i--; + } + } + } + } + + boolean hasArguments = false; + List slotNames = new ArrayList<>(); + List slotTypes = new ArrayList<>(); + slotNames.add("--first"); + slotTypes.add("-"); + + List registerNames = new ArrayList<>(); + List registerTypes = new ArrayList<>(); + if (className != null) { + String fullClassName = pkg == null || pkg.isEmpty() ? className : pkg + "." + className; + registerTypes.add(fullClassName); + localData.scopeStack.add(new LocalRegAVM2Item(null, registerNames.size(), null)); + registerNames.add("this"); + + } else { + registerTypes.add("global"); + registerNames.add("this"); + } + for (GraphTargetItem t : paramTypes) { + registerTypes.add(t.toString()); + slotTypes.add(t.toString()); + } + registerNames.addAll(paramNames); + slotNames.addAll(paramNames); + for (GraphTargetItem p : paramTypes) { + slotTypes.add("" + p); + } + if (hasRest) { + slotTypes.add("*"); + } + localData.registerVars.clear(); + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.getVariableName().equals("arguments") & !n.isDefinition()) { + registerNames.add("arguments"); + registerTypes.add("Object"); + hasArguments = true; + break; + } + } + } + int paramRegCount = registerNames.size(); + + if (needsActivation) { + registerNames.add("+$activation"); + localData.activationReg = registerNames.size() - 1; + registerTypes.add("Object"); + localData.scopeStack.add(new LocalRegAVM2Item(null, localData.activationReg, null)); + } + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.isDefinition()) { + if (!needsActivation || (n.getSlotScope() <= 0)) { + registerNames.add(n.getVariableName()); + registerTypes.add(n.type.toString()); + slotNames.add(n.getVariableName()); + slotTypes.add(n.type.toString()); + } + } + } + } + + int slotScope = className == null ? 0 : 1; + + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + if (n.getVariableName() != null) { + if (!n.getVariableName().equals("this") && needsActivation) { + if (n.getSlotNumber() <= 0) { + n.setSlotNumber(slotNames.indexOf(n.getVariableName())); + n.setSlotScope(slotScope); + } + } else { + n.setRegNumber(registerNames.indexOf(n.getVariableName())); + } + } + } + } + + for (int i = 0; i < registerNames.size(); i++) { + if (needsActivation && i > localData.activationReg) { + break; + } + localData.registerVars.put(registerNames.get(i), i); + } + List declarations = new ArrayList<>(); + loopn: + for (AssignableAVM2Item an : subvariables) { + if (an instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) an; + + if (needsActivation) { + if (n.getSlotScope() != slotScope) { + continue; + } else { + if (n.getSlotNumber() < paramRegCount) { + continue; + } + } + } + for (NameAVM2Item d : declarations) { + if (n.getVariableName() != null && n.getVariableName().equals(d.getVariableName())) { + continue loopn; + } + } + + for (GraphTargetItem it : body) { //search first level of commands + if (it instanceof NameAVM2Item) { + NameAVM2Item n2 = (NameAVM2Item) it; + if (n2.isDefinition() && n2.getAssignedValue() != null && n2.getVariableName().equals(n.getVariableName())) { + continue loopn; + } + if (!n2.isDefinition() && n2.getVariableName() != null && n2.getVariableName().equals(n.getVariableName())) { //used earlier than defined + break; + } + } + } + if (n.unresolved) { + continue; + } + if (n.redirect != null) { + continue; + } + if (n.getNs() != null) { + continue; + } + + if ("this".equals(n.getVariableName()) || paramNames.contains(n.getVariableName()) || "argmuments".equals(n.getVariableName())) { + continue; + } + + NameAVM2Item d = new NameAVM2Item(n.type, n.line, n.getVariableName(), NameAVM2Item.getDefaultValue("" + n.type), true, n.openedNamespaces); + //no index + if (needsActivation) { + if (d.getSlotNumber() <= 0) { + d.setSlotNumber(n.getSlotNumber()); + d.setSlotScope(n.getSlotScope()); + } + } else { + d.setRegNumber(n.getRegNumber()); + } + declarations.add(d); + } + } + + int param_types[] = new int[paramTypes.size()]; + ValueKind optional[] = new ValueKind[paramValues.size()]; + //int param_names[] = new int[paramNames.size()]; + for (int i = 0; i < paramTypes.size(); i++) { + param_types[i] = typeName(localData, paramTypes.get(i)); + //param_names[i] = str(paramNames.get(i)); + } + + for (int i = 0; i < paramValues.size(); i++) { + optional[i] = getValueKind(Namespace.KIND_NAMESPACE/*FIXME*/, paramTypes.get(paramTypes.size() - paramValues.size() + i), paramValues.get(i)); + if (optional[i] == null) { + throw new CompilationException("Default value must be compiletime constant", line); + } + } + + MethodInfo mi = new MethodInfo(param_types, constructor ? 0 : typeName(localData, retType), 0/*name_index*/, 0, optional, new int[0]/*no param_names*/); + if (hasArguments) { + mi.setFlagNeed_Arguments(); + } + //No param names like in official + /* + if (!paramNames.isEmpty()) { + mi.setFlagHas_paramnames(); + }*/ + if (!paramValues.isEmpty()) { + mi.setFlagHas_optional(); + } + if (hasRest) { + mi.setFlagNeed_rest(); + } + + int mindex; + if (!isInterface) { + MethodBody mbody = new MethodBody(); + + if (needsActivation) { + mbody.traits = new Traits(); + int slotId = 1; + for (int i = 1; i < slotNames.size(); i++) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.slot_id = slotId++; + tsc.name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(slotNames.get(i), true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + tsc.type_index = typeName(localData, new TypeItem(slotTypes.get(i))); + mbody.traits.traits.add(tsc); + } + for (int i = 1; i < paramRegCount; i++) { + NameAVM2Item param = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), null, false, new ArrayList()); + param.setRegNumber(i); + NameAVM2Item d = new NameAVM2Item(new TypeItem(registerTypes.get(i)), 0, registerNames.get(i), param, true, new ArrayList()); + d.setSlotScope(slotScope); + d.setSlotNumber(slotNames.indexOf(registerNames.get(i))); + declarations.add(d); + } + } + if (body != null) { + body.addAll(0, declarations); + } + + localData.exceptions = new ArrayList<>(); + localData.callStack.add(mbody); + List src = body == null ? new ArrayList() : generate(localData, body); + + mbody.method_info = abc.addMethodInfo(mi); + mi.setBody(mbody); + mbody.code = new AVM2Code(); + mbody.code.code = toInsList(src); + + if (needsActivation) { + if (localData.traitUsages.containsKey(mbody)) { + List usages = localData.traitUsages.get(mbody); + for (int i = 0; i < mbody.traits.traits.size(); i++) { + if (usages.contains(i)) { + TraitSlotConst tsc = (TraitSlotConst) mbody.traits.traits.get(i); + GraphTargetItem type = TypeItem.UNBOUNDED; + if (tsc.type_index > 0) { + type = new TypeItem(abc.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(abc.constants)); + } + NameAVM2Item d = new NameAVM2Item(type, 0, tsc.getName(abc).getName(abc.constants, new ArrayList()), NameAVM2Item.getDefaultValue("" + type), true, new ArrayList()); + d.setSlotNumber(tsc.slot_id); + d.setSlotScope(slotScope); + mbody.code.code.addAll(0, toInsList(d.toSourceIgnoreReturnValue(localData, this))); + } + } + } + + List acts = new ArrayList<>(); + acts.add(ins(new NewActivationIns())); + acts.add(ins(new DupIns())); + acts.add(AssignableAVM2Item.generateSetLoc(localData.activationReg)); + acts.add(ins(new PushScopeIns())); + + mbody.code.code.addAll(0, acts); + } + + if (constructor) { + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + + int parentConstMinAC = 0; + + for (ABC a : abcs) { + int ci = a.findClassByName(superType); + if (ci > -1) { + MethodInfo pmi = a.method_info.get(a.instance_info.get(ci).iinit_index); + parentConstMinAC = pmi.param_types.length; + if (pmi.flagHas_optional()) { + parentConstMinAC -= pmi.optional.length; + } + + } + } + int ac = -1; + for (AVM2Instruction ins : mbody.code.code) { + if (ins.definition instanceof ConstructSuperIns) { + ac = ins.operands[0]; + if (parentConstMinAC > ac) { + throw new CompilationException("Parent constructor call requires different number of arguments", line); + } + + } + } + if (ac == -1) { + if (parentConstMinAC == 0) { + mbody.code.code.add(0, new AVM2Instruction(0, new GetLocal0Ins(), new int[]{}, new byte[0])); + mbody.code.code.add(1, new AVM2Instruction(0, new ConstructSuperIns(), new int[]{0}, new byte[0])); + + } else { + throw new CompilationException("Parent constructor must be called", line); + } + } + } + if (className != null) {//It's method, not (inner) function + mbody.code.code.add(0, new AVM2Instruction(0, new GetLocal0Ins(), new int[]{}, new byte[0])); + mbody.code.code.add(1, new AVM2Instruction(0, new PushScopeIns(), new int[]{}, new byte[0])); + } + boolean addRet = false; + if (!mbody.code.code.isEmpty()) { + InstructionDefinition lastDef = mbody.code.code.get(mbody.code.code.size() - 1).definition; + if (!((lastDef instanceof ReturnVoidIns) || (lastDef instanceof ReturnValueIns))) { + addRet = true; + } + } else { + addRet = true; + } + if (addRet) { + if (retType.toString().equals("*") || retType.toString().equals("void") || constructor) { + mbody.code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[]{}, new byte[0])); + } else { + mbody.code.code.add(new AVM2Instruction(0, new PushUndefinedIns(), new int[]{}, new byte[0])); + mbody.code.code.add(new AVM2Instruction(0, new ReturnValueIns(), new int[]{}, new byte[0])); + } + } + mbody.exceptions = localData.exceptions.toArray(new ABCException[localData.exceptions.size()]); + int offset = 0; + for (int i = 0; i < mbody.code.code.size(); i++) { + AVM2Instruction ins = mbody.code.code.get(i); + if (ins instanceof ExceptionMarkAVM2Instruction) { + ExceptionMarkAVM2Instruction m = (ExceptionMarkAVM2Instruction) ins; + switch (m.markType) { + case MARK_E_START: + mbody.exceptions[m.exceptionId].start = offset; + break; + case MARK_E_END: + mbody.exceptions[m.exceptionId].end = offset; + break; + case MARK_E_TARGET: + mbody.exceptions[m.exceptionId].target = offset; + break; + } + mbody.code.code.remove(i); + i--; + continue; + } + offset += ins.getBytes().length; + } + + mbody.markOffsets(); + mbody.autoFillStats(abc, initScope, className != null); + abc.addMethodBody(mbody); + mindex = mbody.method_info; + } else { + mindex = abc.addMethodInfo(mi); + } + + return mindex; + } + + public ValueKind getValueKind(int ns, GraphTargetItem type, GraphTargetItem val) { + + if (val instanceof BooleanAVM2Item) { + BooleanAVM2Item bi = (BooleanAVM2Item) val; + if (bi.value) { + return new ValueKind(0, ValueKind.CONSTANT_True); + } else { + return new ValueKind(0, ValueKind.CONSTANT_False); + } + } + + boolean isNs = false; + if (type instanceof NameAVM2Item) { + if (((NameAVM2Item) type).getVariableName().equals("namespace")) { + isNs = true; + } + } + + if ((type instanceof TypeItem) && (((TypeItem) type).fullTypeName.equals("Namespace"))) { + isNs = true; + } + + if (val instanceof StringAVM2Item) { + StringAVM2Item sval = (StringAVM2Item) val; + if (isNs) { + return new ValueKind(namespace(abc.constants.constant_namespace.get(ns).kind, sval.value), Namespace.KIND_NAMESPACE); + } else { + return new ValueKind(str(sval.value), ValueKind.CONSTANT_Utf8); + } + } + if (val instanceof IntegerValueAVM2Item) { + return new ValueKind(abc.constants.getIntId(((IntegerValueAVM2Item) val).value, true), ValueKind.CONSTANT_Int); + } + if (val instanceof FloatValueAVM2Item) { + return new ValueKind(abc.constants.getDoubleId(((FloatValueAVM2Item) val).value, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NanAVM2Item) { + return new ValueKind(abc.constants.getDoubleId(Double.NaN, true), ValueKind.CONSTANT_Double); + } + if (val instanceof NullAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Null); + } + if (val instanceof UndefinedAVM2Item) { + return new ValueKind(0, ValueKind.CONSTANT_Undefined); + } + return null; + } + + private int genNs(List importedClasses, String pkg, String custom, int namespace, List openedNamespaces, SourceGeneratorLocalData localData, int line) throws CompilationException { + if (custom != null) { + PropertyAVM2Item prop = new PropertyAVM2Item(null, custom, abc, allABCs, openedNamespaces, new ArrayList()); + Reference value = new Reference<>(null); + prop.resolve(localData, new Reference(null), new Reference(null), new Reference(0), value); + boolean resolved = true; + if (value.getVal() == null) { + resolved = false; + } + if (!resolved) { + + String customPkg = ""; + String fullCustom = ""; + for (String imp : importedClasses) { + if (imp.endsWith("." + custom)) { + customPkg = imp.substring(0, imp.lastIndexOf('.')); + fullCustom = imp; + break; + } + } + + List aas = new ArrayList<>(); + aas.add(abc); + aas.addAll(allABCs); + for (ABC a : aas) { + for (ScriptInfo si : a.script_info) { + for (Trait t : si.traits.traits) { + Multiname m = t.getName(a); + if (fullCustom.equals(m.getNameWithNamespace(a.constants))) { + if (t instanceof TraitSlotConst) { + if (((TraitSlotConst) t).isNamespace()) { + return ((TraitSlotConst) t).value_index; + } + } + } + } + } + } + + throw new CompilationException("Namespace not defined", line); + } + namespace = value.getVal().value_index; + } + return namespace; + } + + public void generateTraitsPhase2(List importedClasses, String pkg, List items, Trait[] traits, List openedNamespaces, SourceGeneratorLocalData localData) throws CompilationException { + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } else if (item instanceof InterfaceAVM2Item) { + traits[k].name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + } else if (item instanceof ClassAVM2Item) { + traits[k].name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + } else if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((MethodAVM2Item) item).customNamespace, ((MethodAVM2Item) item).namespace, openedNamespaces, localData, ((MethodAVM2Item) item).line), ((MethodAVM2Item) item).functionName); + } else if (item instanceof FunctionAVM2Item) { + traits[k].name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); + } else if (item instanceof ConstAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((ConstAVM2Item) item).customNamespace, ((ConstAVM2Item) item).getNamespace(), openedNamespaces, localData, ((ConstAVM2Item) item).line), ((ConstAVM2Item) item).var); + } else if (item instanceof SlotAVM2Item) { + traits[k].name_index = traitName(genNs(importedClasses, pkg, ((SlotAVM2Item) item).customNamespace, ((SlotAVM2Item) item).getNamespace(), openedNamespaces, localData, ((SlotAVM2Item) item).line), ((SlotAVM2Item) item).var); + } + } + + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof ClassAVM2Item) { + InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList())); + + if (((ClassAVM2Item) item).extendsOp != null) { + instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); + } else { + instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + } + instanceInfo.interfaces = new int[((ClassAVM2Item) item).implementsOp.size()]; + for (int i = 0; i < ((ClassAVM2Item) item).implementsOp.size(); i++) { + instanceInfo.interfaces[i] = superIntName(localData, ((ClassAVM2Item) item).implementsOp.get(i)); + } + } + if (item instanceof InterfaceAVM2Item) { + InstanceInfo instanceInfo = abc.instance_info.get(((TraitClass) traits[k]).class_info); + instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((InterfaceAVM2Item) item).name, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList())); + + instanceInfo.interfaces = new int[((InterfaceAVM2Item) item).superInterfaces.size()]; + for (int i = 0; i < ((InterfaceAVM2Item) item).superInterfaces.size(); i++) { + GraphTargetItem un = ((InterfaceAVM2Item) item).superInterfaces.get(i); + instanceInfo.interfaces[i] = superIntName(localData, un); + } + } + } + } + + public int superIntName(SourceGeneratorLocalData localData, GraphTargetItem un) throws CompilationException { + if (un instanceof UnresolvedAVM2Item) { + ((UnresolvedAVM2Item) un).resolve(null, new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + un = ((UnresolvedAVM2Item) un).resolved; + } + if (!(un instanceof TypeItem)) { //not applyType + throw new CompilationException("Invalid type", 0); + } + TypeItem sup = (TypeItem) un; + int propId = resolveType(localData, sup, abc, allABCs); + int nss[] = new int[]{abc.constants.constant_multiname.get(propId).namespace_index}; + return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(propId).name_index, 0, abc.constants.getNamespaceSetId(new NamespaceSet(nss), true), 0, new ArrayList()), true); + + } + + public void generateTraitsPhase3(int methodInitScope, boolean isInterface, String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts, Trait[] traits, Map initScopes) throws ParseException, CompilationException { + //Note: Names must be generated first before accesed in inner subs + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (traits[k] == null) { + continue; + } + if (item instanceof InterfaceAVM2Item) { + generateClass(((InterfaceAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((InterfaceAVM2Item) item).pkg, localData, (InterfaceAVM2Item) item); + } + + if (item instanceof ClassAVM2Item) { + generateClass(((ClassAVM2Item) item).namespace, abc.class_info.get(((TraitClass) traits[k]).class_info), abc.instance_info.get(((TraitClass) traits[k]).class_info), initScopes.get(traits[k]), ((ClassAVM2Item) item).pkg, localData, (ClassAVM2Item) item); + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + ((TraitMethodGetterSetter) traits[k]).method_info = method(isInterface, new ArrayList(), mai.pkg, mai.needsActivation, mai.subvariables, methodInitScope + (mai.isStatic() ? 0 : 1), mai.hasRest, mai.line, className, superName, false, localData, mai.paramTypes, mai.paramNames, mai.paramValues, mai.body, mai.retType); + } else if (item instanceof FunctionAVM2Item) { + FunctionAVM2Item fai = (FunctionAVM2Item) item; + ((TraitFunction) traits[k]).method_info = method(isInterface, new ArrayList(), fai.pkg, fai.needsActivation, fai.subvariables, methodInitScope, fai.hasRest, fai.line, className, superName, false, localData, fai.paramTypes, fai.paramNames, fai.paramValues, fai.body, fai.retType); + } + } + } + + public Trait[] generateTraitsPhase1(String className, String superName, boolean generateStatic, SourceGeneratorLocalData localData, List items, Traits ts) throws ParseException, CompilationException { + Trait[] traits = new Trait[items.size()]; + int slot_id = 1; + int disp_id = 3; //1 and 2 are for constructor + for (int k = 0; k < items.size(); k++) { + GraphTargetItem item = items.get(k); + if (item instanceof InterfaceAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo ii = new InstanceInfo(); + abc.class_info.add(ci); + abc.instance_info.add(ii); + ii.flags |= InstanceInfo.CLASS_INTERFACE; + tc.class_info = abc.instance_info.size() - 1; + tc.kindType = Trait.TRAIT_CLASS; + //tc.name_index = traitName(((InterfaceAVM2Item) item).namespace, ((InterfaceAVM2Item) item).name); + tc.slot_id = 0; //? + ts.traits.add(tc); + traits[k] = tc; + } + + if (item instanceof ClassAVM2Item) { + TraitClass tc = new TraitClass(); + ClassInfo ci = new ClassInfo(); + InstanceInfo instanceInfo = new InstanceInfo(); + abc.class_info.add(ci); + abc.instance_info.add(instanceInfo); + tc.class_info = abc.instance_info.size() - 1; + + /*instanceInfo.name_index = abc.constants.addMultiname(new Multiname(Multiname.QNAME, abc.constants.getStringId(((ClassAVM2Item) item).className, true), + abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg.packageName, true)), 0, true), 0, 0, new ArrayList())); + */ + + /*if (((ClassAVM2Item) item).extendsOp != null) { + instanceInfo.super_index = typeName(localData, ((ClassAVM2Item) item).extendsOp); + } else { + instanceInfo.super_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, str("Object"), namespace(Namespace.KIND_PACKAGE, ""), 0, 0, new ArrayList()), true); + }*/ + tc.kindType = Trait.TRAIT_CLASS; + // tc.name_index = traitName(((ClassAVM2Item) item).namespace, ((ClassAVM2Item) item).className); + tc.slot_id = slot_id++; + ts.traits.add(tc); + traits[k] = tc; + + } + if ((item instanceof SlotAVM2Item) || (item instanceof ConstAVM2Item)) { + TraitSlotConst tsc = new TraitSlotConst(); + tsc.kindType = (item instanceof SlotAVM2Item) ? Trait.TRAIT_SLOT : Trait.TRAIT_CONST; + String var = null; + GraphTargetItem val = null; + GraphTargetItem type = null; + boolean isNamespace = false; + int namespace = 0; + boolean isStatic = false; + if (item instanceof SlotAVM2Item) { + SlotAVM2Item sai = (SlotAVM2Item) item; + if (sai.isStatic() != generateStatic) { + continue; + } + var = sai.var; + val = sai.value; + type = sai.type; + isStatic = sai.isStatic(); + namespace = sai.getNamespace(); + } + if (item instanceof ConstAVM2Item) { + ConstAVM2Item cai = (ConstAVM2Item) item; + if (cai.isStatic() != generateStatic) { + continue; + } + var = cai.var; + val = cai.value; + type = cai.type; + namespace = cai.getNamespace(); + isNamespace = type.toString().equals("Namespace"); + isStatic = cai.isStatic(); + } + if (isNamespace) { + tsc.name_index = traitName(namespace, var); + } + tsc.type_index = isNamespace ? 0 : (type == null ? 0 : typeName(localData, type)); + + ValueKind vk = getValueKind(namespace, type, val); + if (vk == null) { + tsc.value_kind = ValueKind.CONSTANT_Undefined; + } else { + tsc.value_kind = vk.value_kind; + tsc.value_index = vk.value_index; + } + tsc.slot_id = isStatic ? slot_id++ : 0; + ts.traits.add(tsc); + traits[k] = tsc; + } + if ((item instanceof MethodAVM2Item) || (item instanceof GetterAVM2Item) || (item instanceof SetterAVM2Item)) { + MethodAVM2Item mai = (MethodAVM2Item) item; + if (mai.isStatic() != generateStatic) { + continue; + } + TraitMethodGetterSetter tmgs = new TraitMethodGetterSetter(); + tmgs.kindType = (item instanceof MethodAVM2Item) ? Trait.TRAIT_METHOD : ((item instanceof GetterAVM2Item) ? Trait.TRAIT_GETTER : Trait.TRAIT_SETTER); + //tmgs.name_index = traitName(((MethodAVM2Item) item).namespace, ((MethodAVM2Item) item).functionName); + tmgs.disp_id = mai.isStatic() ? disp_id++ : 0; //For a reason, there is disp_id only for static methods (or not?) + if (mai.isFinal() || mai.isStatic()) { + tmgs.kindFlags |= Trait.ATTR_Final; + } + if (mai.isOverride()) { + tmgs.kindFlags |= Trait.ATTR_Override; + } + ts.traits.add(tmgs); + + traits[k] = tmgs; + } else if (item instanceof FunctionAVM2Item) { + TraitFunction tf = new TraitFunction(); + tf.slot_id = slot_id++; + tf.kindType = Trait.TRAIT_FUNCTION; + //tf.name_index = traitName(((FunctionAVM2Item) item).namespace, ((FunctionAVM2Item) item).functionName); + ts.traits.add(tf); + traits[k] = tf; + } + } + + return traits; + } + + public ScriptInfo generateScriptInfo(SourceGeneratorLocalData localData, List commands) throws ParseException, CompilationException { + ScriptInfo si = new ScriptInfo(); + localData.currentScript = si; + Trait[] traitArr = generateTraitsPhase1(null, null, false, localData, commands, si.traits); + generateTraitsPhase2(new ArrayList(), null/*FIXME*/, commands, traitArr, new ArrayList(), localData); + MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]); + MethodBody mb = new MethodBody(); + mb.method_info = abc.addMethodInfo(mi); + mb.code = new AVM2Code(); + mb.code.code.add(ins(new GetLocal0Ins())); + mb.code.code.add(ins(new PushScopeIns())); + + int traitScope = 2; + + Map initScopes = new HashMap<>(); + + for (Trait t : si.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + List parents = new ArrayList<>(); + if (localData.documentClass) { + mb.code.code.add(ins(new GetScopeObjectIns(), 0)); + traitScope++; + } else { + NamespaceSet nsset = new NamespaceSet(new int[]{abc.constants.constant_multiname.get(tc.name_index).namespace_index}); + mb.code.code.add(ins(new FindPropertyStrictIns(), abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, abc.constants.constant_multiname.get(tc.name_index).name_index, 0, abc.constants.getNamespaceSetId(nsset, true), 0, new ArrayList()), true))); + } + if (abc.instance_info.get(tc.class_info).isInterface()) { + mb.code.code.add(ins(new PushNullIns())); + } else { + + parentNamesAddNames(abc, allABCs, abc.instance_info.get(tc.class_info).name_index, parents, new ArrayList(), new ArrayList()); + for (int i = parents.size() - 1; i >= 1; i--) { + mb.code.code.add(ins(new GetLexIns(), parents.get(i))); + mb.code.code.add(ins(new PushScopeIns())); + traitScope++; + } + mb.code.code.add(ins(new GetLexIns(), parents.get(1))); + } + mb.code.code.add(ins(new NewClassIns(), tc.class_info)); + if (!abc.instance_info.get(tc.class_info).isInterface()) { + for (int i = parents.size() - 1; i >= 1; i--) { + mb.code.code.add(ins(new PopScopeIns())); + } + } + mb.code.code.add(ins(new InitPropertyIns(), tc.name_index)); + initScopes.put(t, traitScope); + traitScope = 1; + } + } + + mb.code.code.add(ins(new ReturnVoidIns())); + mb.autoFillStats(abc, 1, false); + abc.addMethodBody(mb); + si.init_index = mb.method_info; + localData.pkg = null; //FIXME: pkg.packageName; + generateTraitsPhase3(1/*??*/, false, null, null, false, localData, commands, si.traits, traitArr, initScopes); + return si; + } + + public static void parentNamesAddNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces) { + List cindices = new ArrayList<>(); + + List outABCs = new ArrayList<>(); + parentNames(abc, allABCs, name_index, cindices, names, namespaces, outABCs); + for (int i = 0; i < cindices.size(); i++) { + ABC a = outABCs.get(i); + int m = cindices.get(i); + if (a == abc) { + indices.add(m); + continue; + } + Multiname superName = a.constants.constant_multiname.get(m); + indices.add( + abc.constants.getMultinameId( + new Multiname(Multiname.QNAME, + abc.constants.getStringId(superName.getName(a.constants, new ArrayList()), true), + abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true) + ); + } + } + + public static GraphTargetItem getTraitReturnType(ABC abc, Trait t) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + if (tsc.type_index == 0) { + return TypeItem.UNBOUNDED; + } + return PropertyAVM2Item.multinameToType(tsc.type_index, abc.constants); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.kindType == Trait.TRAIT_GETTER) { + return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).ret_type, abc.constants); + } + if (tmgs.kindType == Trait.TRAIT_SETTER) { + if (abc.method_info.get(tmgs.method_info).param_types.length > 0) { + return PropertyAVM2Item.multinameToType(abc.method_info.get(tmgs.method_info).param_types[0], abc.constants); + } else { + return TypeItem.UNBOUNDED; + } + } + } + if (t instanceof TraitFunction) { + return new TypeItem("Function"); + } + return TypeItem.UNBOUNDED; + } + + private static boolean eq(Object a, Object b) { + if (a == null && b == null) { + return true; + } + if (a == null) { + return false; + } + if (b == null) { + return false; + } + return a.equals(b); + } + + public static boolean searchPrototypeChain(boolean instanceOnly, List abcs, String pkg, String obj, String propertyName, Reference outName, Reference outNs, Reference outPropNs, Reference outPropNsKind, Reference outPropNsIndex, Reference outPropType, Reference outPropValue) { + + for (ABC abc : abcs) { + if (!instanceOnly) { + for (ScriptInfo ii : abc.script_info) { + if (ii.deleted) { + continue; + } + for (Trait t : ii.traits.traits) { + if (eq(pkg, t.getName(abc).getNamespace(abc.constants).getName(abc.constants))) { + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + } + } + } + for (int i = 0; i < abc.instance_info.size(); i++) { + InstanceInfo ii = abc.instance_info.get(i); + if (ii.deleted) { + continue; + } + Multiname clsName = ii.getName(abc.constants); + if (obj.equals(clsName.getName(abc.constants, new ArrayList()))) { + if (eq(pkg, clsName.getNamespace(abc.constants).getName(abc.constants))) { + //class found + + for (Trait t : ii.instance_traits.traits) { + if (t.getName(abc) == null) { //in traits phase 2 + continue; + } + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + + if (!instanceOnly) { + for (Trait t : abc.class_info.get(i).static_traits.traits) { + if (t.getName(abc) == null) { //in traits phase 2 + continue; + } + if (propertyName.equals(t.getName(abc).getName(abc.constants, new ArrayList()))) { + outName.setVal(obj); + outNs.setVal(pkg); + outPropNs.setVal(t.getName(abc).getNamespace(abc.constants).getName(abc.constants)); + outPropNsKind.setVal(t.getName(abc).getNamespace(abc.constants).kind); + outPropNsIndex.setVal(abc.constants.getNamespaceSubIndex(t.getName(abc).namespace_index)); + outPropType.setVal(getTraitReturnType(abc, t)); + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + outPropValue.setVal(new ValueKind(tsc.value_index, tsc.value_kind)); + } + return true; + } + } + } + + Multiname superName = abc.constants.constant_multiname.get(ii.super_index); + if (superName != null) { + return searchPrototypeChain(instanceOnly, abcs, superName.getNamespace(abc.constants).getName(abc.constants), superName.getName(abc.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue); + } else { + return false; + } + } + } + } + } + return false; + } + + public static void parentNames(ABC abc, List allABCs, int name_index, List indices, List names, List namespaces, List outABCs) { + indices.add(name_index); + names.add(abc.constants.constant_multiname.get(name_index).getName(abc.constants, new ArrayList())); + namespaces.add(abc.constants.constant_multiname.get(name_index).getNamespace(abc.constants).getName(abc.constants)); + Multiname mname = abc.constants.constant_multiname.get(name_index); + + outABCs.add(abc); + + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + + for (ABC a : abcs) { + for (int i = 0; i < a.instance_info.size(); i++) { + Multiname m = a.constants.constant_multiname.get(a.instance_info.get(i).name_index); + if (m.getName(a.constants, new ArrayList()).equals(mname.getName(abc.constants, new ArrayList()))) { + + if (m.getNamespace(a.constants).hasName(a.constants, mname.getNamespace(abc.constants).getName(abc.constants))) { + //Multiname superName = a.constants.constant_multiname.get(a.instance_info.get(i).super_index); + abcs.remove(a); + if (a.instance_info.get(i).super_index != 0) { + parentNames(a, abcs, a.instance_info.get(i).super_index, indices, names, namespaces, outABCs); + } + /*parentNames(abc,allABCs,abc.constants.getMultinameId( + new Multiname(superName.kind, + abc.constants.getStringId(superName.getName(a.constants, new ArrayList()),true), + abc.constants.getNamespaceId(new Namespace(superName.getNamespace(a.constants).kind, abc.constants.getStringId(superName.getNamespace(a.constants).getName(a.constants),true)),0,true), 0, 0, new ArrayList()), true),indices,names,namespaces,outABCs);*/ + return; + } + } + } + } + } + + /* public void calcRegisters(Reference activationReg, SourceGeneratorLocalData localData, boolean needsActivation, List funParamNames,List funSubVariables,List funBody, Reference hasArguments) throws ParseException { + + }*/ + /*public int resolveType(String objType) { + if (objType.equals("*")) { + return 0; + } + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(allABCs); + for (ABC a : abcs) { + int ci = a.findClassByName(objType); + if (ci != -1) { + Multiname tname = a.instance_info.get(ci).getName(a.constants); + return abc.constants.getMultinameId(new Multiname(tname.kind, + abc.constants.getStringId(tname.getName(a.constants, new ArrayList()), true), + abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + return 0; + }*/ + @Override + public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { + String currentFullClassName = localData.getFullClass(); + + if (localData.documentClass && item.toString().equals(currentFullClassName)) { + int slotId = 0; + int c = abc.findClassByName(currentFullClassName); + for (Trait t : localData.currentScript.traits.traits) { + if (t instanceof TraitClass) { + TraitClass tc = (TraitClass) t; + if (tc.class_info == c) { + slotId = tc.slot_id; + break; + } + } + } + return GraphTargetItem.toSourceMerge(localData, this, ins(new GetGlobalScopeIns()), ins(new GetSlotIns(), slotId)); + } else { + return GraphTargetItem.toSourceMerge(localData, this, ins(new GetLexIns(), resolveType(localData, item, abc, allABCs))); + } + } + + public static int resolveType(SourceGeneratorLocalData localData, GraphTargetItem item, ABC abc, List allABCs) throws CompilationException { + int name_index = 0; + GraphTargetItem typeItem = null; + + if (item instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + item = ((UnresolvedAVM2Item) item).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + } + if (item instanceof TypeItem) { + typeItem = item; + } else if (item instanceof ApplyTypeAVM2Item) { + typeItem = ((ApplyTypeAVM2Item) item).object; + } else { + throw new CompilationException("Invalid type:" + item.getClass().getName(), 0/*??*/); + } + if (typeItem instanceof UnresolvedAVM2Item) { + String fullClass = localData.getFullClass(); + typeItem = ((UnresolvedAVM2Item) typeItem).resolve(new TypeItem(fullClass), new ArrayList(), new ArrayList(), abc, allABCs, new ArrayList(), new ArrayList()); + } + + if (!(typeItem instanceof TypeItem)) { + throw new CompilationException("Invalid type", 0/*??*/); + } + + TypeItem type = (TypeItem) typeItem; + + String name = type.fullTypeName; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + for (InstanceInfo ii : abc.instance_info) { + Multiname mname = abc.constants.constant_multiname.get(ii.name_index); + if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList()))) { + if (mname.getNamespace(abc.constants).hasName(pkg, abc.constants)) { + name_index = ii.name_index; + break; + } + } + } + for (int i = 1; i < abc.constants.constant_multiname.size(); i++) { + Multiname mname = abc.constants.constant_multiname.get(i); + if (mname != null && name.equals(mname.getName(abc.constants, new ArrayList()))) { + if (mname.getNamespace(abc.constants) != null && pkg.equals(mname.getNamespace(abc.constants).getName(abc.constants))) { + name_index = i; + break; + } + } + } + if (name_index == 0) { + if (pkg.isEmpty() && localData.currentScript != null /*FIXME!*/) { + for (Trait t : localData.currentScript.traits.traits) { + if (t.getName(abc).getName(abc.constants, new ArrayList()).equals(name)) { + name_index = t.name_index; + break; + } + } + } + if (name_index == 0) { + name_index = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(pkg, true)), 0, true), 0, 0, new ArrayList()), true); + } + } + + if (item instanceof ApplyTypeAVM2Item) { + ApplyTypeAVM2Item atype = (ApplyTypeAVM2Item) item; + List params = new ArrayList<>(); + for (GraphTargetItem s : atype.params) { + params.add(resolveType(localData, s, abc, allABCs)); + } + return abc.constants.getMultinameId(new Multiname(Multiname.TYPENAME, 0, 0, 0, name_index, params), true); + } + + return name_index; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java index bcd454190..0ad7d27ec 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ActionScriptParser.java @@ -1,2378 +1,2374 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SWC; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ConstructSuperAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.DefaultXMLNamespace; -import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXAttrAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXElemAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; -import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.AddAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.AsTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitAndAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitOrAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitXorAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.DeletePropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.DivideAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.EqAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.GeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.GtAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.InstanceOfAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.IsTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.LShiftAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.LeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.LtAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.ModuloAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.MultiplyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.NegAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.NeqAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.RShiftAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictNeqAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.SubtractAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.TypeOfAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.operations.URShiftAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.Loop; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BinaryOp; -import com.jpexs.decompiler.graph.model.BlockItem; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.CommaExpressionItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.DoWhileItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -import com.jpexs.decompiler.graph.model.ParenthesisItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import com.jpexs.helpers.Helper; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class ActionScriptParser { - - private long uniqLast = 0; - private final boolean debugMode = false; - private static final String AS3_NAMESPACE = "http://adobe.com/AS3/2006/builtin"; - - private ABC abc; - private List otherABCs; - - private static List playerABCs = new ArrayList<>(); - - private long uniqId() { - uniqLast++; - return uniqLast; - } - - private List commands(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { - List ret = new ArrayList<>(); - if (debugMode) { - System.out.println("commands:"); - } - GraphTargetItem cmd = null; - while ((cmd = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)) != null) { - ret.add(cmd); - } - if (debugMode) { - System.out.println("/commands"); - } - return ret; - } - - private GraphTargetItem type(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, ParseException { - ParsedSymbol s = lex(); - if (s.type == SymbolType.MULTIPLY) { - return new UnboundedTypeItem(); - } else if (s.type == SymbolType.VOID) { - return new TypeItem("void"); - } else { - lexer.pushback(s); - } - - GraphTargetItem t = name(pkg,needsActivation, true, openedNamespaces, null, false, false, variables, importedClasses); - t = applyType(pkg,needsActivation, importedClasses, openedNamespaces, t, new HashMap(), false, false, variables); - return t; - } - - private GraphTargetItem memberOrCall(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - ParsedSymbol s = lex(); - GraphTargetItem ret = newcmds; - while (s.isType(SymbolType.DOT, SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) { - switch (s.type) { - case BRACKET_OPEN: - case DOT: - case TYPENAME: - lexer.pushback(s); - ret = member(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - break; - case PARENT_OPEN: - ret = new CallAVM2Item(lexer.yyline(), ret, call(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - break; - - } - s = lex(); - } - if (s.type == SymbolType.INCREMENT) { - if (!isNameOrProp(ret)) { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostIncrementAVM2Item(null, ret); - s = lex(); - - } else if (s.type == SymbolType.DECREMENT) { - if (!isNameOrProp(ret)) { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostDecrementAVM2Item(null, ret); - s = lex(); - } - - lexer.pushback(s); - return ret; - } - - private GraphTargetItem applyType(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - GraphTargetItem ret = obj; - ParsedSymbol s = lex(); - if (s.type == SymbolType.TYPENAME) { - List params = new ArrayList<>(); - do { - params.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - s = lex(); - } while (s.type == SymbolType.COMMA); - if (s.type == SymbolType.USHIFT_RIGHT) { - s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); - lexer.pushback(s); - lexer.pushback(s); - } - if (s.type == SymbolType.SHIFT_RIGHT) { - s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); - lexer.pushback(s); - } - expected(s, lexer.yyline(), SymbolType.GREATER_THAN); - ret = new ApplyTypeAVM2Item(null, ret, params); - } else { - lexer.pushback(s); - } - return ret; - } - - private GraphTargetItem member(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - GraphTargetItem ret = obj; - ParsedSymbol s = lex(); - while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) { - ParsedSymbol s2 = lex(); - boolean attr = false; - if (s.type == SymbolType.DOT) { - if (s2.type == SymbolType.ATTRIBUTE) { - attr = true; - s = lex(); - } else { - lexer.pushback(s2); - } - - } else { - lexer.pushback(s2); - } - if (s.type == SymbolType.TYPENAME) { - lexer.pushback(s); - ret = applyType(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - s = lex(); - } else if (s.type == SymbolType.BRACKET_OPEN) { - GraphTargetItem index = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.BRACKET_CLOSE); - ret = new IndexAVM2Item(attr, ret, index, null, openedNamespaces); - s = lex(); - } else { - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String propName = s.value.toString(); - GraphTargetItem propItem = null; - s = lex(); - GraphTargetItem ns = null; - if (s.type == SymbolType.NAMESPACE_OP) { - ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), propName, null, openedNamespaces); - variables.add((UnresolvedAVM2Item) ns); - s = lex(); - if (s.type == SymbolType.BRACKET_OPEN) { - propItem = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.BRACKET_CLOSE); - propName = null; - } else { - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - propName = s.value.toString(); - propItem = null; - } - } else { - lexer.pushback(s); - } - if (ns != null) { - ret = new NamespacedAVM2Item(ns, propName, propItem, ret, attr, openedNamespaces, null); - } else { - ret = new PropertyAVM2Item(ret, (attr ? "@" : "") + propName, abc, otherABCs, openedNamespaces, new ArrayList()); - } - s = lex(); - } - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem name(String pkg,Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, ParseException { - ParsedSymbol s = lex(); - String name = ""; - if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; - s = lex(); - } - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - name += s.value.toString(); - s = lex(); - boolean attrBracket = false; - - while (s.isType(SymbolType.DOT)) { - name += s.value.toString(); //. or :: - s = lex(); - if (s.type == SymbolType.ATTRIBUTE) { - name += "@"; - s = lex(); - if (s.type == SymbolType.MULTIPLY) { - name += s.value.toString(); - } else if (s.type == SymbolType.IDENTIFIER) { - name += s.value.toString(); - } else { - if (s.type != SymbolType.BRACKET_OPEN) { - throw new ParseException("Attribute identifier or bracket expected", lexer.yyline()); - } - attrBracket = true; - continue; - } - } else { - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.NAMESPACE); - name += s.value.toString(); - } - s = lex(); - } - String nsname = null; - String nsprop = null; - GraphTargetItem nspropItem = null; - if (s.type == SymbolType.NAMESPACE_OP) { - if (name.contains(".")) { - nsname = name.substring(name.lastIndexOf('.') + 1); - } else { - nsname = name; - } - s = lex(); - if (s.type == SymbolType.IDENTIFIER) { - nsprop = s.value.toString(); - } else if (s.type == SymbolType.BRACKET_OPEN) { - nspropItem = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.BRACKET_CLOSE); - } - if (name.contains(".")) { - name = name.substring(0, name.lastIndexOf('.')); - } else { - name = null; - } - s = lex(); - } - /* - List params = new ArrayList<>(); - if (s.type == SymbolType.TYPENAME) { - s = lex(); - do { - String p = ""; - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - p = s.value.toString(); - s = lex(); - while (s.type == SymbolType.DOT) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - name += "." + s.value.toString(); - s = lex(); - } - params.add(p); - } while (s.type == SymbolType.COMMA); - expected(s, lexer.yyline(), SymbolType.GREATER_THAN); - s = lex(); - }*/ - - GraphTargetItem ret = null; - if (name != null) { - UnresolvedAVM2Item unr = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), name, null, openedNamespaces); - //unr.setIndex(index); - variables.add(unr); - ret = unr; - } - if (nsname != null) { - boolean attr = nsname.startsWith("@"); - if (attr) { - nsname = nsname.substring(1); - } - UnresolvedAVM2Item ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), nsname, null, openedNamespaces); - variables.add(ns); - ret = new NamespacedAVM2Item(ns, nsprop, nspropItem, ret, attr, openedNamespaces, null); - } - if (s.type == SymbolType.BRACKET_OPEN) { - lexer.pushback(s); - if (attrBracket) { - lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ATTRIBUTE, "@")); - lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DOT, ".")); - } - ret = member(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - } else { - lexer.pushback(s); - } - return ret; - } - - private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ParseException { - boolean found = false; - for (Object t : expected) { - if (symb.type == t) { - found = true; - } - if (symb.group == t) { - found = true; - } - } - if (!found) { - String expStr = ""; - boolean first = true; - for (Object e : expected) { - if (!first) { - expStr += " or "; - } - expStr += e; - first = false; - } - throw new ParseException("" + expStr + " expected but " + symb.type + " found", line); - } - } - - private ParsedSymbol expectedType(Object... type) throws IOException, ParseException { - ParsedSymbol symb = lex(); - expected(symb, lexer.yyline(), type); - return symb; - } - - private ParsedSymbol lex() throws IOException, ParseException { - ParsedSymbol ret = lexer.lex(); - if (debugMode) { - System.out.println(ret); - } - return ret; - } - - private List call(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - List ret = new ArrayList<>(); - //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER - ParsedSymbol s = lex(); - while (s.type != SymbolType.PARENT_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - ret.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - s = lex(); - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - return ret; - } - - private MethodAVM2Item method(String pkg,boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, GraphTargetItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, ParseException { - FunctionAVM2Item f = function(pkg,isInterface, needsActivation, importedClasses, namespace, thisType, openedNamespaces, functionName, isMethod, variables); - return new MethodAVM2Item(f.pkg,f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, f.namespace, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); - } - - private FunctionAVM2Item function(String pkg,boolean isInterface, Reference needsActivation, List importedClasses, int namespace, GraphTargetItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, ParseException { - openedNamespaces = new ArrayList<>(openedNamespaces); //local copy - int line = lexer.yyline(); - ParsedSymbol s; - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - List paramNames = new ArrayList<>(); - List paramTypes = new ArrayList<>(); - List paramValues = new ArrayList<>(); - boolean hasRest = false; - while (s.type != SymbolType.PARENT_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - if (s.type == SymbolType.REST) { - hasRest = true; - s = lex(); - } - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - - paramNames.add(s.value.toString()); - s = lex(); - if (!hasRest) { - if (s.type == SymbolType.COLON) { - paramTypes.add(type(pkg,needsActivation, importedClasses, openedNamespaces, variables)); - s = lex(); - } else { - paramTypes.add(new UnboundedTypeItem()); - } - if (s.type == SymbolType.ASSIGN) { - paramValues.add(expression(pkg,new Reference(false), importedClasses, openedNamespaces, null, isMethod, isMethod, isMethod, variables)); - s = lex(); - } else { - if (!paramValues.isEmpty()) { - throw new ParseException("Some of parameters do not have default values", lexer.yyline()); - } - } - } - - if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - if (hasRest) { - expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE); - } - } - s = lex(); - GraphTargetItem retType; - if (s.type == SymbolType.COLON) { - retType = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - } else { - retType = new UnboundedTypeItem(); - lexer.pushback(s); - } - List body = null; - List subvariables = new ArrayList<>(); - subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "this", null, true, openedNamespaces)); - for (int i = 0; i < paramNames.size() - (hasRest ? 1 : 0); i++) { - subvariables.add(new NameAVM2Item(paramTypes.get(i), lexer.yyline(), paramNames.get(i), null, true, openedNamespaces)); - } - if (hasRest) { - subvariables.add(new NameAVM2Item(TypeItem.UNBOUNDED, lexer.yyline(), paramNames.get(paramNames.size() - 1), null, true, openedNamespaces)); - } - subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "arguments", null, true, openedNamespaces)); - int parCnt = subvariables.size(); - Reference needsActivation2 = new Reference<>(false); - if (!isInterface) { - expectedType(SymbolType.CURLY_OPEN); - body = commands(pkg,needsActivation2, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, isMethod, 0, subvariables); - expectedType(SymbolType.CURLY_CLOSE); - } else { - expectedType(SymbolType.SEMICOLON); - } - - for (int i = 0; i < parCnt; i++) { - subvariables.remove(0); - } - return new FunctionAVM2Item(pkg,isInterface, needsActivation2.getVal(), namespace, hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); - } - - private GraphTargetItem traits(String scriptName,boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws ParseException, IOException, CompilationException { - ParsedSymbol s; - GraphTargetItem constr = null; - TypeItem thisType = pkg==null && classNameStr == null?null:new TypeItem(pkg == null || "".equals(pkg) ? classNameStr : pkg + "." + classNameStr); - List constrVariables = new ArrayList<>(); - List originalOpenedNamespaces=openedNamespaces; - int originalPrivateNs = privateNs; - boolean inPkg = pkg!=null; - looptrait: - while (true) { - s = lex(); - boolean isStatic = false; - int namespace = -1; - boolean isGetter = false; - boolean isSetter = false; - boolean isOverride = false; - boolean isFinal = false; - boolean isDynamic = false; - String customAccess = null; - - if(scriptTraits && s.type == SymbolType.PACKAGE){ - if(inPkg){ - throw new ParseException("No subpackages allowed", lexer.yyline()); - } - openedNamespaces = new ArrayList<>(); - lexer.pushback(s); - PackageAVM2Item p=parsePackage(openedNamespaces); - pkg = p.packageName; - inPkg = true; - publicNs = p.publicNs; - importedClasses = p.importedClasses; - s = lex(); - } - if (inPkg || classNameStr!=null) { - if (s.type == SymbolType.CURLY_OPEN) { - staticInitializer.addAll(commands(pkg,sinitNeedsActivation, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, false, 0, sinitVariables)); - expectedType(SymbolType.CURLY_CLOSE); - s = lex(); - } - - while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE, SymbolType.PROTECTED, SymbolType.OVERRIDE, SymbolType.FINAL, SymbolType.DYNAMIC, SymbolType.IDENTIFIER)) { - if (s.type == SymbolType.FINAL) { - if (isFinal) { - throw new ParseException("Only one final keyword allowed", lexer.yyline()); - } - isFinal = true; - } else if (s.type == SymbolType.DYNAMIC) { - if (isDynamic) { - throw new ParseException("Only one dynamic keyword allowed", lexer.yyline()); - } - isDynamic = true; - } else if (s.type == SymbolType.OVERRIDE) { - if (isOverride) { - throw new ParseException("Only one override keyword allowed", lexer.yyline()); - } - isOverride = true; - } else if (s.type == SymbolType.STATIC) { - if (isInterface) { - throw new ParseException("Interface cannot have static traits", lexer.yyline()); - } - if (classNameStr == null) { - throw new ParseException("No static keyword allowed here", lexer.yyline()); - } - if (isStatic) { - throw new ParseException("Only one static keyword allowed", lexer.yyline()); - } - isStatic = true; - } else if (s.type == SymbolType.IDENTIFIER) { - customAccess = s.value.toString(); - namespace = -2; - } else { - if (namespace != -1) { - throw new ParseException("Only one access identifier allowed", lexer.yyline()); - } - } - switch (s.type) { - case PUBLIC: - namespace = publicNs; - if (isInterface) { - throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); - } - break; - case PRIVATE: - namespace = privateNs; - if (isInterface) { - throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); - } - break; - case PROTECTED: - namespace = protectedNs; - if (isInterface) { - throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); - } - break; - } - s = lex(); - } - } else { - namespace = privateNs; - } - if (namespace == -1) { - if (isInterface) { - namespace = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(pkg==null||pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr, true)), 0, true); - } else { - namespace = packageInternalNs; - } - } - if (namespace == protectedNs && isStatic) { - namespace = protectedStaticNs; - } - switch (s.type) { - /*case PACKAGE: - lexer.pushback(s); - traits.add(parsePackage(openedNamespaces)); - break;*/ - case CLASS: - List subNamespaces=new ArrayList<>(openedNamespaces); - if (classNameStr != null) { - throw new ParseException("Nested classes not supported", lexer.yyline()); - } - if (isOverride) { - throw new ParseException("Override flag not allowed for classes", lexer.yyline()); - } - - //GraphTargetItem classTypeStr = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String classTypeStr = s.value.toString(); - GraphTargetItem extendsTypeStr = null; - s = lex(); - if (s.type == SymbolType.EXTENDS) { - extendsTypeStr = type(pkg,new Reference(false), importedClasses, subNamespaces, new ArrayList()); - s = lex(); - } - List implementsTypeStrs = new ArrayList<>(); - if (s.type == SymbolType.IMPLEMENTS) { - do { - GraphTargetItem implementsTypeStr = type(pkg,new Reference(false), importedClasses, subNamespaces, new ArrayList()); - implementsTypeStrs.add(implementsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - if (customAccess != null) { - throw new ParseException("Class cannot have custom namespace", lexer.yyline()); - } - traits.add((classTraits(scriptName,publicNs,pkg,importedClasses, isDynamic, isFinal, subNamespaces, pkg, namespace, false, classTypeStr, extendsTypeStr, implementsTypeStrs, new ArrayList()))); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INTERFACE: - if (classNameStr != null) { - throw new ParseException("Nested interfaces not supported", lexer.yyline()); - } - if (isOverride) { - throw new ParseException("Override flag not allowed for interfaces", lexer.yyline()); - } - if (isFinal) { - throw new ParseException("Final flag not allowed for interfaces", lexer.yyline()); - } - if (isDynamic) { - throw new ParseException("Dynamic flag not allowed for interfaces", lexer.yyline()); - } - //GraphTargetItem interfaceTypeStr = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String intTypeStr = s.value.toString(); - s = lex(); - List intExtendsTypeStrs = new ArrayList<>(); - - if (s.type == SymbolType.EXTENDS) { - do { - GraphTargetItem intExtendsTypeStr = type(pkg,new Reference(false), importedClasses, openedNamespaces, new ArrayList()); - intExtendsTypeStrs.add(intExtendsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - if (customAccess != null) { - throw new ParseException("Interface cannot have custom namespace", lexer.yyline()); - } - traits.add((classTraits(scriptName,publicNs,pkg,importedClasses, false, isFinal, openedNamespaces, pkg, namespace, true, intTypeStr, null, intExtendsTypeStrs, new ArrayList()))); - expectedType(SymbolType.CURLY_CLOSE); - break; - - case FUNCTION: - - if (isDynamic) { - throw new ParseException("Dynamic flag not allowed for methods", lexer.yyline()); - } - s = lex(); - if (s.type == SymbolType.GET) { - if (classNameStr == null) { - throw new ParseException("No get keyword allowed here", lexer.yyline()); - } - isGetter = true; - s = lex(); - } else if (s.type == SymbolType.SET) { - if (classNameStr == null) { - throw new ParseException("No set keyword allowed here", lexer.yyline()); - } - isSetter = true; - s = lex(); - } - - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String fname = s.value.toString(); - if (classNameStr != null && fname.equals(classNameStr)) { //constructor - if (isStatic) { - throw new ParseException("Constructor cannot be static", lexer.yyline()); - } - if (isStatic) { - throw new ParseException("Constructor cannot be static", lexer.yyline()); - } - if (isOverride) { - throw new ParseException("Override flag not allowed for constructor", lexer.yyline()); - } - if (isFinal) { - throw new ParseException("Final flag not allowed for constructor", lexer.yyline()); - } - if (isInterface) { - throw new ParseException("Interface cannot have constructor", lexer.yyline()); - } - constr = (method(pkg,false, customAccess, new Reference(false), importedClasses, false, false, thisType, openedNamespaces, false, namespace, "", true, constrVariables)); - } else { - MethodAVM2Item ft = method(pkg,isInterface, customAccess, new Reference(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, namespace, fname, true, new ArrayList()); - - if (isGetter) { - if (!ft.paramTypes.isEmpty()) { - throw new ParseException("Getter can't have any parameters", lexer.yyline()); - } - } - - if (isSetter) { - if (ft.paramTypes.size() != 1) { - throw new ParseException("Getter must have exactly one parameter", lexer.yyline()); - } - } - - if (isStatic && isInterface) { - if (isInterface) { - throw new ParseException("Interface cannot have static fields", lexer.yyline()); - } - } - GraphTargetItem t; - if (isGetter) { - GetterAVM2Item g = new GetterAVM2Item(ft.pkg,isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); - t = g; - } else if (isSetter) { - SetterAVM2Item st = new SetterAVM2Item(ft.pkg,isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); - t = st; - } else { - t = ft; - } - - traits.add(t); - } - //} - break; - case NAMESPACE: - if (isInterface) { - throw new ParseException("Interface cannot have namespace fields", lexer.yyline()); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String nname = s.value.toString(); - String nval = ""; - s = lex(); - - if (s.type == SymbolType.ASSIGN) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.STRING); - nval = s.value.toString(); - s = lex(); - } else { - nval = (pkg==null||pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr) + "/" + nname; - } - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - - ConstAVM2Item ns = new ConstAVM2Item(pkg,customAccess, true, namespace, nname, new TypeItem("Namespace"), new StringAVM2Item(null, nval), lexer.yyline()); - traits.add(ns); - break; - case CONST: - case VAR: - boolean isConst = s.type == SymbolType.CONST; - if (isOverride) { - throw new ParseException("Override flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); - } - if (isFinal) { - throw new ParseException("Final flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); - } - if (isDynamic) { - throw new ParseException("Dynamic flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); - } - if (isInterface) { - throw new ParseException("Interface cannot have variable/const fields", lexer.yyline()); - } - - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String vcname = s.value.toString(); - s = lex(); - GraphTargetItem type = null; - if (s.type == SymbolType.COLON) { - type = type(pkg,new Reference(false), importedClasses, openedNamespaces, new ArrayList()); - s = lex(); - } else { - type = TypeItem.UNBOUNDED; - } - - GraphTargetItem value = null; - - if (s.type == SymbolType.ASSIGN) { - value = expression(pkg,new Reference(false), importedClasses, openedNamespaces, new HashMap(), false, false, true, isStatic || isConst ? sinitVariables : constrVariables); - s = lex(); - } - GraphTargetItem tar; - if (isConst) { - tar = new ConstAVM2Item(pkg,customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); - } else { - tar = new SlotAVM2Item(pkg,customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); - } - traits.add(tar); - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - break; - default: - if(s.type == SymbolType.CURLY_CLOSE && inPkg && classNameStr == null){ - inPkg = false; - pkg = null; - openedNamespaces = originalOpenedNamespaces; - privateNs = originalPrivateNs; - }else{ - lexer.pushback(s); - break looptrait; - } - - } - } - return constr; - } - - private GraphTargetItem classTraits(String scriptName,int gpublicNs,String pkg,List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ParseException, CompilationException { - - GraphTargetItem ret = null; - - ParsedSymbol s = null; - List traits = new ArrayList<>(); - - String classNameStr = nameStr; - - openedNamespaces = new ArrayList<>(openedNamespaces); - - int publicNs=0; - int privateNs = 0; - int packageInternalNs = 0; - if(pkg!=null){ - openedNamespaces.add(packageInternalNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true)); - } - if(pkg!=null && !pkg.equals("")){ - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); - }else{ - publicNs = gpublicNs; - } - - openedNamespaces.add(privateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(fileName + "$", true) - - - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(AS3_NAMESPACE, true)), 0, true)); - - - //int privateNs = 0; - int protectedNs = 0; - //int publicNs = namespace; - int protectedStaticNs = 0; - - openedNamespaces.add(protectedNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PROTECTED, abc.constants.getStringId(packageName==null?(scriptName+"$0:"/*FIXME?*/+classNameStr):packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)))); - openedNamespaces.add(protectedStaticNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(packageName==null||packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)))); - - if (extendsStr != null) { - List indices = new ArrayList<>(); - List names = new ArrayList<>(); - List namespaces = new ArrayList<>(); - //FIXME for Private classes in script!!! - AVM2SourceGenerator.parentNamesAddNames(abc, otherABCs, AVM2SourceGenerator.resolveType(new SourceGeneratorLocalData(new HashMap(), 0, false, 0), ((TypeItem) ((UnresolvedAVM2Item) extendsStr).resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), new ArrayList())), abc, otherABCs), indices, names, namespaces); - for (int i = 0; i < names.size(); i++) { - if (namespaces.get(i).isEmpty()) { - continue; - } - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(namespaces.get(i) + ":" + names.get(i), true)), 0, true)); - } - } - - Reference staticNeedsActivation = new Reference<>(false); - List staticInit = new ArrayList<>(); - List sinitVariables = new ArrayList<>(); - GraphTargetItem constr = traits(scriptName,false, sinitVariables, staticNeedsActivation, staticInit, importedClasses, privateNs, protectedNs, publicNs, packageInternalNs, protectedStaticNs, openedNamespaces, packageName, classNameStr, isInterface, traits); - - if (isInterface) { - return new InterfaceAVM2Item(importedClasses,packageName,openedNamespaces, isFinal, namespace, classNameStr, implementsStr, traits); - } else { - return new ClassAVM2Item(importedClasses,packageName,openedNamespaces, protectedNs, isDynamic, isFinal, namespace, classNameStr, extendsStr, implementsStr, staticInit, staticNeedsActivation.getVal(), sinitVariables, constr, traits); - } - } - - private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - switch (s.type) { - /*case INT: - expectedType(SymbolType.PARENT_OPEN); - ret = new ToIntegerAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case NUMBER_OP: - s = lex(); - if (s.type == SymbolType.DOT) { - VariableAVM2Item vi = new VariableAVM2Item(s.value.toString(), null, false); - variables.add(vi); - ret = memberOrCall(vi, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToNumberAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - } - break; - case STRING_OP: - ParsedSymbol sop = s; - s = lex(); - if (s.type == SymbolType.DOT) { - lexer.pushback(s); - VariableAVM2Item vi2 = new VariableAVM2Item(sop.value.toString(), null, false); - variables.add(vi2); - ret = memberOrCall(vi2, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToStringAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); - } - break;*/ - default: - return null; - } - //return ret; - } - - private GraphTargetItem add(Object a) { - if (a instanceof List) { - List l = (List) a; - if (l.isEmpty()) { - return null; - } - GraphTargetItem o = add(l.get(0)); - for (int i = 1; i < l.size(); i++) { - o = add(o, l.get(i)); - } - return o; - } - if (a instanceof StringBuilder) { - if (((StringBuilder) a).length() == 0) { - return null; - } - GraphTargetItem ret = new StringAVM2Item(null, a.toString()); - ((StringBuilder) a).setLength(0); - return ret; - } - if (a instanceof String) { - return new StringAVM2Item(null, (String) a); - } - if (a instanceof GraphTargetItem) { - return (GraphTargetItem) a; - } - return null; - } - - private GraphTargetItem add(Object a, Object b) { - GraphTargetItem ta = add(a); - GraphTargetItem tb = add(b); - if (ta == null && tb == null) { - return null; - } - if (ta == null) { - return tb; - } - if (tb == null) { - return ta; - } - return new AddAVM2Item(null, ta, tb); - } - - private void addS(List rets, StringBuilder sb) { - if (sb.length() > 0) { - if (!rets.isEmpty() && (rets.get(rets.size() - 1) instanceof StringAVM2Item)) { - ((StringAVM2Item) rets.get(rets.size() - 1)).value += sb.toString(); - } else { - rets.add(new StringAVM2Item(null, sb.toString())); - } - sb.setLength(0); - } - } - - private List xmltag(String pkg,Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - ParsedSymbol s = null; - List rets = new ArrayList<>(); - //GraphTargetItem ret = null; - StringBuilder sb = new StringBuilder(); - loop: - do { - s = lex(); - List sub = new ArrayList<>(); - Reference subclose = new Reference<>(0); - Reference subusesvars = new Reference<>(false); - switch (s.type) { - case XML_ATTRNAMEVAR_BEGIN: //add - usesVars.setVal(true); - addS(rets, sb); - rets.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.CURLY_CLOSE); - expectedType(SymbolType.ASSIGN); - sb.append("="); - lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); - break; - case XML_ATTRVALVAR_BEGIN: //esc_xattr - usesVars.setVal(true); - sb.append("\""); - addS(rets, sb); - rets.add(new EscapeXAttrAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); - sb.append("\""); - expectedType(SymbolType.CURLY_CLOSE); - lexer.yybegin(ActionScriptLexer.XMLOPENTAG); - break; - case XML_INSTRATTRNAMEVAR_BEGIN: //add - usesVars.setVal(true); - addS(rets, sb); - rets.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.CURLY_CLOSE); - expectedType(SymbolType.ASSIGN); - sb.append("="); - lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); - break; - case XML_INSTRATTRVALVAR_BEGIN: //esc_xattr - usesVars.setVal(true); - sb.append("\""); - addS(rets, sb); - rets.add(new EscapeXAttrAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); - sb.append("\""); - expectedType(SymbolType.CURLY_CLOSE); - lexer.yybegin(ActionScriptLexer.XMLOPENTAG); - break; - case XML_VAR_BEGIN: //esc_xelem - usesVars.setVal(true); - addS(rets, sb); - rets.add(new EscapeXElemAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.CURLY_CLOSE); - lexer.yybegin(ActionScriptLexer.XML); - break; - case XML_FINISHVARTAG_BEGIN: //add - usesVars.setVal(true); - closedVarTags.setVal(closedVarTags.getVal() + 1); - sb.append(""); - addS(rets, sb); - lexer.yybegin(ActionScriptLexer.XML); - break; - case XML_STARTVARTAG_BEGIN: //add - //openedTags.add("*"); - - //ret = add(ret, ); - GraphTargetItem ex = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.CURLY_CLOSE); - lexer.yybegin(ActionScriptLexer.XMLOPENTAG); - sub.add("*"); - sb.append("<"); - addS(rets, sb); - rets.add(ex); - rets.addAll(xmltag(pkg,subusesvars, sub, subclose, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - closedVarTags.setVal(subclose.getVal() + subclose.getVal()); - break; - case XML_INSTRVARTAG_BEGIN: //add - usesVars.setVal(true); - addS(rets, sb); - sb.append(" st = xmltag(pkg,subusesvars, sub, closedVarTags, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); - sb.append(s.value.toString()); - addS(rets, sb); - rets.addAll(st); - closedVarTags.setVal(subclose.getVal() + subclose.getVal()); - break; - /*case XML_STARTTAG_END: - sb.append(s.value.toString()); - ret = addstr(ret,sb); - break;*/ - case XML_FINISHTAG: - String tname = s.value.toString().substring(2, s.value.toString().length() - 1).trim(); - if (openedTags.contains(tname)) { - openedTags.remove(tname); - } else if (openedTags.contains("*")) { - openedTags.remove("*"); - } else { - throw new ParseException("XML : Closing unopened tag", lexer.yyline()); - } - sb.append(s.value.toString()); - break; - case XML_STARTFINISHTAG_END: - openedTags.remove(openedTags.size() - 1); //close last tag - sb.append(s.value.toString()); - break; - case EOF: - throw new ParseException("End of file before XML finish", lexer.yyline()); - default: - sb.append(s.value.toString()); - break; - } - } while (!(openedTags.isEmpty() || closedVarTags.getVal() >= openedTags.size())); - addS(rets, sb); - return rets; - } - - private GraphTargetItem xml(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - List openedTags = new ArrayList<>(); - int closedVarTags = 0; - - GraphTargetItem ret = add(xmltag(pkg,new Reference(false), openedTags, new Reference(closedVarTags), needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - ret = new XMLAVM2Item(ret); - lexer.yybegin(ActionScriptLexer.YYINITIAL); - //TODO: Order of additions as in official compiler - return ret; - } - - private GraphTargetItem command(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ParseException { - LexBufferer buf = new LexBufferer(); - lexer.addListener(buf); - GraphTargetItem ret = null; - if (debugMode) { - System.out.println("command:"); - } - ParsedSymbol s = lex(); - if (s.type == SymbolType.EOF) { - return null; - } - String loopLabel = null; - - if (s.type == SymbolType.IDENTIFIER) { - ParsedSymbol sc = lex(); - if (sc.type == SymbolType.COLON) { - loopLabel = s.value.toString(); - s = lex(); - } else { - lexer.pushback(sc); - } - } - - if (s.type == SymbolType.DEFAULT) { - ParsedSymbol sx = lex(); - if (sx.type != SymbolType.IDENTIFIER) { - lexer.pushback(sx); - } else { - if (!sx.value.equals("xml")) { - lexer.pushback(sx); - } else { - expectedType(SymbolType.NAMESPACE); - expectedType(SymbolType.ASSIGN); - GraphTargetItem ns = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - ret = new DefaultXMLNamespace(null, ns); - //TODO: use dxns for attribute namespaces instead of dxnslate - } - } - } - if (ret == null) { - switch (s.type) { - case USE: - expectedType(SymbolType.NAMESPACE); - GraphTargetItem ns = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE /*FIXME?*/, abc.constants.getStringId(ns.toString(), true)), 0, true)); - break; - case WITH: - needsActivation.setVal(true); - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem wvar = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//(name(false, openedNamespaces, registerVars, inFunction, inMethod, variables)); - if (!isNameOrProp(wvar)) { - throw new ParseException("Not a property or name", lexer.yyline()); - } - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List withVars = new ArrayList<>(); - List wcmd = commands(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, withVars); - variables.addAll(withVars); - for (AssignableAVM2Item a : withVars) { - if (a instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item ua = (UnresolvedAVM2Item) a; - ua.scopeStack.add(0, wvar); - } - } - expectedType(SymbolType.CURLY_CLOSE); - ret = new WithAVM2Item(null, wvar, wcmd); - ((WithAVM2Item) ret).subvariables = withVars; - break; - /*case DELETE: - GraphTargetItem varDel = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); - if(!isNameOrProp(varDel)){ - throw new ParseException("Not a property or name", lexer.yyline()); - } - if (varDel instanceof GetPropertyAVM2Item) { - GetPropertyAVM2Item gm = (GetPropertyAVM2Item) varDel; - ret = new DeletePropertyAVM2Item(null, gm.object, gm.propertyName); - } else if (varDel instanceof NameAVM2Item) { - variables.remove(varDel); - ret = new DeletePropertyAVM2Item(null, null, (NameAVM2Item) varDel); - } else { - throw new ParseException("Not a property", lexer.yyline()); - } - break;*/ - case FUNCTION: - s = lexer.lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - needsActivation.setVal(true); - ret = (function(pkg,false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, s.value.toString(), false, variables)); - break; - case VAR: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String varIdentifier = s.value.toString(); - s = lex(); - GraphTargetItem type; - if (s.type == SymbolType.COLON) { - type = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - s = lex(); - } else { - type = new UnboundedTypeItem(); - } - - if (s.type == SymbolType.ASSIGN) { - GraphTargetItem varval = (expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, varval, true, openedNamespaces); - variables.add((NameAVM2Item) ret); - } else { - ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, null, true, openedNamespaces); - variables.add((NameAVM2Item) ret); - lexer.pushback(s); - } - break; - case CURLY_OPEN: - ret = new BlockItem(null, commands(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - /*case INCREMENT: //preincrement - case DECREMENT: //predecrement - GraphTargetItem varincdec = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); - if(!isNameOrProp(varincdec)){ - throw new ParseException("Not a property or name", lexer.yyline()); - } - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementAVM2Item(null, varincdec); - } else if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementAVM2Item(null, varincdec); - } - break;*/ - case SUPER: //constructor call - ParsedSymbol ss2 = lex(); - if (ss2.type == SymbolType.PARENT_OPEN) { - List args = call(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); - ret = new ConstructSuperAVM2Item(null, new LocalRegAVM2Item(null, 0, null), args); - } else {//no costructor call, but it could be calling parent methods... => handle in expression - lexer.pushback(ss2); - lexer.pushback(s); - } - break; - case IF: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem ifExpr = (expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - GraphTargetItem onTrue = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); - List onTrueList = new ArrayList<>(); - onTrueList.add(onTrue); - s = lex(); - List onFalseList = null; - if (s.type == SymbolType.ELSE) { - onFalseList = new ArrayList<>(); - onFalseList.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - } else { - lexer.pushback(s); - } - ret = new IfItem(null, ifExpr, onTrueList, onFalseList); - break; - case WHILE: - expectedType(SymbolType.PARENT_OPEN); - List whileExpr = new ArrayList<>(); - whileExpr.add(commaExpression(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - List whileBody = new ArrayList<>(); - Loop wloop = new Loop(uniqId(), null, null); - if (loopLabel != null) { - loopLabels.put(wloop, loopLabel); - } - loops.push(wloop); - whileBody.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - ret = new WhileItem(null, wloop, whileExpr, whileBody); - break; - case DO: - List doBody = new ArrayList<>(); - Loop dloop = new Loop(uniqId(), null, null); - loops.push(dloop); - if (loopLabel != null) { - loopLabels.put(dloop, loopLabel); - } - doBody.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - expectedType(SymbolType.WHILE); - expectedType(SymbolType.PARENT_OPEN); - List doExpr = new ArrayList<>(); - doExpr.add(commaExpression(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new DoWhileItem(null, dloop, doBody, doExpr); - break; - case FOR: - s = lex(); - boolean forin = false; - boolean each = false; - GraphTargetItem collection = null; - if (s.type == SymbolType.EACH) { - each = true; - forin = true; - s = lex(); - } - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - GraphTargetItem firstCommand = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, false, variables); - if (firstCommand instanceof NameAVM2Item) { - NameAVM2Item nai = (NameAVM2Item) firstCommand; - if (nai.isDefinition() && nai.getAssignedValue() == null) { - firstCommand = expressionRemainder(pkg,needsActivation, openedNamespaces, firstCommand, registerVars, inFunction, inMethod, true, variables, importedClasses); - } - } - InAVM2Item inexpr = null; - if (firstCommand instanceof InAVM2Item) { - forin = true; - inexpr = (InAVM2Item) firstCommand; - } else { - if (forin) { - throw new ParseException("In expression required", lexer.yyline()); - } - } - - Loop floop = new Loop(uniqId(), null, null); - loops.push(floop); - if (loopLabel != null) { - loopLabels.put(floop, loopLabel); - } - List forFinalCommands = new ArrayList<>(); - GraphTargetItem forExpr = null; - List forFirstCommands = new ArrayList<>(); - if (!forin) { - //GraphTargetItem firstCommand = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); - if (firstCommand != null) { //can be empty command - forFirstCommands.add(firstCommand); - } - forExpr = (expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - } - expectedType(SymbolType.PARENT_CLOSE); - List forBody = new ArrayList<>(); - forBody.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); - if (forin) { - if (each) { - ret = new ForEachInAVM2Item(null, floop, inexpr, forBody); - } else { - - ret = new ForInAVM2Item(null, floop, inexpr, forBody); - } - } else { - ret = new ForItem(null, floop, forFirstCommands, forExpr, forFinalCommands, forBody); - } - break; - case SWITCH: - Loop sloop = new Loop(-uniqId(), null, null); //negative id marks switch = no continue - loops.push(sloop); - if (loopLabel != null) { - loopLabels.put(sloop, loopLabel); - } - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem switchExpr = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - s = lex(); - //ret.addAll(switchExpr); - int exprReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - registerVars.put("__switch" + uniqId(), i); - exprReg = i; - break; - } - } - List> caseIfs = new ArrayList<>(); - List> caseCmds = new ArrayList<>(); - List caseExprsAll = new ArrayList<>(); - List valueMapping = new ArrayList<>(); - int pos = 0; - while (s.type == SymbolType.CASE) { - List caseExprs = new ArrayList<>(); - while (s.type == SymbolType.CASE) { - GraphTargetItem curCaseExpr = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - caseExprs.add(curCaseExpr); - expectedType(SymbolType.COLON); - s = lex(); - caseExprsAll.add(curCaseExpr); - valueMapping.add(pos); - } - pos++; - lexer.pushback(s); - List caseCmd = commands(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); - caseCmds.add(caseCmd); - s = lex(); - } - List defCmd = new ArrayList<>(); - if (s.type == SymbolType.DEFAULT) { - expectedType(SymbolType.COLON); - defCmd = commands(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); - s = lexer.lex(); - } - expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); - ret = new SwitchItem(null, sloop, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); - break; - case BREAK: - s = lex(); - long bloopId = 0; - if (loops.isEmpty()) { - throw new ParseException("No loop to break", lexer.yyline()); - } - if (s.type == SymbolType.IDENTIFIER) { - String breakLabel = s.value.toString(); - for (Loop l : loops) { - if (breakLabel.equals(loopLabels.get(l))) { - bloopId = l.id; - break; - } - } - if (bloopId == 0) { - throw new ParseException("Identifier of loop expected", lexer.yyline()); - } - } else { - lexer.pushback(s); - bloopId = loops.peek().id; - } - ret = new BreakItem(null, bloopId); - break; - case CONTINUE: - s = lex(); - long cloopId = 0; - if (loops.isEmpty()) { - throw new ParseException("No loop to continue", lexer.yyline()); - } - if (s.type == SymbolType.IDENTIFIER) { - String continueLabel = s.value.toString(); - for (Loop l : loops) { - if (l.id < 0) { //negative id marks switch => no continue - continue; - } - if (continueLabel.equals(loopLabels.get(l))) { - cloopId = l.id; - break; - } - } - if (cloopId == -1) { - throw new ParseException("Identifier of loop expected", lexer.yyline()); - } - } else { - lexer.pushback(s); - for (int i = loops.size() - 1; i >= 0; i--) { - if (loops.get(i).id >= 0) {//no switches - cloopId = loops.get(i).id; - break; - } - } - if (cloopId <= 0) { - throw new ParseException("No loop to continue", lexer.yyline()); - } - } - //TODO: handle switch - ret = new ContinueItem(null, cloopId); - break; - case RETURN: - GraphTargetItem retexpr = expression(pkg,needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); - if (retexpr == null) { - ret = new ReturnVoidAVM2Item(null); - } else { - ret = new ReturnValueAVM2Item(null, retexpr); - } - break; - case TRY: - needsActivation.setVal(true); - List tryCommands = new ArrayList<>(); - tryCommands.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - s = lex(); - boolean found = false; - List> catchCommands = new ArrayList<>(); - List catchExceptions = new ArrayList<>(); - int varCnt = variables.size(); - List> catchesVars = new ArrayList<>(); - while (s.type == SymbolType.CATCH) { - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - - String enamestr = s.value.toString(); - expectedType(SymbolType.COLON); - GraphTargetItem etype = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - NameAVM2Item e = new NameAVM2Item(etype, lexer.yyline(), enamestr, new ExceptionAVM2Item(null)/*?*/, true/*?*/, openedNamespaces); - variables.add(e); - catchExceptions.add(e); - e.setSlotNumber(1); - e.setSlotScope(Integer.MAX_VALUE); //will be changed later - expectedType(SymbolType.PARENT_CLOSE); - List cc = new ArrayList<>(); - List catchVars = new ArrayList<>(); - cc.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, catchVars)); - catchesVars.add(catchVars); - variables.addAll(catchVars); - - for (AssignableAVM2Item a : catchVars) { - if (a instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item ui = (UnresolvedAVM2Item) a; - if (ui.getVariableName().equals(e.getVariableName())) { - try { - ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); - } catch (CompilationException ex) { - //ignore - } - ui.setSlotNumber(e.getSlotNumber()); - ui.setSlotScope(e.getSlotScope()); - } - - } - } - - catchCommands.add(cc); - s = lex(); - found = true; - } - //TODO: - for (int i = varCnt; i < variables.size(); i++) { - AssignableAVM2Item av = variables.get(i); - if (av instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item ui = (UnresolvedAVM2Item) av; - for (NameAVM2Item e : catchExceptions) { - if (ui.getVariableName().equals(e.getVariableName())) { - try { - ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); - } catch (CompilationException ex) { - //ignore - } - ui.setSlotNumber(e.getSlotNumber()); - ui.setSlotScope(e.getSlotScope()); - } - } - } - } - - List finallyCommands = null; - if (s.type == SymbolType.FINALLY) { - finallyCommands = new ArrayList<>(); - finallyCommands.add(command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); - found = true; - s = lex(); - } - if (!found) { - expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); - } - lexer.pushback(s); - TryAVM2Item tai = new TryAVM2Item(tryCommands, null, catchCommands, finallyCommands); - tai.catchVariables = catchesVars; - tai.catchExceptions2 = catchExceptions; - ret = tai; - break; - case THROW: - ret = new ThrowAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - break; - default: - GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel, variables); - if (valcmd != null) { - ret = valcmd; - break; - } - if (s.type == SymbolType.SEMICOLON) { - return null; - } - lexer.pushback(s); - ret = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - if (debugMode) { - System.out.println("/command"); - } - } - } - if (debugMode) { - System.out.println("/command"); - } - lexer.removeListener(buf); - if (ret == null) { //can be popped expression - buf.pushAllBack(lexer); - ret = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - } - s = lex(); - if ((s != null) && (s.type != SymbolType.SEMICOLON)) { - lexer.pushback(s); - } - - return ret; - - } - - private GraphTargetItem expression(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { - return expression(pkg,needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); - } - - private GraphTargetItem fixPrecedence(GraphTargetItem expr) { - GraphTargetItem ret = expr; - if (expr instanceof BinaryOp) { - BinaryOp bo = (BinaryOp) expr; - GraphTargetItem left = bo.getLeftSide(); - //GraphTargetItem right=bo.getRightSide(); - if (left.getPrecedence() > bo.getPrecedence()) { - if (left instanceof BinaryOp) { - BinaryOp leftBo = (BinaryOp) left; - bo.setLeftSide(leftBo.getRightSide()); - leftBo.setRightSide(expr); - return left; - } - } - } - return ret; - } - - private GraphTargetItem expressionRemainder(String pkg,Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List importedClasses) throws IOException, ParseException { - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - - if (ret == null) { - switch (s.type) { - case AS: - GraphTargetItem type = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - ret = new AsTypeAVM2Item(null, expr, type); - allowRemainder = false; - break; - case DESCENDANTS: - ParsedSymbol d = lex(); - expected(d, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.MULTIPLY); - ret = new GetDescendantsAVM2Item(expr, d.type == SymbolType.MULTIPLY?null:d.value.toString(), openedNamespaces); - allowRemainder = true; - break; - case FILTER: - needsActivation.setVal(true); - ret = new XMLFilterAVM2Item(expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables), openedNamespaces); - expectedType(SymbolType.PARENT_CLOSE); - allowRemainder = true; - break; - /*case NAMESPACE_OP: - s = lex(); - if (s.type == SymbolType.BRACKET_OPEN) { - GraphTargetItem index = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - NameAVM2Item name = new NameAVM2Item(new UnboundedTypeItem(), lexer.yyline(), null, null, false, openedNamespaces); - name.setIndex(index); - name.setNs(expr); - ret = name; - expectedType(SymbolType.BRACKET_CLOSE); - } else { - lexer.pushback(s); - GraphTargetItem name = name(pkg,needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); - if (name instanceof UnresolvedAVM2Item) { - ((UnresolvedAVM2Item) name).setNs(expr); - //((UnresolvedAVM2Item) name).unresolved = false; - //TODO - } else { - throw new ParseException("Not a property name", lexer.yyline()); - } - ret = name; - } - break;*/ - case IN: - ret = new InAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - break; - case TERNAR: - GraphTargetItem terOnTrue = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.COLON); - GraphTargetItem terOnFalse = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - ret = new TernarOpItem(null, expr, terOnTrue, terOnFalse); - break; - case SHIFT_LEFT: - ret = new LShiftAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case SHIFT_RIGHT: - ret = new RShiftAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case USHIFT_RIGHT: - ret = new URShiftAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case BITAND: - ret = new BitAndAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case BITOR: - ret = new BitOrAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case DIVIDE: - ret = new DivideAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case MODULO: - ret = new ModuloAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case EQUALS: - ret = new EqAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case STRICT_EQUALS: - ret = new StrictEqAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case NOT_EQUAL: - ret = new NeqAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case STRICT_NOT_EQUAL: - ret = new StrictNeqAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case LOWER_THAN: - ret = new LtAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case LOWER_EQUAL: - ret = new LeAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case GREATER_THAN: - case XML_STARTVARTAG_BEGIN: - case XML_STARTTAG_BEGIN: - if (s.type != SymbolType.GREATER_THAN) { - lexer.yypushbackstr(s.value.toString().substring(1)); //parse again as GREATER_THAN - } - ret = new GtAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case GREATER_EQUAL: - ret = new GeAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case AND: - ret = new AndItem(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case OR: - ret = new OrItem(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case MINUS: - ret = new SubtractAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case MULTIPLY: - ret = new MultiplyAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case PLUS: - ret = new AddAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case XOR: - ret = new BitXorAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case INSTANCEOF: - ret = new InstanceOfAVM2Item(null, expr, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - break; - case IS: - GraphTargetItem istype = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables);//type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - ret = new IsTypeAVM2Item(null, expr, istype); - allowRemainder = false; - break; - case ASSIGN: - case ASSIGN_BITAND: - case ASSIGN_BITOR: - case ASSIGN_DIVIDE: - case ASSIGN_MINUS: - case ASSIGN_MODULO: - case ASSIGN_MULTIPLY: - case ASSIGN_PLUS: - case ASSIGN_SHIFT_LEFT: - case ASSIGN_SHIFT_RIGHT: - case ASSIGN_USHIFT_RIGHT: - case ASSIGN_XOR: - GraphTargetItem assigned = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - switch (s.type) { - case ASSIGN: - //assigned = assigned; - break; - case ASSIGN_BITAND: - assigned = new BitAndAVM2Item(null, expr, assigned); - break; - case ASSIGN_BITOR: - assigned = new BitOrAVM2Item(null, expr, assigned); - break; - case ASSIGN_DIVIDE: - assigned = new DivideAVM2Item(null, expr, assigned); - break; - case ASSIGN_MINUS: - assigned = new SubtractAVM2Item(null, expr, assigned); - break; - case ASSIGN_MODULO: - assigned = new ModuloAVM2Item(null, expr, assigned); - break; - case ASSIGN_MULTIPLY: - assigned = new MultiplyAVM2Item(null, expr, assigned); - break; - case ASSIGN_PLUS: - assigned = new AddAVM2Item(null, expr, assigned); - break; - case ASSIGN_SHIFT_LEFT: - assigned = new LShiftAVM2Item(null, expr, assigned); - break; - case ASSIGN_SHIFT_RIGHT: - assigned = new RShiftAVM2Item(null, expr, assigned); - break; - case ASSIGN_USHIFT_RIGHT: - assigned = new URShiftAVM2Item(null, expr, assigned); - break; - case ASSIGN_XOR: - assigned = new BitXorAVM2Item(null, expr, assigned); - break; - } - - if (!(expr instanceof AssignableAVM2Item)) { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - AssignableAVM2Item as = ((AssignableAVM2Item) expr).copy(); - if ((as instanceof UnresolvedAVM2Item) || (as instanceof NameAVM2Item)) { - variables.add(as); - } - as.setAssignedValue(assigned); - if (expr instanceof NameAVM2Item) { - ((NameAVM2Item) expr).setDefinition(false); - } - ret = as; - break; - case DOT: //member - case BRACKET_OPEN: //member - case PARENT_OPEN: //function call - lexer.pushback(s); - ret = memberOrCall(pkg,needsActivation, importedClasses, openedNamespaces, expr, registerVars, inFunction, inMethod, variables); - break; - - default: - lexer.pushback(s); - if (expr instanceof ParenthesisItem) { - if (isType(((ParenthesisItem) expr).value)) { - GraphTargetItem expr2 = expression(pkg,needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, true, variables); - if (expr2 != null) { - ret = new CoerceAVM2Item(null, ((ParenthesisItem) expr).value, expr2); - } - } - } - } - } - ret = fixPrecedence(ret); - return ret; - } - - private boolean isNameOrProp(GraphTargetItem item) { - if (item instanceof UnresolvedAVM2Item) { - return true; //we don't know yet - } - if (item instanceof NameAVM2Item) { - return true; - } - if (item instanceof PropertyAVM2Item) { - return true; - } - if (item instanceof IndexAVM2Item) { - return true; - } - return false; - } - - private boolean isType(GraphTargetItem item) { - if (item == null) { - return false; - } - while (item instanceof GetPropertyAVM2Item) { - item = ((GetPropertyAVM2Item) item).object; - } - if (item instanceof NameAVM2Item) { - return true; - } - return false; - } - - private int brackets(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - ParsedSymbol s = lex(); - int arrCnt = 0; - if (s.type == SymbolType.BRACKET_OPEN) { - s = lex(); - - while (s.type != SymbolType.BRACKET_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - arrCnt++; - ret.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE); - } - } - } else { - lexer.pushback(s); - return -1; - } - return arrCnt; - } - - private GraphTargetItem commaExpression(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ParseException { - GraphTargetItem cmd = null; - List expr = new ArrayList<>(); - ParsedSymbol s; - do { - cmd = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forInLevel, false, variables); - if (cmd != null) { - expr.add(cmd); - } - s = lex(); - } while (s.type == SymbolType.COMMA && cmd != null); - lexer.pushback(s); - if (cmd == null) { - expr.add(expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - } else { - if (!cmd.hasReturnValue()) { - throw new ParseException("Expression expected", lexer.yyline()); - } - } - return new CommaExpressionItem(null, expr); - } - - private GraphTargetItem expression(String pkg,Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { - if (debugMode) { - System.out.println("expression:"); - } - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - boolean existsRemainder = false; - boolean assocRight = false; - switch (s.type) { - case XML_STARTTAG_BEGIN: - lexer.pushback(s); - ret = xml(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); - existsRemainder = true; - break; - case STRING: - ret = new StringAVM2Item(null, s.value.toString()); - existsRemainder = true; - break; - case NEGATE: - ret = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables); - ret = new NegAVM2Item(null, ret); - existsRemainder = true; - break; - case MINUS: - s = lex(); - if (s.isType(SymbolType.DOUBLE)) { - ret = new FloatValueAVM2Item(null, -(Double) s.value); - existsRemainder = true; - } else if (s.isType(SymbolType.INTEGER)) { - ret = new IntegerValueAVM2Item(null, -(Long) s.value); - existsRemainder = true; - } else { - lexer.pushback(s); - GraphTargetItem num = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - if (num instanceof IntegerValueAVM2Item) { - ((IntegerValueAVM2Item) num).value = -((IntegerValueAVM2Item) num).value; - ret = num; - } else if (num instanceof FloatValueAVM2Item) { - Double d = ((FloatValueAVM2Item) num).value; - if (d.isInfinite()) { - ((FloatValueAVM2Item) num).value = Double.NEGATIVE_INFINITY; - } else { - ((FloatValueAVM2Item) num).value = -d; - } - ret = (num); - } else { - ret = (new SubtractAVM2Item(null, new IntegerValueAVM2Item(null, 0L), num)); - } - } - break; - case TYPEOF: - ret = new TypeOfAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - existsRemainder = true; - break; - case TRUE: - ret = new BooleanAVM2Item(null, true); - existsRemainder = true; - break; - case NULL: - ret = new NullAVM2Item(null); - existsRemainder = true; - break; - case UNDEFINED: - ret = new UndefinedAVM2Item(null); - break; - case FALSE: - ret = new BooleanAVM2Item(null, false); - existsRemainder = true; - break; - case CURLY_OPEN: //Object literal - s = lex(); - List nvs = new ArrayList<>(); - - while (s.type != SymbolType.CURLY_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); - - GraphTargetItem n = new StringAVM2Item(null, s.value.toString()); -//expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); - expectedType(SymbolType.COLON); - GraphTargetItem v = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); - - NameValuePair nv = new NameValuePair(n, v); - nvs.add(nv); - s = lex(); - if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); - } - } - ret = new NewObjectAVM2Item(null, nvs); - ret = memberOrCall(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - break; - case BRACKET_OPEN: //Array literal or just brackets - lexer.pushback(s); - List inBrackets = new ArrayList<>(); - int arrCnt = brackets(pkg,needsActivation, importedClasses, openedNamespaces, inBrackets, registerVars, inFunction, inMethod, variables); - ret = new NewArrayAVM2Item(null, inBrackets); - ret = memberOrCall(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - - break; - case FUNCTION: - s = lexer.lex(); - String fname = ""; - if (s.isType(SymbolType.IDENTIFIER)) { - fname = s.value.toString(); - } else { - lexer.pushback(s); - } - needsActivation.setVal(true); - ret = function(pkg,false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, fname, false, variables); - break; - case NAN: - ret = new NanAVM2Item(null); - existsRemainder = true; - break; - case INFINITY: - ret = new FloatValueAVM2Item(null, Double.POSITIVE_INFINITY); - existsRemainder = true; - break; - case INTEGER: - ret = new IntegerValueAVM2Item(null, (Long) s.value); - existsRemainder = true; - break; - case DOUBLE: - ret = new FloatValueAVM2Item(null, (Double) s.value); - existsRemainder = true; - break; - case DELETE: - GraphTargetItem varDel = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); - if (!isNameOrProp(varDel)) { - throw new ParseException("Not a property or name", lexer.yyline()); - } - ret = new DeletePropertyAVM2Item(varDel, lexer.yyline()); - break; - case INCREMENT: - case DECREMENT: //preincrement - GraphTargetItem varincdec = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false/*?*/, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); - if (!isNameOrProp(varincdec)) { - throw new ParseException("Not a property or name", lexer.yyline()); - } - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementAVM2Item(null, varincdec); - } - if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementAVM2Item(null, varincdec); - } - existsRemainder = true; - break; - case NOT: - ret = new NotItem(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); - existsRemainder = true; - break; - case PARENT_OPEN: - ret = new ParenthesisItem(null, expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(pkg,needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); - existsRemainder = true; - break; - case NEW: - s = lex(); - if (s.type == SymbolType.XML_STARTTAG_BEGIN) { - lexer.yypushbackstr(s.value.toString().substring(1), ActionScriptLexer.YYINITIAL); - s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.LOWER_THAN); - } - if (s.type == SymbolType.FUNCTION) { - s = lexer.lex(); - String ffname = ""; - if (s.isType(SymbolType.IDENTIFIER)) { - ffname = s.value.toString(); - } else { - lexer.pushback(s); - } - needsActivation.setVal(true); - ret = function(pkg,false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, ffname, false, variables); - } else if (s.type == SymbolType.LOWER_THAN) { - GraphTargetItem subtype = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); - expectedType(SymbolType.GREATER_THAN); - s = lex(); - expected(s, lexer.yyline(), SymbolType.BRACKET_OPEN); - lexer.pushback(s); - List params = new ArrayList<>(); - brackets(pkg,needsActivation, importedClasses, openedNamespaces, params, registerVars, inFunction, inMethod, variables); - ret = new InitVectorAVM2Item(subtype, params, openedNamespaces); - } else if (s.type == SymbolType.PARENT_OPEN) { - GraphTargetItem newvar = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); - newvar = applyType(pkg,needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.PARENT_OPEN); - ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - - } else { - lexer.pushback(s); - GraphTargetItem newvar = name(pkg,needsActivation, false /*?*/, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); - newvar = applyType(pkg,needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); - expectedType(SymbolType.PARENT_OPEN); - ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); - } - existsRemainder = true; - break; - case IDENTIFIER: - case THIS: - case SUPER: - case ATTRIBUTE: - lexer.pushback(s); - GraphTargetItem var = name(pkg,needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); - var = memberOrCall(pkg,needsActivation, importedClasses, openedNamespaces, var, registerVars, inFunction, inMethod, variables); - ret = var; - existsRemainder = true; - break; - default: - GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1, variables); - if (excmd != null) { - existsRemainder = true; //? - ret = excmd; - break; - } - lexer.pushback(s); - } - if (allowRemainder && existsRemainder) { - GraphTargetItem rem = ret; - do { - rem = expressionRemainder(pkg,needsActivation, openedNamespaces, rem, registerVars, inFunction, inMethod, assocRight, variables, importedClasses); - if (rem != null) { - ret = rem; - } - } while ((!assocRight) && (rem != null)); - } - if (debugMode) { - System.out.println("/expression"); - } - return ret; - } - - private ActionScriptLexer lexer = null; - private List constantPool; - - private PackageAVM2Item parsePackage(List openedNamespaces) throws IOException, ParseException, CompilationException { - List items = new ArrayList<>(); - expectedType(SymbolType.PACKAGE); - String name = ""; - ParsedSymbol s = lex(); - if (s.type != SymbolType.CURLY_OPEN) { - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - name = s.value.toString(); - s = lex(); - } - while (s.type != SymbolType.CURLY_OPEN) { - expected(s, lexer.yyline(), SymbolType.DOT); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - name += "." + s.value.toString(); - s = lex(); - } - - - List importedClasses = new ArrayList<>(); - - s = lex(); - while (s.type == SymbolType.IMPORT) { - String impPackage = ""; - String impName = null; - boolean all = false; - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - impName = s.value.toString(); - s = lex(); - while (s.type == SymbolType.DOT) { - if (!"".equals(impPackage)) { - impPackage += "."; - } - impPackage += impName; - - s = lex(); - if (s.type == SymbolType.MULTIPLY) { - impName = null; - s = lex(); - break; - } - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - - impName = s.value.toString(); - s = lex(); - } - - if (impName == null) { - openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(impPackage, true)), 0, true)); - } else { - importedClasses.add(impPackage + "." + impName); - } - - expected(s, lexer.yyline(), SymbolType.SEMICOLON); - s = lex(); - } - lexer.pushback(s); - - int publicNs; - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(name, true)), 0, true)); - - //traits(false, new ArrayList(), new Reference(false), new ArrayList(), importedClasses, privateNs, 0, publicNs, packageInternalNs, 0, openedNamespaces, name, null, false, items); - //expectedType(SymbolType.CURLY_CLOSE); - return new PackageAVM2Item(publicNs,importedClasses,name, items); - } - - private List parseScript(String fileName) throws IOException, ParseException, CompilationException { - - List openedNamespaces = new ArrayList<>(); - - int scriptPrivateNs = 0; - - if (fileName.contains("/")) { - fileName = fileName.substring(fileName.lastIndexOf('/') + 1); - } - if (fileName.contains("\\")) { - fileName = fileName.substring(fileName.lastIndexOf('\\') + 1); - } - String className = fileName; - if (className.endsWith(".as")) { - className = className.substring(0, className.length() - 3); - } - openedNamespaces.add(scriptPrivateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(name + ":" + className, true) - - int publicNs; - openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)),0,true)); - - List items = new ArrayList<>(); - traits(fileName,true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); - return items; - } - - public List scriptTraitsFromString(String str, String fileName) throws ParseException, IOException, CompilationException { - lexer = new ActionScriptLexer(str); - - List ret = parseScript(fileName); - if (lexer.lex().type != SymbolType.EOF) { - throw new ParseException("Parsing finisned before end of the file", lexer.yyline()); - } - return ret; - } - - public void addScriptFromTree(List items, boolean documentClass) throws ParseException, CompilationException { - AVM2SourceGenerator gen = new AVM2SourceGenerator(abc, otherABCs); - List ret = new ArrayList<>(); - SourceGeneratorLocalData localData = new SourceGeneratorLocalData( - new HashMap(), 0, Boolean.FALSE, 0); - localData.documentClass = documentClass; - abc.script_info.add(gen.generateScriptInfo(localData, items)); - } - - public void addScript(String s, boolean documentClass, String fileName) throws ParseException, IOException, CompilationException { - List traits = scriptTraitsFromString(s, fileName); - addScriptFromTree(traits, documentClass); - } - - public ActionScriptParser(ABC abc, List otherABCs) { - this.abc = abc; - this.otherABCs = otherABCs; - } - - private static void initPlayer() throws IOException, InterruptedException { - if (playerABCs.isEmpty()) { - SWC swc = new SWC(new FileInputStream(Configuration.getPlayerSWC())); - SWF swf = new SWF(swc.getSWF("library.swf"), true); - for (Tag t : swf.tags) { - if (t instanceof ABCContainerTag) { - playerABCs.add(((ABCContainerTag) t).getABC()); - } - } - } - } - - public static void compile(String src, ABC abc, List otherABCs, boolean documentClass, String fileName) throws ParseException, IOException, InterruptedException, CompilationException { - List parABCs = new ArrayList<>(); - initPlayer(); - parABCs.addAll(playerABCs); - parABCs.addAll(otherABCs); - ActionScriptParser parser = new ActionScriptParser(abc, parABCs); - parser.addScript(src, documentClass, fileName); - } - - public static void compile(SWF swf, String src, String dst) { - System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); - try { - initPlayer(); - ABC abc = new ABC(swf); - ActionScriptParser parser = new ActionScriptParser(abc, playerABCs); - parser.addScript(new String(Helper.readFile(src), "UTF-8"), true, src); - abc.saveToStream(new FileOutputStream(new File(dst))); - } catch (Exception ex) { - Logger.getLogger(ActionScriptParser.class.getName()).log(Level.SEVERE, null, ex); - } - System.exit(0); - } - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SWC; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.BooleanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ConstructSuperAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.DefaultXMLNamespace; +import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXAttrAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.EscapeXElemAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.FloatValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetDescendantsAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.GetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InitVectorAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.LocalRegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; +import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.PostIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ReturnVoidAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForInAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.clauses.TryAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.AddAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.AsTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitAndAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitOrAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.BitXorAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.DeletePropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.DivideAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.EqAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.GeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.GtAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.InstanceOfAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.IsTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.LShiftAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.LeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.LtAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.ModuloAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.MultiplyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.NegAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.NeqAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreDecrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.PreIncrementAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.RShiftAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictEqAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.StrictNeqAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.SubtractAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.TypeOfAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.operations.URShiftAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.Loop; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BinaryOp; +import com.jpexs.decompiler.graph.model.BlockItem; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.DoWhileItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +import com.jpexs.decompiler.graph.model.ParenthesisItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import com.jpexs.helpers.Helper; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class ActionScriptParser { + + private long uniqLast = 0; + private final boolean debugMode = false; + private static final String AS3_NAMESPACE = "http://adobe.com/AS3/2006/builtin"; + + private ABC abc; + private List otherABCs; + + private static List playerABCs = new ArrayList<>(); + + private long uniqId() { + uniqLast++; + return uniqLast; + } + + private List commands(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { + List ret = new ArrayList<>(); + if (debugMode) { + System.out.println("commands:"); + } + GraphTargetItem cmd = null; + while ((cmd = command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)) != null) { + ret.add(cmd); + } + if (debugMode) { + System.out.println("/commands"); + } + return ret; + } + + private GraphTargetItem type(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List variables) throws IOException, ParseException { + ParsedSymbol s = lex(); + if (s.type == SymbolType.MULTIPLY) { + return new UnboundedTypeItem(); + } else if (s.type == SymbolType.VOID) { + return new TypeItem("void"); + } else { + lexer.pushback(s); + } + + GraphTargetItem t = name(pkg, needsActivation, true, openedNamespaces, null, false, false, variables, importedClasses); + t = applyType(pkg, needsActivation, importedClasses, openedNamespaces, t, new HashMap(), false, false, variables); + return t; + } + + private GraphTargetItem memberOrCall(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + ParsedSymbol s = lex(); + GraphTargetItem ret = newcmds; + while (s.isType(SymbolType.DOT, SymbolType.PARENT_OPEN, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) { + switch (s.type) { + case BRACKET_OPEN: + case DOT: + case TYPENAME: + lexer.pushback(s); + ret = member(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + break; + case PARENT_OPEN: + ret = new CallAVM2Item(lexer.yyline(), ret, call(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + break; + + } + s = lex(); + } + if (s.type == SymbolType.INCREMENT) { + if (!isNameOrProp(ret)) { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostIncrementAVM2Item(null, ret); + s = lex(); + + } else if (s.type == SymbolType.DECREMENT) { + if (!isNameOrProp(ret)) { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostDecrementAVM2Item(null, ret); + s = lex(); + } + + lexer.pushback(s); + return ret; + } + + private GraphTargetItem applyType(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + GraphTargetItem ret = obj; + ParsedSymbol s = lex(); + if (s.type == SymbolType.TYPENAME) { + List params = new ArrayList<>(); + do { + params.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + s = lex(); + } while (s.type == SymbolType.COMMA); + if (s.type == SymbolType.USHIFT_RIGHT) { + s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); + lexer.pushback(s); + lexer.pushback(s); + } + if (s.type == SymbolType.SHIFT_RIGHT) { + s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN); + lexer.pushback(s); + } + expected(s, lexer.yyline(), SymbolType.GREATER_THAN); + ret = new ApplyTypeAVM2Item(null, ret, params); + } else { + lexer.pushback(s); + } + return ret; + } + + private GraphTargetItem member(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + GraphTargetItem ret = obj; + ParsedSymbol s = lex(); + while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.TYPENAME)) { + ParsedSymbol s2 = lex(); + boolean attr = false; + if (s.type == SymbolType.DOT) { + if (s2.type == SymbolType.ATTRIBUTE) { + attr = true; + s = lex(); + } else { + lexer.pushback(s2); + } + + } else { + lexer.pushback(s2); + } + if (s.type == SymbolType.TYPENAME) { + lexer.pushback(s); + ret = applyType(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + s = lex(); + } else if (s.type == SymbolType.BRACKET_OPEN) { + GraphTargetItem index = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.BRACKET_CLOSE); + ret = new IndexAVM2Item(attr, ret, index, null, openedNamespaces); + s = lex(); + } else { + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String propName = s.value.toString(); + GraphTargetItem propItem = null; + s = lex(); + GraphTargetItem ns = null; + if (s.type == SymbolType.NAMESPACE_OP) { + ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, false, null, lexer.yyline(), propName, null, openedNamespaces); + variables.add((UnresolvedAVM2Item) ns); + s = lex(); + if (s.type == SymbolType.BRACKET_OPEN) { + propItem = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.BRACKET_CLOSE); + propName = null; + } else { + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + propName = s.value.toString(); + propItem = null; + } + } else { + lexer.pushback(s); + } + if (ns != null) { + ret = new NamespacedAVM2Item(ns, propName, propItem, ret, attr, openedNamespaces, null); + } else { + ret = new PropertyAVM2Item(ret, (attr ? "@" : "") + propName, abc, otherABCs, openedNamespaces, new ArrayList()); + } + s = lex(); + } + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem name(String pkg, Reference needsActivation, boolean typeOnly, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables, List importedClasses) throws IOException, ParseException { + ParsedSymbol s = lex(); + String name = ""; + if (s.type == SymbolType.ATTRIBUTE) { + name += "@"; + s = lex(); + } + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); + name += s.value.toString(); + s = lex(); + boolean attrBracket = false; + + while (s.isType(SymbolType.DOT)) { + name += s.value.toString(); //. or :: + s = lex(); + if (s.type == SymbolType.ATTRIBUTE) { + name += "@"; + s = lex(); + if (s.type == SymbolType.MULTIPLY) { + name += s.value.toString(); + } else if (s.type == SymbolType.IDENTIFIER) { + name += s.value.toString(); + } else { + if (s.type != SymbolType.BRACKET_OPEN) { + throw new ParseException("Attribute identifier or bracket expected", lexer.yyline()); + } + attrBracket = true; + continue; + } + } else { + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.NAMESPACE); + name += s.value.toString(); + } + s = lex(); + } + String nsname = null; + String nsprop = null; + GraphTargetItem nspropItem = null; + if (s.type == SymbolType.NAMESPACE_OP) { + if (name.contains(".")) { + nsname = name.substring(name.lastIndexOf('.') + 1); + } else { + nsname = name; + } + s = lex(); + if (s.type == SymbolType.IDENTIFIER) { + nsprop = s.value.toString(); + } else if (s.type == SymbolType.BRACKET_OPEN) { + nspropItem = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.BRACKET_CLOSE); + } + if (name.contains(".")) { + name = name.substring(0, name.lastIndexOf('.')); + } else { + name = null; + } + s = lex(); + } + /* + List params = new ArrayList<>(); + if (s.type == SymbolType.TYPENAME) { + s = lex(); + do { + String p = ""; + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + p = s.value.toString(); + s = lex(); + while (s.type == SymbolType.DOT) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + name += "." + s.value.toString(); + s = lex(); + } + params.add(p); + } while (s.type == SymbolType.COMMA); + expected(s, lexer.yyline(), SymbolType.GREATER_THAN); + s = lex(); + }*/ + + GraphTargetItem ret = null; + if (name != null) { + UnresolvedAVM2Item unr = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), name, null, openedNamespaces); + //unr.setIndex(index); + variables.add(unr); + ret = unr; + } + if (nsname != null) { + boolean attr = nsname.startsWith("@"); + if (attr) { + nsname = nsname.substring(1); + } + UnresolvedAVM2Item ns = new UnresolvedAVM2Item(new ArrayList(), importedClasses, typeOnly, null, lexer.yyline(), nsname, null, openedNamespaces); + variables.add(ns); + ret = new NamespacedAVM2Item(ns, nsprop, nspropItem, ret, attr, openedNamespaces, null); + } + if (s.type == SymbolType.BRACKET_OPEN) { + lexer.pushback(s); + if (attrBracket) { + lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ATTRIBUTE, "@")); + lexer.pushback(new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DOT, ".")); + } + ret = member(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + } else { + lexer.pushback(s); + } + return ret; + } + + private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ParseException { + boolean found = false; + for (Object t : expected) { + if (symb.type == t) { + found = true; + } + if (symb.group == t) { + found = true; + } + } + if (!found) { + String expStr = ""; + boolean first = true; + for (Object e : expected) { + if (!first) { + expStr += " or "; + } + expStr += e; + first = false; + } + throw new ParseException("" + expStr + " expected but " + symb.type + " found", line); + } + } + + private ParsedSymbol expectedType(Object... type) throws IOException, ParseException { + ParsedSymbol symb = lex(); + expected(symb, lexer.yyline(), type); + return symb; + } + + private ParsedSymbol lex() throws IOException, ParseException { + ParsedSymbol ret = lexer.lex(); + if (debugMode) { + System.out.println(ret); + } + return ret; + } + + private List call(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + List ret = new ArrayList<>(); + //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER + ParsedSymbol s = lex(); + while (s.type != SymbolType.PARENT_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + ret.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + s = lex(); + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + return ret; + } + + private MethodAVM2Item method(String pkg, boolean isInterface, String customAccess, Reference needsActivation, List importedClasses, boolean override, boolean isFinal, GraphTargetItem thisType, List openedNamespaces, boolean isStatic, int namespace, String functionName, boolean isMethod, List variables) throws IOException, ParseException { + FunctionAVM2Item f = function(pkg, isInterface, needsActivation, importedClasses, namespace, thisType, openedNamespaces, functionName, isMethod, variables); + return new MethodAVM2Item(f.pkg, f.isInterface, customAccess, f.needsActivation, f.hasRest, f.line, override, isFinal, isStatic, f.namespace, functionName, f.paramTypes, f.paramNames, f.paramValues, f.body, f.subvariables, f.retType); + } + + private FunctionAVM2Item function(String pkg, boolean isInterface, Reference needsActivation, List importedClasses, int namespace, GraphTargetItem thisType, List openedNamespaces, String functionName, boolean isMethod, List variables) throws IOException, ParseException { + openedNamespaces = new ArrayList<>(openedNamespaces); //local copy + int line = lexer.yyline(); + ParsedSymbol s; + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + List paramNames = new ArrayList<>(); + List paramTypes = new ArrayList<>(); + List paramValues = new ArrayList<>(); + boolean hasRest = false; + while (s.type != SymbolType.PARENT_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + if (s.type == SymbolType.REST) { + hasRest = true; + s = lex(); + } + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + + paramNames.add(s.value.toString()); + s = lex(); + if (!hasRest) { + if (s.type == SymbolType.COLON) { + paramTypes.add(type(pkg, needsActivation, importedClasses, openedNamespaces, variables)); + s = lex(); + } else { + paramTypes.add(new UnboundedTypeItem()); + } + if (s.type == SymbolType.ASSIGN) { + paramValues.add(expression(pkg, new Reference(false), importedClasses, openedNamespaces, null, isMethod, isMethod, isMethod, variables)); + s = lex(); + } else { + if (!paramValues.isEmpty()) { + throw new ParseException("Some of parameters do not have default values", lexer.yyline()); + } + } + } + + if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + if (hasRest) { + expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE); + } + } + s = lex(); + GraphTargetItem retType; + if (s.type == SymbolType.COLON) { + retType = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + } else { + retType = new UnboundedTypeItem(); + lexer.pushback(s); + } + List body = null; + List subvariables = new ArrayList<>(); + subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "this", null, true, openedNamespaces)); + for (int i = 0; i < paramNames.size() - (hasRest ? 1 : 0); i++) { + subvariables.add(new NameAVM2Item(paramTypes.get(i), lexer.yyline(), paramNames.get(i), null, true, openedNamespaces)); + } + if (hasRest) { + subvariables.add(new NameAVM2Item(TypeItem.UNBOUNDED, lexer.yyline(), paramNames.get(paramNames.size() - 1), null, true, openedNamespaces)); + } + subvariables.add(new NameAVM2Item(thisType, lexer.yyline(), "arguments", null, true, openedNamespaces)); + int parCnt = subvariables.size(); + Reference needsActivation2 = new Reference<>(false); + if (!isInterface) { + expectedType(SymbolType.CURLY_OPEN); + body = commands(pkg, needsActivation2, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, isMethod, 0, subvariables); + expectedType(SymbolType.CURLY_CLOSE); + } else { + expectedType(SymbolType.SEMICOLON); + } + + for (int i = 0; i < parCnt; i++) { + subvariables.remove(0); + } + return new FunctionAVM2Item(pkg, isInterface, needsActivation2.getVal(), namespace, hasRest, line, functionName, paramTypes, paramNames, paramValues, body, subvariables, retType); + } + + private GraphTargetItem traits(String scriptName, boolean scriptTraits, List sinitVariables, Reference sinitNeedsActivation, List staticInitializer, List importedClasses, int privateNs, int protectedNs, int publicNs, int packageInternalNs, int protectedStaticNs, List openedNamespaces, String pkg, String classNameStr, boolean isInterface, List traits) throws ParseException, IOException, CompilationException { + ParsedSymbol s; + GraphTargetItem constr = null; + TypeItem thisType = pkg == null && classNameStr == null ? null : new TypeItem(pkg == null || "".equals(pkg) ? classNameStr : pkg + "." + classNameStr); + List constrVariables = new ArrayList<>(); + List originalOpenedNamespaces = openedNamespaces; + int originalPrivateNs = privateNs; + boolean inPkg = pkg != null; + looptrait: + while (true) { + s = lex(); + boolean isStatic = false; + int namespace = -1; + boolean isGetter = false; + boolean isSetter = false; + boolean isOverride = false; + boolean isFinal = false; + boolean isDynamic = false; + String customAccess = null; + + if (scriptTraits && s.type == SymbolType.PACKAGE) { + if (inPkg) { + throw new ParseException("No subpackages allowed", lexer.yyline()); + } + openedNamespaces = new ArrayList<>(); + lexer.pushback(s); + PackageAVM2Item p = parsePackage(openedNamespaces); + pkg = p.packageName; + inPkg = true; + publicNs = p.publicNs; + importedClasses = p.importedClasses; + s = lex(); + } + if (inPkg || classNameStr != null) { + if (s.type == SymbolType.CURLY_OPEN) { + staticInitializer.addAll(commands(pkg, sinitNeedsActivation, importedClasses, openedNamespaces, new Stack(), new HashMap(), new HashMap(), true, false, 0, sinitVariables)); + expectedType(SymbolType.CURLY_CLOSE); + s = lex(); + } + + while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE, SymbolType.PROTECTED, SymbolType.OVERRIDE, SymbolType.FINAL, SymbolType.DYNAMIC, SymbolType.IDENTIFIER)) { + if (s.type == SymbolType.FINAL) { + if (isFinal) { + throw new ParseException("Only one final keyword allowed", lexer.yyline()); + } + isFinal = true; + } else if (s.type == SymbolType.DYNAMIC) { + if (isDynamic) { + throw new ParseException("Only one dynamic keyword allowed", lexer.yyline()); + } + isDynamic = true; + } else if (s.type == SymbolType.OVERRIDE) { + if (isOverride) { + throw new ParseException("Only one override keyword allowed", lexer.yyline()); + } + isOverride = true; + } else if (s.type == SymbolType.STATIC) { + if (isInterface) { + throw new ParseException("Interface cannot have static traits", lexer.yyline()); + } + if (classNameStr == null) { + throw new ParseException("No static keyword allowed here", lexer.yyline()); + } + if (isStatic) { + throw new ParseException("Only one static keyword allowed", lexer.yyline()); + } + isStatic = true; + } else if (s.type == SymbolType.IDENTIFIER) { + customAccess = s.value.toString(); + namespace = -2; + } else { + if (namespace != -1) { + throw new ParseException("Only one access identifier allowed", lexer.yyline()); + } + } + switch (s.type) { + case PUBLIC: + namespace = publicNs; + if (isInterface) { + throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); + } + break; + case PRIVATE: + namespace = privateNs; + if (isInterface) { + throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); + } + break; + case PROTECTED: + namespace = protectedNs; + if (isInterface) { + throw new ParseException("Interface cannot have public, private or protected modifier", lexer.yyline()); + } + break; + } + s = lex(); + } + } else { + namespace = privateNs; + } + if (namespace == -1) { + if (isInterface) { + namespace = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(pkg == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr, true)), 0, true); + } else { + namespace = packageInternalNs; + } + } + if (namespace == protectedNs && isStatic) { + namespace = protectedStaticNs; + } + switch (s.type) { + /*case PACKAGE: + lexer.pushback(s); + traits.add(parsePackage(openedNamespaces)); + break;*/ + case CLASS: + List subNamespaces = new ArrayList<>(openedNamespaces); + if (classNameStr != null) { + throw new ParseException("Nested classes not supported", lexer.yyline()); + } + if (isOverride) { + throw new ParseException("Override flag not allowed for classes", lexer.yyline()); + } + + //GraphTargetItem classTypeStr = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String classTypeStr = s.value.toString(); + GraphTargetItem extendsTypeStr = null; + s = lex(); + if (s.type == SymbolType.EXTENDS) { + extendsTypeStr = type(pkg, new Reference(false), importedClasses, subNamespaces, new ArrayList()); + s = lex(); + } + List implementsTypeStrs = new ArrayList<>(); + if (s.type == SymbolType.IMPLEMENTS) { + do { + GraphTargetItem implementsTypeStr = type(pkg, new Reference(false), importedClasses, subNamespaces, new ArrayList()); + implementsTypeStrs.add(implementsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + if (customAccess != null) { + throw new ParseException("Class cannot have custom namespace", lexer.yyline()); + } + traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, isDynamic, isFinal, subNamespaces, pkg, namespace, false, classTypeStr, extendsTypeStr, implementsTypeStrs, new ArrayList()))); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INTERFACE: + if (classNameStr != null) { + throw new ParseException("Nested interfaces not supported", lexer.yyline()); + } + if (isOverride) { + throw new ParseException("Override flag not allowed for interfaces", lexer.yyline()); + } + if (isFinal) { + throw new ParseException("Final flag not allowed for interfaces", lexer.yyline()); + } + if (isDynamic) { + throw new ParseException("Dynamic flag not allowed for interfaces", lexer.yyline()); + } + //GraphTargetItem interfaceTypeStr = type(pkg,needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String intTypeStr = s.value.toString(); + s = lex(); + List intExtendsTypeStrs = new ArrayList<>(); + + if (s.type == SymbolType.EXTENDS) { + do { + GraphTargetItem intExtendsTypeStr = type(pkg, new Reference(false), importedClasses, openedNamespaces, new ArrayList()); + intExtendsTypeStrs.add(intExtendsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + if (customAccess != null) { + throw new ParseException("Interface cannot have custom namespace", lexer.yyline()); + } + traits.add((classTraits(scriptName, publicNs, pkg, importedClasses, false, isFinal, openedNamespaces, pkg, namespace, true, intTypeStr, null, intExtendsTypeStrs, new ArrayList()))); + expectedType(SymbolType.CURLY_CLOSE); + break; + + case FUNCTION: + + if (isDynamic) { + throw new ParseException("Dynamic flag not allowed for methods", lexer.yyline()); + } + s = lex(); + if (s.type == SymbolType.GET) { + if (classNameStr == null) { + throw new ParseException("No get keyword allowed here", lexer.yyline()); + } + isGetter = true; + s = lex(); + } else if (s.type == SymbolType.SET) { + if (classNameStr == null) { + throw new ParseException("No set keyword allowed here", lexer.yyline()); + } + isSetter = true; + s = lex(); + } + + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String fname = s.value.toString(); + if (classNameStr != null && fname.equals(classNameStr)) { //constructor + if (isStatic) { + throw new ParseException("Constructor cannot be static", lexer.yyline()); + } + if (isStatic) { + throw new ParseException("Constructor cannot be static", lexer.yyline()); + } + if (isOverride) { + throw new ParseException("Override flag not allowed for constructor", lexer.yyline()); + } + if (isFinal) { + throw new ParseException("Final flag not allowed for constructor", lexer.yyline()); + } + if (isInterface) { + throw new ParseException("Interface cannot have constructor", lexer.yyline()); + } + constr = (method(pkg, false, customAccess, new Reference(false), importedClasses, false, false, thisType, openedNamespaces, false, namespace, "", true, constrVariables)); + } else { + MethodAVM2Item ft = method(pkg, isInterface, customAccess, new Reference(false), importedClasses, isOverride, isFinal, thisType, openedNamespaces, isStatic, namespace, fname, true, new ArrayList()); + + if (isGetter) { + if (!ft.paramTypes.isEmpty()) { + throw new ParseException("Getter can't have any parameters", lexer.yyline()); + } + } + + if (isSetter) { + if (ft.paramTypes.size() != 1) { + throw new ParseException("Getter must have exactly one parameter", lexer.yyline()); + } + } + + if (isStatic && isInterface) { + if (isInterface) { + throw new ParseException("Interface cannot have static fields", lexer.yyline()); + } + } + GraphTargetItem t; + if (isGetter) { + GetterAVM2Item g = new GetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); + t = g; + } else if (isSetter) { + SetterAVM2Item st = new SetterAVM2Item(ft.pkg, isInterface, customAccess, ft.needsActivation, ft.hasRest, ft.line, ft.isOverride(), ft.isFinal(), isStatic, ft.namespace, ft.functionName, ft.paramTypes, ft.paramNames, ft.paramValues, ft.body, ft.subvariables, ft.retType); + t = st; + } else { + t = ft; + } + + traits.add(t); + } + //} + break; + case NAMESPACE: + if (isInterface) { + throw new ParseException("Interface cannot have namespace fields", lexer.yyline()); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String nname = s.value.toString(); + String nval = ""; + s = lex(); + + if (s.type == SymbolType.ASSIGN) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.STRING); + nval = s.value.toString(); + s = lex(); + } else { + nval = (pkg == null || pkg.isEmpty() ? classNameStr : pkg + ":" + classNameStr) + "/" + nname; + } + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + + ConstAVM2Item ns = new ConstAVM2Item(pkg, customAccess, true, namespace, nname, new TypeItem("Namespace"), new StringAVM2Item(null, nval), lexer.yyline()); + traits.add(ns); + break; + case CONST: + case VAR: + boolean isConst = s.type == SymbolType.CONST; + if (isOverride) { + throw new ParseException("Override flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); + } + if (isFinal) { + throw new ParseException("Final flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); + } + if (isDynamic) { + throw new ParseException("Dynamic flag not allowed for " + (isConst ? "consts" : "vars"), lexer.yyline()); + } + if (isInterface) { + throw new ParseException("Interface cannot have variable/const fields", lexer.yyline()); + } + + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String vcname = s.value.toString(); + s = lex(); + GraphTargetItem type = null; + if (s.type == SymbolType.COLON) { + type = type(pkg, new Reference(false), importedClasses, openedNamespaces, new ArrayList()); + s = lex(); + } else { + type = TypeItem.UNBOUNDED; + } + + GraphTargetItem value = null; + + if (s.type == SymbolType.ASSIGN) { + value = expression(pkg, new Reference(false), importedClasses, openedNamespaces, new HashMap(), false, false, true, isStatic || isConst ? sinitVariables : constrVariables); + s = lex(); + } + GraphTargetItem tar; + if (isConst) { + tar = new ConstAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); + } else { + tar = new SlotAVM2Item(pkg, customAccess, isStatic, namespace, vcname, type, value, lexer.yyline()); + } + traits.add(tar); + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + break; + default: + if (s.type == SymbolType.CURLY_CLOSE && inPkg && classNameStr == null) { + inPkg = false; + pkg = null; + openedNamespaces = originalOpenedNamespaces; + privateNs = originalPrivateNs; + } else { + lexer.pushback(s); + break looptrait; + } + + } + } + return constr; + } + + private GraphTargetItem classTraits(String scriptName, int gpublicNs, String pkg, List importedClasses, boolean isDynamic, boolean isFinal, List openedNamespaces, String packageName, int namespace, boolean isInterface, String nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ParseException, CompilationException { + + GraphTargetItem ret = null; + + ParsedSymbol s = null; + List traits = new ArrayList<>(); + + String classNameStr = nameStr; + + openedNamespaces = new ArrayList<>(openedNamespaces); + + int publicNs = 0; + int privateNs = 0; + int packageInternalNs = 0; + if (pkg != null) { + openedNamespaces.add(packageInternalNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(pkg, true)), 0, true)); + } + if (pkg != null && !pkg.isEmpty()) { + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); + } else { + publicNs = gpublicNs; + } + + openedNamespaces.add(privateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(fileName + "$", true) + + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_NAMESPACE, abc.constants.getStringId(AS3_NAMESPACE, true)), 0, true)); + + //int privateNs = 0; + int protectedNs = 0; + //int publicNs = namespace; + int protectedStaticNs = 0; + + openedNamespaces.add(protectedNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PROTECTED, abc.constants.getStringId(packageName == null ? (scriptName + "$0:"/*FIXME?*/ + classNameStr) : packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)))); + openedNamespaces.add(protectedStaticNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(packageName == null || packageName.isEmpty() ? classNameStr : packageName + ":" + classNameStr, true)))); + + if (extendsStr != null) { + List indices = new ArrayList<>(); + List names = new ArrayList<>(); + List namespaces = new ArrayList<>(); + //FIXME for Private classes in script!!! + AVM2SourceGenerator.parentNamesAddNames(abc, otherABCs, AVM2SourceGenerator.resolveType(new SourceGeneratorLocalData(new HashMap(), 0, false, 0), ((TypeItem) ((UnresolvedAVM2Item) extendsStr).resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), new ArrayList())), abc, otherABCs), indices, names, namespaces); + for (int i = 0; i < names.size(); i++) { + if (namespaces.get(i).isEmpty()) { + continue; + } + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_STATIC_PROTECTED, abc.constants.getStringId(namespaces.get(i) + ":" + names.get(i), true)), 0, true)); + } + } + + Reference staticNeedsActivation = new Reference<>(false); + List staticInit = new ArrayList<>(); + List sinitVariables = new ArrayList<>(); + GraphTargetItem constr = traits(scriptName, false, sinitVariables, staticNeedsActivation, staticInit, importedClasses, privateNs, protectedNs, publicNs, packageInternalNs, protectedStaticNs, openedNamespaces, packageName, classNameStr, isInterface, traits); + + if (isInterface) { + return new InterfaceAVM2Item(importedClasses, packageName, openedNamespaces, isFinal, namespace, classNameStr, implementsStr, traits); + } else { + return new ClassAVM2Item(importedClasses, packageName, openedNamespaces, protectedNs, isDynamic, isFinal, namespace, classNameStr, extendsStr, implementsStr, staticInit, staticNeedsActivation.getVal(), sinitVariables, constr, traits); + } + } + + private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + switch (s.type) { + /*case INT: + expectedType(SymbolType.PARENT_OPEN); + ret = new ToIntegerAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case NUMBER_OP: + s = lex(); + if (s.type == SymbolType.DOT) { + VariableAVM2Item vi = new VariableAVM2Item(s.value.toString(), null, false); + variables.add(vi); + ret = memberOrCall(vi, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToNumberAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + } + break; + case STRING_OP: + ParsedSymbol sop = s; + s = lex(); + if (s.type == SymbolType.DOT) { + lexer.pushback(s); + VariableAVM2Item vi2 = new VariableAVM2Item(sop.value.toString(), null, false); + variables.add(vi2); + ret = memberOrCall(vi2, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToStringAVM2Item(null, expression(pkg,needsActivation, importedClasses, openedNamespaces,openedNamespacesKinds,registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); + } + break;*/ + default: + return null; + } + //return ret; + } + + private GraphTargetItem add(Object a) { + if (a instanceof List) { + List l = (List) a; + if (l.isEmpty()) { + return null; + } + GraphTargetItem o = add(l.get(0)); + for (int i = 1; i < l.size(); i++) { + o = add(o, l.get(i)); + } + return o; + } + if (a instanceof StringBuilder) { + if (((StringBuilder) a).length() == 0) { + return null; + } + GraphTargetItem ret = new StringAVM2Item(null, a.toString()); + ((StringBuilder) a).setLength(0); + return ret; + } + if (a instanceof String) { + return new StringAVM2Item(null, (String) a); + } + if (a instanceof GraphTargetItem) { + return (GraphTargetItem) a; + } + return null; + } + + private GraphTargetItem add(Object a, Object b) { + GraphTargetItem ta = add(a); + GraphTargetItem tb = add(b); + if (ta == null && tb == null) { + return null; + } + if (ta == null) { + return tb; + } + if (tb == null) { + return ta; + } + return new AddAVM2Item(null, ta, tb); + } + + private void addS(List rets, StringBuilder sb) { + if (sb.length() > 0) { + if (!rets.isEmpty() && (rets.get(rets.size() - 1) instanceof StringAVM2Item)) { + ((StringAVM2Item) rets.get(rets.size() - 1)).value += sb.toString(); + } else { + rets.add(new StringAVM2Item(null, sb.toString())); + } + sb.setLength(0); + } + } + + private List xmltag(String pkg, Reference usesVars, List openedTags, Reference closedVarTags, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + ParsedSymbol s = null; + List rets = new ArrayList<>(); + //GraphTargetItem ret = null; + StringBuilder sb = new StringBuilder(); + loop: + do { + s = lex(); + List sub = new ArrayList<>(); + Reference subclose = new Reference<>(0); + Reference subusesvars = new Reference<>(false); + switch (s.type) { + case XML_ATTRNAMEVAR_BEGIN: //add + usesVars.setVal(true); + addS(rets, sb); + rets.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.CURLY_CLOSE); + expectedType(SymbolType.ASSIGN); + sb.append("="); + lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); + break; + case XML_ATTRVALVAR_BEGIN: //esc_xattr + usesVars.setVal(true); + sb.append("\""); + addS(rets, sb); + rets.add(new EscapeXAttrAVM2Item(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); + sb.append("\""); + expectedType(SymbolType.CURLY_CLOSE); + lexer.yybegin(ActionScriptLexer.XMLOPENTAG); + break; + case XML_INSTRATTRNAMEVAR_BEGIN: //add + usesVars.setVal(true); + addS(rets, sb); + rets.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.CURLY_CLOSE); + expectedType(SymbolType.ASSIGN); + sb.append("="); + lexer.yybegin(ActionScriptLexer.XMLOPENTAGATTRIB); + break; + case XML_INSTRATTRVALVAR_BEGIN: //esc_xattr + usesVars.setVal(true); + sb.append("\""); + addS(rets, sb); + rets.add(new EscapeXAttrAVM2Item(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); + sb.append("\""); + expectedType(SymbolType.CURLY_CLOSE); + lexer.yybegin(ActionScriptLexer.XMLOPENTAG); + break; + case XML_VAR_BEGIN: //esc_xelem + usesVars.setVal(true); + addS(rets, sb); + rets.add(new EscapeXElemAVM2Item(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.CURLY_CLOSE); + lexer.yybegin(ActionScriptLexer.XML); + break; + case XML_FINISHVARTAG_BEGIN: //add + usesVars.setVal(true); + closedVarTags.setVal(closedVarTags.getVal() + 1); + sb.append(""); + addS(rets, sb); + lexer.yybegin(ActionScriptLexer.XML); + break; + case XML_STARTVARTAG_BEGIN: //add + //openedTags.add("*"); + + //ret = add(ret, ); + GraphTargetItem ex = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.CURLY_CLOSE); + lexer.yybegin(ActionScriptLexer.XMLOPENTAG); + sub.add("*"); + sb.append("<"); + addS(rets, sb); + rets.add(ex); + rets.addAll(xmltag(pkg, subusesvars, sub, subclose, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + closedVarTags.setVal(subclose.getVal() + subclose.getVal()); + break; + case XML_INSTRVARTAG_BEGIN: //add + usesVars.setVal(true); + addS(rets, sb); + sb.append(" st = xmltag(pkg, subusesvars, sub, closedVarTags, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); + sb.append(s.value.toString()); + addS(rets, sb); + rets.addAll(st); + closedVarTags.setVal(subclose.getVal() + subclose.getVal()); + break; + /*case XML_STARTTAG_END: + sb.append(s.value.toString()); + ret = addstr(ret,sb); + break;*/ + case XML_FINISHTAG: + String tname = s.value.toString().substring(2, s.value.toString().length() - 1).trim(); + if (openedTags.contains(tname)) { + openedTags.remove(tname); + } else if (openedTags.contains("*")) { + openedTags.remove("*"); + } else { + throw new ParseException("XML : Closing unopened tag", lexer.yyline()); + } + sb.append(s.value.toString()); + break; + case XML_STARTFINISHTAG_END: + openedTags.remove(openedTags.size() - 1); //close last tag + sb.append(s.value.toString()); + break; + case EOF: + throw new ParseException("End of file before XML finish", lexer.yyline()); + default: + sb.append(s.value.toString()); + break; + } + } while (!(openedTags.isEmpty() || closedVarTags.getVal() >= openedTags.size())); + addS(rets, sb); + return rets; + } + + private GraphTargetItem xml(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + List openedTags = new ArrayList<>(); + int closedVarTags = 0; + + GraphTargetItem ret = add(xmltag(pkg, new Reference(false), openedTags, new Reference(closedVarTags), needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + ret = new XMLAVM2Item(ret); + lexer.yybegin(ActionScriptLexer.YYINITIAL); + //TODO: Order of additions as in official compiler + return ret; + } + + private GraphTargetItem command(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ParseException { + LexBufferer buf = new LexBufferer(); + lexer.addListener(buf); + GraphTargetItem ret = null; + if (debugMode) { + System.out.println("command:"); + } + ParsedSymbol s = lex(); + if (s.type == SymbolType.EOF) { + return null; + } + String loopLabel = null; + + if (s.type == SymbolType.IDENTIFIER) { + ParsedSymbol sc = lex(); + if (sc.type == SymbolType.COLON) { + loopLabel = s.value.toString(); + s = lex(); + } else { + lexer.pushback(sc); + } + } + + if (s.type == SymbolType.DEFAULT) { + ParsedSymbol sx = lex(); + if (sx.type != SymbolType.IDENTIFIER) { + lexer.pushback(sx); + } else { + if (!sx.value.equals("xml")) { + lexer.pushback(sx); + } else { + expectedType(SymbolType.NAMESPACE); + expectedType(SymbolType.ASSIGN); + GraphTargetItem ns = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + ret = new DefaultXMLNamespace(null, ns); + //TODO: use dxns for attribute namespaces instead of dxnslate + } + } + } + if (ret == null) { + switch (s.type) { + case USE: + expectedType(SymbolType.NAMESPACE); + GraphTargetItem ns = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE /*FIXME?*/, abc.constants.getStringId(ns.toString(), true)), 0, true)); + break; + case WITH: + needsActivation.setVal(true); + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem wvar = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//(name(false, openedNamespaces, registerVars, inFunction, inMethod, variables)); + if (!isNameOrProp(wvar)) { + throw new ParseException("Not a property or name", lexer.yyline()); + } + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List withVars = new ArrayList<>(); + List wcmd = commands(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, withVars); + variables.addAll(withVars); + for (AssignableAVM2Item a : withVars) { + if (a instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item ua = (UnresolvedAVM2Item) a; + ua.scopeStack.add(0, wvar); + } + } + expectedType(SymbolType.CURLY_CLOSE); + ret = new WithAVM2Item(null, wvar, wcmd); + ((WithAVM2Item) ret).subvariables = withVars; + break; + /*case DELETE: + GraphTargetItem varDel = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); + if(!isNameOrProp(varDel)){ + throw new ParseException("Not a property or name", lexer.yyline()); + } + if (varDel instanceof GetPropertyAVM2Item) { + GetPropertyAVM2Item gm = (GetPropertyAVM2Item) varDel; + ret = new DeletePropertyAVM2Item(null, gm.object, gm.propertyName); + } else if (varDel instanceof NameAVM2Item) { + variables.remove(varDel); + ret = new DeletePropertyAVM2Item(null, null, (NameAVM2Item) varDel); + } else { + throw new ParseException("Not a property", lexer.yyline()); + } + break;*/ + case FUNCTION: + s = lexer.lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + needsActivation.setVal(true); + ret = (function(pkg, false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, s.value.toString(), false, variables)); + break; + case VAR: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String varIdentifier = s.value.toString(); + s = lex(); + GraphTargetItem type; + if (s.type == SymbolType.COLON) { + type = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + s = lex(); + } else { + type = new UnboundedTypeItem(); + } + + if (s.type == SymbolType.ASSIGN) { + GraphTargetItem varval = (expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, varval, true, openedNamespaces); + variables.add((NameAVM2Item) ret); + } else { + ret = new NameAVM2Item(type, lexer.yyline(), varIdentifier, null, true, openedNamespaces); + variables.add((NameAVM2Item) ret); + lexer.pushback(s); + } + break; + case CURLY_OPEN: + ret = new BlockItem(null, commands(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + /*case INCREMENT: //preincrement + case DECREMENT: //predecrement + GraphTargetItem varincdec = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); + if(!isNameOrProp(varincdec)){ + throw new ParseException("Not a property or name", lexer.yyline()); + } + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementAVM2Item(null, varincdec); + } else if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementAVM2Item(null, varincdec); + } + break;*/ + case SUPER: //constructor call + ParsedSymbol ss2 = lex(); + if (ss2.type == SymbolType.PARENT_OPEN) { + List args = call(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); + ret = new ConstructSuperAVM2Item(null, new LocalRegAVM2Item(null, 0, null), args); + } else {//no costructor call, but it could be calling parent methods... => handle in expression + lexer.pushback(ss2); + lexer.pushback(s); + } + break; + case IF: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem ifExpr = (expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + GraphTargetItem onTrue = command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); + List onTrueList = new ArrayList<>(); + onTrueList.add(onTrue); + s = lex(); + List onFalseList = null; + if (s.type == SymbolType.ELSE) { + onFalseList = new ArrayList<>(); + onFalseList.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + } else { + lexer.pushback(s); + } + ret = new IfItem(null, ifExpr, onTrueList, onFalseList); + break; + case WHILE: + expectedType(SymbolType.PARENT_OPEN); + List whileExpr = new ArrayList<>(); + whileExpr.add(commaExpression(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + List whileBody = new ArrayList<>(); + Loop wloop = new Loop(uniqId(), null, null); + if (loopLabel != null) { + loopLabels.put(wloop, loopLabel); + } + loops.push(wloop); + whileBody.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + ret = new WhileItem(null, wloop, whileExpr, whileBody); + break; + case DO: + List doBody = new ArrayList<>(); + Loop dloop = new Loop(uniqId(), null, null); + loops.push(dloop); + if (loopLabel != null) { + loopLabels.put(dloop, loopLabel); + } + doBody.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + expectedType(SymbolType.WHILE); + expectedType(SymbolType.PARENT_OPEN); + List doExpr = new ArrayList<>(); + doExpr.add(commaExpression(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new DoWhileItem(null, dloop, doBody, doExpr); + break; + case FOR: + s = lex(); + boolean forin = false; + boolean each = false; + GraphTargetItem collection = null; + if (s.type == SymbolType.EACH) { + each = true; + forin = true; + s = lex(); + } + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + GraphTargetItem firstCommand = command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, false, variables); + if (firstCommand instanceof NameAVM2Item) { + NameAVM2Item nai = (NameAVM2Item) firstCommand; + if (nai.isDefinition() && nai.getAssignedValue() == null) { + firstCommand = expressionRemainder(pkg, needsActivation, openedNamespaces, firstCommand, registerVars, inFunction, inMethod, true, variables, importedClasses); + } + } + InAVM2Item inexpr = null; + if (firstCommand instanceof InAVM2Item) { + forin = true; + inexpr = (InAVM2Item) firstCommand; + } else { + if (forin) { + throw new ParseException("In expression required", lexer.yyline()); + } + } + + Loop floop = new Loop(uniqId(), null, null); + loops.push(floop); + if (loopLabel != null) { + loopLabels.put(floop, loopLabel); + } + List forFinalCommands = new ArrayList<>(); + GraphTargetItem forExpr = null; + List forFirstCommands = new ArrayList<>(); + if (!forin) { + //GraphTargetItem firstCommand = command(pkg,needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables); + if (firstCommand != null) { //can be empty command + forFirstCommands.add(firstCommand); + } + forExpr = (expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.SEMICOLON); + forFinalCommands.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + } + expectedType(SymbolType.PARENT_CLOSE); + List forBody = new ArrayList<>(); + forBody.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); + if (forin) { + if (each) { + ret = new ForEachInAVM2Item(null, floop, inexpr, forBody); + } else { + + ret = new ForInAVM2Item(null, floop, inexpr, forBody); + } + } else { + ret = new ForItem(null, floop, forFirstCommands, forExpr, forFinalCommands, forBody); + } + break; + case SWITCH: + Loop sloop = new Loop(-uniqId(), null, null); //negative id marks switch = no continue + loops.push(sloop); + if (loopLabel != null) { + loopLabels.put(sloop, loopLabel); + } + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem switchExpr = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + s = lex(); + //ret.addAll(switchExpr); + int exprReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + registerVars.put("__switch" + uniqId(), i); + exprReg = i; + break; + } + } + List> caseIfs = new ArrayList<>(); + List> caseCmds = new ArrayList<>(); + List caseExprsAll = new ArrayList<>(); + List valueMapping = new ArrayList<>(); + int pos = 0; + while (s.type == SymbolType.CASE) { + List caseExprs = new ArrayList<>(); + while (s.type == SymbolType.CASE) { + GraphTargetItem curCaseExpr = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + caseExprs.add(curCaseExpr); + expectedType(SymbolType.COLON); + s = lex(); + caseExprsAll.add(curCaseExpr); + valueMapping.add(pos); + } + pos++; + lexer.pushback(s); + List caseCmd = commands(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); + caseCmds.add(caseCmd); + s = lex(); + } + List defCmd = new ArrayList<>(); + if (s.type == SymbolType.DEFAULT) { + expectedType(SymbolType.COLON); + defCmd = commands(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, variables); + s = lexer.lex(); + } + expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); + ret = new SwitchItem(null, sloop, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); + break; + case BREAK: + s = lex(); + long bloopId = 0; + if (loops.isEmpty()) { + throw new ParseException("No loop to break", lexer.yyline()); + } + if (s.type == SymbolType.IDENTIFIER) { + String breakLabel = s.value.toString(); + for (Loop l : loops) { + if (breakLabel.equals(loopLabels.get(l))) { + bloopId = l.id; + break; + } + } + if (bloopId == 0) { + throw new ParseException("Identifier of loop expected", lexer.yyline()); + } + } else { + lexer.pushback(s); + bloopId = loops.peek().id; + } + ret = new BreakItem(null, bloopId); + break; + case CONTINUE: + s = lex(); + long cloopId = 0; + if (loops.isEmpty()) { + throw new ParseException("No loop to continue", lexer.yyline()); + } + if (s.type == SymbolType.IDENTIFIER) { + String continueLabel = s.value.toString(); + for (Loop l : loops) { + if (l.id < 0) { //negative id marks switch => no continue + continue; + } + if (continueLabel.equals(loopLabels.get(l))) { + cloopId = l.id; + break; + } + } + if (cloopId == -1) { + throw new ParseException("Identifier of loop expected", lexer.yyline()); + } + } else { + lexer.pushback(s); + for (int i = loops.size() - 1; i >= 0; i--) { + if (loops.get(i).id >= 0) {//no switches + cloopId = loops.get(i).id; + break; + } + } + if (cloopId <= 0) { + throw new ParseException("No loop to continue", lexer.yyline()); + } + } + //TODO: handle switch + ret = new ContinueItem(null, cloopId); + break; + case RETURN: + GraphTargetItem retexpr = expression(pkg, needsActivation, importedClasses, openedNamespaces, true, registerVars, inFunction, inMethod, true, variables); + if (retexpr == null) { + ret = new ReturnVoidAVM2Item(null); + } else { + ret = new ReturnValueAVM2Item(null, retexpr); + } + break; + case TRY: + needsActivation.setVal(true); + List tryCommands = new ArrayList<>(); + tryCommands.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + s = lex(); + boolean found = false; + List> catchCommands = new ArrayList<>(); + List catchExceptions = new ArrayList<>(); + int varCnt = variables.size(); + List> catchesVars = new ArrayList<>(); + while (s.type == SymbolType.CATCH) { + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); + + String enamestr = s.value.toString(); + expectedType(SymbolType.COLON); + GraphTargetItem etype = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + NameAVM2Item e = new NameAVM2Item(etype, lexer.yyline(), enamestr, new ExceptionAVM2Item(null)/*?*/, true/*?*/, openedNamespaces); + variables.add(e); + catchExceptions.add(e); + e.setSlotNumber(1); + e.setSlotScope(Integer.MAX_VALUE); //will be changed later + expectedType(SymbolType.PARENT_CLOSE); + List cc = new ArrayList<>(); + List catchVars = new ArrayList<>(); + cc.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, catchVars)); + catchesVars.add(catchVars); + variables.addAll(catchVars); + + for (AssignableAVM2Item a : catchVars) { + if (a instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item ui = (UnresolvedAVM2Item) a; + if (ui.getVariableName().equals(e.getVariableName())) { + try { + ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); + } catch (CompilationException ex) { + //ignore + } + ui.setSlotNumber(e.getSlotNumber()); + ui.setSlotScope(e.getSlotScope()); + } + + } + } + + catchCommands.add(cc); + s = lex(); + found = true; + } + //TODO: + for (int i = varCnt; i < variables.size(); i++) { + AssignableAVM2Item av = variables.get(i); + if (av instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item ui = (UnresolvedAVM2Item) av; + for (NameAVM2Item e : catchExceptions) { + if (ui.getVariableName().equals(e.getVariableName())) { + try { + ui.resolve(null, new ArrayList(), new ArrayList(), abc, otherABCs, new ArrayList(), variables); + } catch (CompilationException ex) { + //ignore + } + ui.setSlotNumber(e.getSlotNumber()); + ui.setSlotScope(e.getSlotScope()); + } + } + } + } + + List finallyCommands = null; + if (s.type == SymbolType.FINALLY) { + finallyCommands = new ArrayList<>(); + finallyCommands.add(command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forinlevel, true, variables)); + found = true; + s = lex(); + } + if (!found) { + expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); + } + lexer.pushback(s); + TryAVM2Item tai = new TryAVM2Item(tryCommands, null, catchCommands, finallyCommands); + tai.catchVariables = catchesVars; + tai.catchExceptions2 = catchExceptions; + ret = tai; + break; + case THROW: + ret = new ThrowAVM2Item(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + break; + default: + GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel, variables); + if (valcmd != null) { + ret = valcmd; + break; + } + if (s.type == SymbolType.SEMICOLON) { + return null; + } + lexer.pushback(s); + ret = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + if (debugMode) { + System.out.println("/command"); + } + } + } + if (debugMode) { + System.out.println("/command"); + } + lexer.removeListener(buf); + if (ret == null) { //can be popped expression + buf.pushAllBack(lexer); + ret = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + } + s = lex(); + if ((s != null) && (s.type != SymbolType.SEMICOLON)) { + lexer.pushback(s); + } + + return ret; + + } + + private GraphTargetItem expression(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { + return expression(pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, allowRemainder, variables); + } + + private GraphTargetItem fixPrecedence(GraphTargetItem expr) { + GraphTargetItem ret = expr; + if (expr instanceof BinaryOp) { + BinaryOp bo = (BinaryOp) expr; + GraphTargetItem left = bo.getLeftSide(); + //GraphTargetItem right=bo.getRightSide(); + if (left.getPrecedence() > bo.getPrecedence()) { + if (left instanceof BinaryOp) { + BinaryOp leftBo = (BinaryOp) left; + bo.setLeftSide(leftBo.getRightSide()); + leftBo.setRightSide(expr); + return left; + } + } + } + return ret; + } + + private GraphTargetItem expressionRemainder(String pkg, Reference needsActivation, List openedNamespaces, GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables, List importedClasses) throws IOException, ParseException { + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + + if (ret == null) { + switch (s.type) { + case AS: + GraphTargetItem type = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + ret = new AsTypeAVM2Item(null, expr, type); + allowRemainder = false; + break; + case DESCENDANTS: + ParsedSymbol d = lex(); + expected(d, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.MULTIPLY); + ret = new GetDescendantsAVM2Item(expr, d.type == SymbolType.MULTIPLY ? null : d.value.toString(), openedNamespaces); + allowRemainder = true; + break; + case FILTER: + needsActivation.setVal(true); + ret = new XMLFilterAVM2Item(expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables), openedNamespaces); + expectedType(SymbolType.PARENT_CLOSE); + allowRemainder = true; + break; + /*case NAMESPACE_OP: + s = lex(); + if (s.type == SymbolType.BRACKET_OPEN) { + GraphTargetItem index = expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + NameAVM2Item name = new NameAVM2Item(new UnboundedTypeItem(), lexer.yyline(), null, null, false, openedNamespaces); + name.setIndex(index); + name.setNs(expr); + ret = name; + expectedType(SymbolType.BRACKET_CLOSE); + } else { + lexer.pushback(s); + GraphTargetItem name = name(pkg,needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); + if (name instanceof UnresolvedAVM2Item) { + ((UnresolvedAVM2Item) name).setNs(expr); + //((UnresolvedAVM2Item) name).unresolved = false; + //TODO + } else { + throw new ParseException("Not a property name", lexer.yyline()); + } + ret = name; + } + break;*/ + case IN: + ret = new InAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + break; + case TERNAR: + GraphTargetItem terOnTrue = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.COLON); + GraphTargetItem terOnFalse = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + ret = new TernarOpItem(null, expr, terOnTrue, terOnFalse); + break; + case SHIFT_LEFT: + ret = new LShiftAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case SHIFT_RIGHT: + ret = new RShiftAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case USHIFT_RIGHT: + ret = new URShiftAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case BITAND: + ret = new BitAndAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case BITOR: + ret = new BitOrAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case DIVIDE: + ret = new DivideAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case MODULO: + ret = new ModuloAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case EQUALS: + ret = new EqAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case STRICT_EQUALS: + ret = new StrictEqAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case NOT_EQUAL: + ret = new NeqAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case STRICT_NOT_EQUAL: + ret = new StrictNeqAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case LOWER_THAN: + ret = new LtAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case LOWER_EQUAL: + ret = new LeAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case GREATER_THAN: + case XML_STARTVARTAG_BEGIN: + case XML_STARTTAG_BEGIN: + if (s.type != SymbolType.GREATER_THAN) { + lexer.yypushbackstr(s.value.toString().substring(1)); //parse again as GREATER_THAN + } + ret = new GtAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case GREATER_EQUAL: + ret = new GeAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case AND: + ret = new AndItem(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case OR: + ret = new OrItem(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case MINUS: + ret = new SubtractAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case MULTIPLY: + ret = new MultiplyAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case PLUS: + ret = new AddAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case XOR: + ret = new BitXorAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case INSTANCEOF: + ret = new InstanceOfAVM2Item(null, expr, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + break; + case IS: + GraphTargetItem istype = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables);//type(pkg,needsActivation, importedClasses, openedNamespaces, variables); + ret = new IsTypeAVM2Item(null, expr, istype); + allowRemainder = false; + break; + case ASSIGN: + case ASSIGN_BITAND: + case ASSIGN_BITOR: + case ASSIGN_DIVIDE: + case ASSIGN_MINUS: + case ASSIGN_MODULO: + case ASSIGN_MULTIPLY: + case ASSIGN_PLUS: + case ASSIGN_SHIFT_LEFT: + case ASSIGN_SHIFT_RIGHT: + case ASSIGN_USHIFT_RIGHT: + case ASSIGN_XOR: + GraphTargetItem assigned = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + switch (s.type) { + case ASSIGN: + //assigned = assigned; + break; + case ASSIGN_BITAND: + assigned = new BitAndAVM2Item(null, expr, assigned); + break; + case ASSIGN_BITOR: + assigned = new BitOrAVM2Item(null, expr, assigned); + break; + case ASSIGN_DIVIDE: + assigned = new DivideAVM2Item(null, expr, assigned); + break; + case ASSIGN_MINUS: + assigned = new SubtractAVM2Item(null, expr, assigned); + break; + case ASSIGN_MODULO: + assigned = new ModuloAVM2Item(null, expr, assigned); + break; + case ASSIGN_MULTIPLY: + assigned = new MultiplyAVM2Item(null, expr, assigned); + break; + case ASSIGN_PLUS: + assigned = new AddAVM2Item(null, expr, assigned); + break; + case ASSIGN_SHIFT_LEFT: + assigned = new LShiftAVM2Item(null, expr, assigned); + break; + case ASSIGN_SHIFT_RIGHT: + assigned = new RShiftAVM2Item(null, expr, assigned); + break; + case ASSIGN_USHIFT_RIGHT: + assigned = new URShiftAVM2Item(null, expr, assigned); + break; + case ASSIGN_XOR: + assigned = new BitXorAVM2Item(null, expr, assigned); + break; + } + + if (!(expr instanceof AssignableAVM2Item)) { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + AssignableAVM2Item as = ((AssignableAVM2Item) expr).copy(); + if ((as instanceof UnresolvedAVM2Item) || (as instanceof NameAVM2Item)) { + variables.add(as); + } + as.setAssignedValue(assigned); + if (expr instanceof NameAVM2Item) { + ((NameAVM2Item) expr).setDefinition(false); + } + ret = as; + break; + case DOT: //member + case BRACKET_OPEN: //member + case PARENT_OPEN: //function call + lexer.pushback(s); + ret = memberOrCall(pkg, needsActivation, importedClasses, openedNamespaces, expr, registerVars, inFunction, inMethod, variables); + break; + + default: + lexer.pushback(s); + if (expr instanceof ParenthesisItem) { + if (isType(((ParenthesisItem) expr).value)) { + GraphTargetItem expr2 = expression(pkg, needsActivation, importedClasses, openedNamespaces, false, registerVars, inFunction, inMethod, true, variables); + if (expr2 != null) { + ret = new CoerceAVM2Item(null, ((ParenthesisItem) expr).value, expr2); + } + } + } + } + } + ret = fixPrecedence(ret); + return ret; + } + + private boolean isNameOrProp(GraphTargetItem item) { + if (item instanceof UnresolvedAVM2Item) { + return true; //we don't know yet + } + if (item instanceof NameAVM2Item) { + return true; + } + if (item instanceof PropertyAVM2Item) { + return true; + } + if (item instanceof IndexAVM2Item) { + return true; + } + return false; + } + + private boolean isType(GraphTargetItem item) { + if (item == null) { + return false; + } + while (item instanceof GetPropertyAVM2Item) { + item = ((GetPropertyAVM2Item) item).object; + } + if (item instanceof NameAVM2Item) { + return true; + } + return false; + } + + private int brackets(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + ParsedSymbol s = lex(); + int arrCnt = 0; + if (s.type == SymbolType.BRACKET_OPEN) { + s = lex(); + + while (s.type != SymbolType.BRACKET_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + arrCnt++; + ret.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE); + } + } + } else { + lexer.pushback(s); + return -1; + } + return arrCnt; + } + + private GraphTargetItem commaExpression(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, Stack loops, Map loopLabels, HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ParseException { + GraphTargetItem cmd = null; + List expr = new ArrayList<>(); + ParsedSymbol s; + do { + cmd = command(pkg, needsActivation, importedClasses, openedNamespaces, loops, loopLabels, registerVars, inFunction, inMethod, forInLevel, false, variables); + if (cmd != null) { + expr.add(cmd); + } + s = lex(); + } while (s.type == SymbolType.COMMA && cmd != null); + lexer.pushback(s); + if (cmd == null) { + expr.add(expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + } else { + if (!cmd.hasReturnValue()) { + throw new ParseException("Expression expected", lexer.yyline()); + } + } + return new CommaExpressionItem(null, expr); + } + + private GraphTargetItem expression(String pkg, Reference needsActivation, List importedClasses, List openedNamespaces, boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { + if (debugMode) { + System.out.println("expression:"); + } + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + boolean existsRemainder = false; + boolean assocRight = false; + switch (s.type) { + case XML_STARTTAG_BEGIN: + lexer.pushback(s); + ret = xml(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables); + existsRemainder = true; + break; + case STRING: + ret = new StringAVM2Item(null, s.value.toString()); + existsRemainder = true; + break; + case NEGATE: + ret = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables); + ret = new NegAVM2Item(null, ret); + existsRemainder = true; + break; + case MINUS: + s = lex(); + if (s.isType(SymbolType.DOUBLE)) { + ret = new FloatValueAVM2Item(null, -(Double) s.value); + existsRemainder = true; + } else if (s.isType(SymbolType.INTEGER)) { + ret = new IntegerValueAVM2Item(null, -(Long) s.value); + existsRemainder = true; + } else { + lexer.pushback(s); + GraphTargetItem num = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + if (num instanceof IntegerValueAVM2Item) { + ((IntegerValueAVM2Item) num).value = -((IntegerValueAVM2Item) num).value; + ret = num; + } else if (num instanceof FloatValueAVM2Item) { + Double d = ((FloatValueAVM2Item) num).value; + if (d.isInfinite()) { + ((FloatValueAVM2Item) num).value = Double.NEGATIVE_INFINITY; + } else { + ((FloatValueAVM2Item) num).value = -d; + } + ret = (num); + } else { + ret = (new SubtractAVM2Item(null, new IntegerValueAVM2Item(null, 0L), num)); + } + } + break; + case TYPEOF: + ret = new TypeOfAVM2Item(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + existsRemainder = true; + break; + case TRUE: + ret = new BooleanAVM2Item(null, true); + existsRemainder = true; + break; + case NULL: + ret = new NullAVM2Item(null); + existsRemainder = true; + break; + case UNDEFINED: + ret = new UndefinedAVM2Item(null); + break; + case FALSE: + ret = new BooleanAVM2Item(null, false); + existsRemainder = true; + break; + case CURLY_OPEN: //Object literal + s = lex(); + List nvs = new ArrayList<>(); + + while (s.type != SymbolType.CURLY_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); + + GraphTargetItem n = new StringAVM2Item(null, s.value.toString()); +//expression(pkg,needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); + expectedType(SymbolType.COLON); + GraphTargetItem v = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, allowRemainder, variables); + + NameValuePair nv = new NameValuePair(n, v); + nvs.add(nv); + s = lex(); + if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); + } + } + ret = new NewObjectAVM2Item(null, nvs); + ret = memberOrCall(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + break; + case BRACKET_OPEN: //Array literal or just brackets + lexer.pushback(s); + List inBrackets = new ArrayList<>(); + int arrCnt = brackets(pkg, needsActivation, importedClasses, openedNamespaces, inBrackets, registerVars, inFunction, inMethod, variables); + ret = new NewArrayAVM2Item(null, inBrackets); + ret = memberOrCall(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + + break; + case FUNCTION: + s = lexer.lex(); + String fname = ""; + if (s.isType(SymbolType.IDENTIFIER)) { + fname = s.value.toString(); + } else { + lexer.pushback(s); + } + needsActivation.setVal(true); + ret = function(pkg, false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, fname, false, variables); + break; + case NAN: + ret = new NanAVM2Item(null); + existsRemainder = true; + break; + case INFINITY: + ret = new FloatValueAVM2Item(null, Double.POSITIVE_INFINITY); + existsRemainder = true; + break; + case INTEGER: + ret = new IntegerValueAVM2Item(null, (Long) s.value); + existsRemainder = true; + break; + case DOUBLE: + ret = new FloatValueAVM2Item(null, (Double) s.value); + existsRemainder = true; + break; + case DELETE: + GraphTargetItem varDel = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); + if (!isNameOrProp(varDel)) { + throw new ParseException("Not a property or name", lexer.yyline()); + } + ret = new DeletePropertyAVM2Item(varDel, lexer.yyline()); + break; + case INCREMENT: + case DECREMENT: //preincrement + GraphTargetItem varincdec = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false/*?*/, variables);//name(false, openedNamespaces, registerVars, inFunction, inMethod, variables); + if (!isNameOrProp(varincdec)) { + throw new ParseException("Not a property or name", lexer.yyline()); + } + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementAVM2Item(null, varincdec); + } + if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementAVM2Item(null, varincdec); + } + existsRemainder = true; + break; + case NOT: + ret = new NotItem(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, false, variables)); + existsRemainder = true; + break; + case PARENT_OPEN: + ret = new ParenthesisItem(null, expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(pkg, needsActivation, importedClasses, openedNamespaces, ret, registerVars, inFunction, inMethod, variables); + existsRemainder = true; + break; + case NEW: + s = lex(); + if (s.type == SymbolType.XML_STARTTAG_BEGIN) { + lexer.yypushbackstr(s.value.toString().substring(1), ActionScriptLexer.YYINITIAL); + s = new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.LOWER_THAN); + } + if (s.type == SymbolType.FUNCTION) { + s = lexer.lex(); + String ffname = ""; + if (s.isType(SymbolType.IDENTIFIER)) { + ffname = s.value.toString(); + } else { + lexer.pushback(s); + } + needsActivation.setVal(true); + ret = function(pkg, false, needsActivation, importedClasses, 0/*?*/, TypeItem.UNBOUNDED, openedNamespaces, ffname, false, variables); + } else if (s.type == SymbolType.LOWER_THAN) { + GraphTargetItem subtype = type(pkg, needsActivation, importedClasses, openedNamespaces, variables); + expectedType(SymbolType.GREATER_THAN); + s = lex(); + expected(s, lexer.yyline(), SymbolType.BRACKET_OPEN); + lexer.pushback(s); + List params = new ArrayList<>(); + brackets(pkg, needsActivation, importedClasses, openedNamespaces, params, registerVars, inFunction, inMethod, variables); + ret = new InitVectorAVM2Item(subtype, params, openedNamespaces); + } else if (s.type == SymbolType.PARENT_OPEN) { + GraphTargetItem newvar = expression(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, true, variables); + newvar = applyType(pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.PARENT_OPEN); + ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + + } else { + lexer.pushback(s); + GraphTargetItem newvar = name(pkg, needsActivation, false /*?*/, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); + newvar = applyType(pkg, needsActivation, importedClasses, openedNamespaces, newvar, registerVars, inFunction, inMethod, variables); + expectedType(SymbolType.PARENT_OPEN); + ret = new ConstructSomethingAVM2Item(lexer.yyline(), openedNamespaces, newvar, call(pkg, needsActivation, importedClasses, openedNamespaces, registerVars, inFunction, inMethod, variables)); + } + existsRemainder = true; + break; + case IDENTIFIER: + case THIS: + case SUPER: + case ATTRIBUTE: + lexer.pushback(s); + GraphTargetItem var = name(pkg, needsActivation, false, openedNamespaces, registerVars, inFunction, inMethod, variables, importedClasses); + var = memberOrCall(pkg, needsActivation, importedClasses, openedNamespaces, var, registerVars, inFunction, inMethod, variables); + ret = var; + existsRemainder = true; + break; + default: + GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1, variables); + if (excmd != null) { + existsRemainder = true; //? + ret = excmd; + break; + } + lexer.pushback(s); + } + if (allowRemainder && existsRemainder) { + GraphTargetItem rem = ret; + do { + rem = expressionRemainder(pkg, needsActivation, openedNamespaces, rem, registerVars, inFunction, inMethod, assocRight, variables, importedClasses); + if (rem != null) { + ret = rem; + } + } while ((!assocRight) && (rem != null)); + } + if (debugMode) { + System.out.println("/expression"); + } + return ret; + } + + private ActionScriptLexer lexer = null; + private List constantPool; + + private PackageAVM2Item parsePackage(List openedNamespaces) throws IOException, ParseException, CompilationException { + List items = new ArrayList<>(); + expectedType(SymbolType.PACKAGE); + String name = ""; + ParsedSymbol s = lex(); + if (s.type != SymbolType.CURLY_OPEN) { + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + name = s.value.toString(); + s = lex(); + } + while (s.type != SymbolType.CURLY_OPEN) { + expected(s, lexer.yyline(), SymbolType.DOT); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + name += "." + s.value.toString(); + s = lex(); + } + + List importedClasses = new ArrayList<>(); + + s = lex(); + while (s.type == SymbolType.IMPORT) { + String impPackage = ""; + String impName = null; + boolean all = false; + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + impName = s.value.toString(); + s = lex(); + while (s.type == SymbolType.DOT) { + if (!"".equals(impPackage)) { + impPackage += "."; + } + impPackage += impName; + + s = lex(); + if (s.type == SymbolType.MULTIPLY) { + impName = null; + s = lex(); + break; + } + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + + impName = s.value.toString(); + s = lex(); + } + + if (impName == null) { + openedNamespaces.add(abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(impPackage, true)), 0, true)); + } else { + importedClasses.add(impPackage + "." + impName); + } + + expected(s, lexer.yyline(), SymbolType.SEMICOLON); + s = lex(); + } + lexer.pushback(s); + + int publicNs; + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId(name, true)), 0, true)); + + //traits(false, new ArrayList(), new Reference(false), new ArrayList(), importedClasses, privateNs, 0, publicNs, packageInternalNs, 0, openedNamespaces, name, null, false, items); + //expectedType(SymbolType.CURLY_CLOSE); + return new PackageAVM2Item(publicNs, importedClasses, name, items); + } + + private List parseScript(String fileName) throws IOException, ParseException, CompilationException { + + List openedNamespaces = new ArrayList<>(); + + int scriptPrivateNs = 0; + + if (fileName.contains("/")) { + fileName = fileName.substring(fileName.lastIndexOf('/') + 1); + } + if (fileName.contains("\\")) { + fileName = fileName.substring(fileName.lastIndexOf('\\') + 1); + } + String className = fileName; + if (className.endsWith(".as")) { + className = className.substring(0, className.length() - 3); + } + openedNamespaces.add(scriptPrivateNs = abc.constants.addNamespace(new Namespace(Namespace.KIND_PRIVATE, 0))); //abc.constants.getStringId(name + ":" + className, true) + + int publicNs; + openedNamespaces.add(publicNs = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("", true)), 0, true)); + + List items = new ArrayList<>(); + traits(fileName, true, new ArrayList(), new Reference<>(false), new ArrayList(), new ArrayList(), scriptPrivateNs, 0, publicNs, 0, 0, openedNamespaces, null, null, false, items); + return items; + } + + public List scriptTraitsFromString(String str, String fileName) throws ParseException, IOException, CompilationException { + lexer = new ActionScriptLexer(str); + + List ret = parseScript(fileName); + if (lexer.lex().type != SymbolType.EOF) { + throw new ParseException("Parsing finisned before end of the file", lexer.yyline()); + } + return ret; + } + + public void addScriptFromTree(List items, boolean documentClass) throws ParseException, CompilationException { + AVM2SourceGenerator gen = new AVM2SourceGenerator(abc, otherABCs); + List ret = new ArrayList<>(); + SourceGeneratorLocalData localData = new SourceGeneratorLocalData( + new HashMap(), 0, Boolean.FALSE, 0); + localData.documentClass = documentClass; + abc.script_info.add(gen.generateScriptInfo(localData, items)); + } + + public void addScript(String s, boolean documentClass, String fileName) throws ParseException, IOException, CompilationException { + List traits = scriptTraitsFromString(s, fileName); + addScriptFromTree(traits, documentClass); + } + + public ActionScriptParser(ABC abc, List otherABCs) { + this.abc = abc; + this.otherABCs = otherABCs; + } + + private static void initPlayer() throws IOException, InterruptedException { + if (playerABCs.isEmpty()) { + SWC swc = new SWC(new FileInputStream(Configuration.getPlayerSWC())); + SWF swf = new SWF(swc.getSWF("library.swf"), true); + for (Tag t : swf.tags) { + if (t instanceof ABCContainerTag) { + playerABCs.add(((ABCContainerTag) t).getABC()); + } + } + } + } + + public static void compile(String src, ABC abc, List otherABCs, boolean documentClass, String fileName) throws ParseException, IOException, InterruptedException, CompilationException { + List parABCs = new ArrayList<>(); + initPlayer(); + parABCs.addAll(playerABCs); + parABCs.addAll(otherABCs); + ActionScriptParser parser = new ActionScriptParser(abc, parABCs); + parser.addScript(src, documentClass, fileName); + } + + public static void compile(SWF swf, String src, String dst) { + System.err.println("WARNING: AS3 compiler is not finished yet. This is only used for debuggging!"); + try { + initPlayer(); + ABC abc = new ABC(swf); + ActionScriptParser parser = new ActionScriptParser(abc, playerABCs); + parser.addScript(new String(Helper.readFile(src), "UTF-8"), true, src); + abc.saveToStream(new FileOutputStream(new File(dst))); + } catch (Exception ex) { + Logger.getLogger(ActionScriptParser.class.getName()).log(Level.SEVERE, null, ex); + } + System.exit(0); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java index 5ca3318b3..afa9b77a2 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/CallAVM2Item.java @@ -1,188 +1,185 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeFunctionItem; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class CallAVM2Item extends AVM2Item { - - public GraphTargetItem name; - public List arguments; - public int line; - - public CallAVM2Item(int line, GraphTargetItem name, List arguments) { - super(null, NOPRECEDENCE); - this.name = name; - this.arguments = arguments; - this.line = line; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { - - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - - GraphTargetItem callable = name; - if (callable instanceof UnresolvedAVM2Item) { - callable = ((UnresolvedAVM2Item) callable).resolved; - } - if (callable instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) callable; - List allAbcs = new ArrayList<>(); - allAbcs.add(g.abc); - allAbcs.addAll(g.allABCs); - String cname; - String pkgName = ""; - cname = localData.currentClass; - pkgName = localData.pkg; - GraphTargetItem obj = null; - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropNsIndex = new Reference<>(0); - Reference outPropType = new Reference<>(null); - Reference outPropValue = new Reference<>(null); - if (cname!=null && AVM2SourceGenerator.searchPrototypeChain(true, allAbcs, pkgName, cname, n.getVariableName(), outName, outNs, outPropNs, outPropNsKind,outPropNsIndex, outPropType, outPropValue)) { - NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), n.line, "this", null, false, n.openedNamespaces); - nobj.setRegNumber(0); - obj = nobj; - } - PropertyAVM2Item p = new PropertyAVM2Item(obj, n.getVariableName(), g.abc, g.allABCs, n.openedNamespaces, new ArrayList()); - p.setAssignedValue(n.getAssignedValue()); - callable = p; - } - - int propIndex = -1; - if (callable instanceof TypeItem) { - TypeItem t = (TypeItem) callable; - propIndex = AVM2SourceGenerator.resolveType(localData,t, ((AVM2SourceGenerator) generator).abc,((AVM2SourceGenerator) generator).allABCs); - } - Object obj = null; - - if (callable instanceof PropertyAVM2Item) { - PropertyAVM2Item prop = (PropertyAVM2Item) callable; - obj = prop.object; - if (obj == null) { - - List allAbcs = new ArrayList<>(); - allAbcs.add(g.abc); - allAbcs.addAll(g.allABCs); - String cname; - String pkgName = ""; - cname = localData.currentClass; - pkgName = localData.pkg; - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropNsIndex = new Reference<>(0); - Reference outPropType = new Reference<>(null); - Reference outPropValue = new Reference<>(null); - if (cname!=null && AVM2SourceGenerator.searchPrototypeChain(true, allAbcs, pkgName, cname, prop.propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue) && (localData.currentClass.equals("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()))) { - NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), 0, "this", null, false, new ArrayList()); - nobj.setRegNumber(0); - obj = nobj; - } - } - propIndex = prop.resolveProperty(localData); - } - - if (propIndex != -1) { - if (obj == null) { - obj = new AVM2Instruction(0, new FindPropertyStrictIns(), new int[]{propIndex}, new byte[0]); - } - return toSourceMerge(localData, generator, obj, arguments, - ins(needsReturn?new CallPropertyIns():new CallPropVoidIns(), propIndex, arguments.size()) - ); - } - - if(callable instanceof IndexAVM2Item){ - return ((IndexAVM2Item)callable).toSource(localData, generator, needsReturn, true, arguments,false,false); - } - if(callable instanceof NamespacedAVM2Item){ - return ((NamespacedAVM2Item)callable).toSource(localData, generator, needsReturn, true, arguments,false,false); - } - - return toSourceMerge(localData, generator, callable, ins(new GetGlobalScopeIns()),arguments,ins(new CallIns(),arguments.size())); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, true); - } - - - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, false); - } - - @Override - public GraphTargetItem returnType() { - GraphTargetItem callable = name; - if (callable instanceof UnresolvedAVM2Item) { - callable = ((UnresolvedAVM2Item) callable).resolved; - } - - if (callable instanceof TypeItem) { - return TypeItem.UNBOUNDED; - } - - GraphTargetItem ti = callable.returnType(); - if (ti instanceof TypeFunctionItem) { - TypeFunctionItem tfi = (TypeFunctionItem) ti; - return new TypeItem(tfi.fullTypeName); - } - return ti; - } - - @Override - public boolean hasReturnValue() { - return true; - } -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetGlobalScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeFunctionItem; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class CallAVM2Item extends AVM2Item { + + public GraphTargetItem name; + public List arguments; + public int line; + + public CallAVM2Item(int line, GraphTargetItem name, List arguments) { + super(null, NOPRECEDENCE); + this.name = name; + this.arguments = arguments; + this.line = line; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { + + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + + GraphTargetItem callable = name; + if (callable instanceof UnresolvedAVM2Item) { + callable = ((UnresolvedAVM2Item) callable).resolved; + } + if (callable instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) callable; + List allAbcs = new ArrayList<>(); + allAbcs.add(g.abc); + allAbcs.addAll(g.allABCs); + String cname; + String pkgName = ""; + cname = localData.currentClass; + pkgName = localData.pkg; + GraphTargetItem obj = null; + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropNsIndex = new Reference<>(0); + Reference outPropType = new Reference<>(null); + Reference outPropValue = new Reference<>(null); + if (cname != null && AVM2SourceGenerator.searchPrototypeChain(true, allAbcs, pkgName, cname, n.getVariableName(), outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) { + NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), n.line, "this", null, false, n.openedNamespaces); + nobj.setRegNumber(0); + obj = nobj; + } + PropertyAVM2Item p = new PropertyAVM2Item(obj, n.getVariableName(), g.abc, g.allABCs, n.openedNamespaces, new ArrayList()); + p.setAssignedValue(n.getAssignedValue()); + callable = p; + } + + int propIndex = -1; + if (callable instanceof TypeItem) { + TypeItem t = (TypeItem) callable; + propIndex = AVM2SourceGenerator.resolveType(localData, t, ((AVM2SourceGenerator) generator).abc, ((AVM2SourceGenerator) generator).allABCs); + } + Object obj = null; + + if (callable instanceof PropertyAVM2Item) { + PropertyAVM2Item prop = (PropertyAVM2Item) callable; + obj = prop.object; + if (obj == null) { + + List allAbcs = new ArrayList<>(); + allAbcs.add(g.abc); + allAbcs.addAll(g.allABCs); + String cname; + String pkgName = ""; + cname = localData.currentClass; + pkgName = localData.pkg; + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropNsIndex = new Reference<>(0); + Reference outPropType = new Reference<>(null); + Reference outPropValue = new Reference<>(null); + if (cname != null && AVM2SourceGenerator.searchPrototypeChain(true, allAbcs, pkgName, cname, prop.propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue) && (localData.currentClass.equals("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()))) { + NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), 0, "this", null, false, new ArrayList()); + nobj.setRegNumber(0); + obj = nobj; + } + } + propIndex = prop.resolveProperty(localData); + } + + if (propIndex != -1) { + if (obj == null) { + obj = new AVM2Instruction(0, new FindPropertyStrictIns(), new int[]{propIndex}, new byte[0]); + } + return toSourceMerge(localData, generator, obj, arguments, + ins(needsReturn ? new CallPropertyIns() : new CallPropVoidIns(), propIndex, arguments.size()) + ); + } + + if (callable instanceof IndexAVM2Item) { + return ((IndexAVM2Item) callable).toSource(localData, generator, needsReturn, true, arguments, false, false); + } + if (callable instanceof NamespacedAVM2Item) { + return ((NamespacedAVM2Item) callable).toSource(localData, generator, needsReturn, true, arguments, false, false); + } + + return toSourceMerge(localData, generator, callable, ins(new GetGlobalScopeIns()), arguments, ins(new CallIns(), arguments.size())); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, true); + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, false); + } + + @Override + public GraphTargetItem returnType() { + GraphTargetItem callable = name; + if (callable instanceof UnresolvedAVM2Item) { + callable = ((UnresolvedAVM2Item) callable).resolved; + } + + if (callable instanceof TypeItem) { + return TypeItem.UNBOUNDED; + } + + GraphTargetItem ti = callable.returnType(); + if (ti instanceof TypeFunctionItem) { + TypeFunctionItem tfi = (TypeFunctionItem) ti; + return new TypeItem(tfi.fullTypeName); + } + return ti; + } + + @Override + public boolean hasReturnValue() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java index 3ca799143..10d04ecbc 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ClassAVM2Item.java @@ -1,102 +1,102 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.Block; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import java.util.ArrayList; -import java.util.List; - -public class ClassAVM2Item extends AVM2Item implements Block { - - public List traits; - public GraphTargetItem extendsOp; - public List implementsOp; - public String className; - public GraphTargetItem constructor; - public int namespace; - public int protectedNs; - public boolean isDynamic; - public boolean isFinal; - public List openedNamespaces; - public List staticInit; - public boolean staticInitActivation; - public List sinitVariables; - public List importedClasses; - - public String pkg; - - @Override - public List> getSubs() { - List> ret = new ArrayList<>(); - if (traits != null) { - ret.add(traits); - } - return ret; - } - - public ClassAVM2Item(List importedClasses,String pkg,List openedNamespaces, int protectedNs, boolean isDynamic, boolean isFinal, int namespace, String className, GraphTargetItem extendsOp, List implementsOp, List staticInit, boolean staticInitActivation, List sinitVariables, GraphTargetItem constructor, List traits) { - super(null, NOPRECEDENCE); - this.importedClasses = importedClasses; - this.pkg = pkg; - this.protectedNs = protectedNs; - this.className = className; - this.traits = traits; - this.extendsOp = extendsOp; - this.implementsOp = implementsOp; - this.constructor = constructor; - this.namespace = namespace; - this.isDynamic = isDynamic; - this.isFinal = isFinal; - this.openedNamespaces = openedNamespaces; - this.staticInit = staticInit; - this.staticInitActivation = staticInitActivation; - this.sinitVariables = sinitVariables; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - @Override - public List getContinues() { - List ret = new ArrayList<>(); - return ret; - } - - @Override - public boolean needsSemicolon() { - return false; - } - - @Override - public boolean hasReturnValue() { - return false; - } - - @Override - public GraphTargetItem returnType() { - return new UnboundedTypeItem(); //FIXME - } - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.Block; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.ArrayList; +import java.util.List; + +public class ClassAVM2Item extends AVM2Item implements Block { + + public List traits; + public GraphTargetItem extendsOp; + public List implementsOp; + public String className; + public GraphTargetItem constructor; + public int namespace; + public int protectedNs; + public boolean isDynamic; + public boolean isFinal; + public List openedNamespaces; + public List staticInit; + public boolean staticInitActivation; + public List sinitVariables; + public List importedClasses; + + public String pkg; + + @Override + public List> getSubs() { + List> ret = new ArrayList<>(); + if (traits != null) { + ret.add(traits); + } + return ret; + } + + public ClassAVM2Item(List importedClasses, String pkg, List openedNamespaces, int protectedNs, boolean isDynamic, boolean isFinal, int namespace, String className, GraphTargetItem extendsOp, List implementsOp, List staticInit, boolean staticInitActivation, List sinitVariables, GraphTargetItem constructor, List traits) { + super(null, NOPRECEDENCE); + this.importedClasses = importedClasses; + this.pkg = pkg; + this.protectedNs = protectedNs; + this.className = className; + this.traits = traits; + this.extendsOp = extendsOp; + this.implementsOp = implementsOp; + this.constructor = constructor; + this.namespace = namespace; + this.isDynamic = isDynamic; + this.isFinal = isFinal; + this.openedNamespaces = openedNamespaces; + this.staticInit = staticInit; + this.staticInitActivation = staticInitActivation; + this.sinitVariables = sinitVariables; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + @Override + public List getContinues() { + List ret = new ArrayList<>(); + return ret; + } + + @Override + public boolean needsSemicolon() { + return false; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public GraphTargetItem returnType() { + return new UnboundedTypeItem(); //FIXME + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java index 80c9f404a..8575fe8a8 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstAVM2Item.java @@ -1,73 +1,73 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; - -/** - * - * @author JPEXS - */ -public class ConstAVM2Item extends AVM2Item { - - private final int namespace; - private boolean isStatic; - public String var; - public GraphTargetItem type; - public String customNamespace; - public int line; - public String pkg; - - public int getNamespace() { - return namespace; - } - - public boolean isStatic() { - return isStatic; - } - - public ConstAVM2Item(String pkg,String customNamespace, boolean isStatic, int namespace, String var, GraphTargetItem type, GraphTargetItem value, int line) { - super(null, NOPRECEDENCE); - this.pkg = pkg; - this.line = line; - this.namespace = namespace; - this.value = value; - this.isStatic = isStatic; - this.var = var; - this.type = type; - this.customNamespace = customNamespace; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; //TODO - } - - @Override - public GraphTargetItem returnType() { - return type; - } - - @Override - public boolean hasReturnValue() { - return true; - } - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; + +/** + * + * @author JPEXS + */ +public class ConstAVM2Item extends AVM2Item { + + private final int namespace; + private boolean isStatic; + public String var; + public GraphTargetItem type; + public String customNamespace; + public int line; + public String pkg; + + public int getNamespace() { + return namespace; + } + + public boolean isStatic() { + return isStatic; + } + + public ConstAVM2Item(String pkg, String customNamespace, boolean isStatic, int namespace, String var, GraphTargetItem type, GraphTargetItem value, int line) { + super(null, NOPRECEDENCE); + this.pkg = pkg; + this.line = line; + this.namespace = namespace; + this.value = value; + this.isStatic = isStatic; + this.var = var; + this.type = type; + this.customNamespace = customNamespace; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; //TODO + } + + @Override + public GraphTargetItem returnType() { + return type; + } + + @Override + public boolean hasReturnValue() { + return true; + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstructSomethingAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstructSomethingAVM2Item.java index 35b314193..4baa614c5 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstructSomethingAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/ConstructSomethingAVM2Item.java @@ -1,104 +1,100 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ApplyTypeIns; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class ConstructSomethingAVM2Item extends CallAVM2Item { - - public List openedNamespaces; - - public ConstructSomethingAVM2Item(int line, List openedNamespaces, GraphTargetItem name, List arguments) { - super(line, name, arguments); - this.openedNamespaces = openedNamespaces; - } - - @Override - public GraphTargetItem returnType() { - return name.returnType(); - } - - private int allNsSetWithVec(ABC abc) { - int nssa[] = new int[openedNamespaces.size() + 1]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - nssa[nssa.length - 1] = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("__AS3__.vec", true)), 0, true); - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - - GraphTargetItem resname = name; - if (resname instanceof UnresolvedAVM2Item) { - resname = ((UnresolvedAVM2Item) resname).resolved; - } - - if (resname instanceof TypeItem) { - TypeItem prop = (TypeItem) resname; - int type_index = AVM2SourceGenerator.resolveType(localData,resname,((AVM2SourceGenerator)generator).abc,((AVM2SourceGenerator)generator).allABCs); - return toSourceMerge(localData, generator, - new AVM2Instruction(0, new FindPropertyStrictIns(), new int[]{type_index, arguments.size()}, new byte[0]), arguments, - new AVM2Instruction(0, new ConstructPropIns(), new int[]{type_index, arguments.size()}, new byte[0]) - ); - } - - if (resname instanceof PropertyAVM2Item) { - PropertyAVM2Item prop=(PropertyAVM2Item)resname; - return toSourceMerge(localData, generator, prop.resolveObject(localData, generator),arguments, - ins(new ConstructPropIns(), prop.resolveProperty(localData),arguments.size()) - ); - } - - if (resname instanceof NameAVM2Item) { - return toSourceMerge(localData, generator, resname,arguments,ins(new ConstructIns(),arguments.size())); - } - - if(resname instanceof IndexAVM2Item){ - return ((IndexAVM2Item)resname).toSource(localData, generator, true, false, arguments, false, true); - } - - if(resname instanceof NamespacedAVM2Item){ - return ((NamespacedAVM2Item)resname).toSource(localData, generator, true, false, arguments, false, true); - } - return toSourceMerge(localData, generator, resname, arguments, ins(new ConstructIns(),arguments.size())); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ConstructSomethingAVM2Item extends CallAVM2Item { + + public List openedNamespaces; + + public ConstructSomethingAVM2Item(int line, List openedNamespaces, GraphTargetItem name, List arguments) { + super(line, name, arguments); + this.openedNamespaces = openedNamespaces; + } + + @Override + public GraphTargetItem returnType() { + return name.returnType(); + } + + private int allNsSetWithVec(ABC abc) { + int nssa[] = new int[openedNamespaces.size() + 1]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + nssa[nssa.length - 1] = abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.getStringId("__AS3__.vec", true)), 0, true); + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + + GraphTargetItem resname = name; + if (resname instanceof UnresolvedAVM2Item) { + resname = ((UnresolvedAVM2Item) resname).resolved; + } + + if (resname instanceof TypeItem) { + TypeItem prop = (TypeItem) resname; + int type_index = AVM2SourceGenerator.resolveType(localData, resname, ((AVM2SourceGenerator) generator).abc, ((AVM2SourceGenerator) generator).allABCs); + return toSourceMerge(localData, generator, + new AVM2Instruction(0, new FindPropertyStrictIns(), new int[]{type_index, arguments.size()}, new byte[0]), arguments, + new AVM2Instruction(0, new ConstructPropIns(), new int[]{type_index, arguments.size()}, new byte[0]) + ); + } + + if (resname instanceof PropertyAVM2Item) { + PropertyAVM2Item prop = (PropertyAVM2Item) resname; + return toSourceMerge(localData, generator, prop.resolveObject(localData, generator), arguments, + ins(new ConstructPropIns(), prop.resolveProperty(localData), arguments.size()) + ); + } + + if (resname instanceof NameAVM2Item) { + return toSourceMerge(localData, generator, resname, arguments, ins(new ConstructIns(), arguments.size())); + } + + if (resname instanceof IndexAVM2Item) { + return ((IndexAVM2Item) resname).toSource(localData, generator, true, false, arguments, false, true); + } + + if (resname instanceof NamespacedAVM2Item) { + return ((NamespacedAVM2Item) resname).toSource(localData, generator, true, false, arguments, false, true); + } + return toSourceMerge(localData, generator, resname, arguments, ins(new ConstructIns(), arguments.size())); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/FunctionAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/FunctionAVM2Item.java index 5b4307c0e..0599f8877 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/FunctionAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/FunctionAVM2Item.java @@ -1,88 +1,88 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class FunctionAVM2Item extends AVM2Item { - - public String calculatedFunctionName; - public String functionName; - public int namespace; - public List paramNames; - public List body; - public List subvariables; - public List paramTypes; - public List paramValues; - public GraphTargetItem retType; - public int line; - public boolean hasRest; - public boolean needsActivation; - public boolean isInterface; - public String pkg; - - public FunctionAVM2Item(String pkg,boolean isInterface, boolean needsActivation, int namespace, boolean hasRest, int line, String functionName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { - super(null, NOPRECEDENCE); - this.pkg = pkg; - this.needsActivation = needsActivation; - this.namespace = namespace; - this.paramNames = paramNames; - this.body = body; - this.functionName = functionName; - this.subvariables = subvariables; - this.paramTypes = paramTypes; - this.paramValues = paramValues; - this.retType = retType; - this.line = line; - this.hasRest = hasRest; - this.isInterface = isInterface; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; //todo? - } - - @Override - public GraphTargetItem returnType() { - return new TypeItem("Function"); - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return ((AVM2SourceGenerator) generator).generate(localData, this); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class FunctionAVM2Item extends AVM2Item { + + public String calculatedFunctionName; + public String functionName; + public int namespace; + public List paramNames; + public List body; + public List subvariables; + public List paramTypes; + public List paramValues; + public GraphTargetItem retType; + public int line; + public boolean hasRest; + public boolean needsActivation; + public boolean isInterface; + public String pkg; + + public FunctionAVM2Item(String pkg, boolean isInterface, boolean needsActivation, int namespace, boolean hasRest, int line, String functionName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { + super(null, NOPRECEDENCE); + this.pkg = pkg; + this.needsActivation = needsActivation; + this.namespace = namespace; + this.paramNames = paramNames; + this.body = body; + this.functionName = functionName; + this.subvariables = subvariables; + this.paramTypes = paramTypes; + this.paramValues = paramValues; + this.retType = retType; + this.line = line; + this.hasRest = hasRest; + this.isInterface = isInterface; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; //todo? + } + + @Override + public GraphTargetItem returnType() { + return new TypeItem("Function"); + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return ((AVM2SourceGenerator) generator).generate(localData, this); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/GetterAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/GetterAVM2Item.java index 0c23b23b9..4799b17cd 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/GetterAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/GetterAVM2Item.java @@ -1,32 +1,32 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class GetterAVM2Item extends MethodAVM2Item { - - public GetterAVM2Item(String pkg, boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { - super(pkg,isInterface, customNamespace, needsActivation, hasRest, line, override, isFinal, isStatic, namespace, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class GetterAVM2Item extends MethodAVM2Item { + + public GetterAVM2Item(String pkg, boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { + super(pkg, isInterface, customNamespace, needsActivation, hasRest, line, override, isFinal, isStatic, namespace, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/IndexAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/IndexAVM2Item.java index da820fb89..503f00a3d 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/IndexAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/IndexAVM2Item.java @@ -1,162 +1,160 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.dupSetTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.getTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.killTemp; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IndexAVM2Item extends AssignableAVM2Item { - - private List openedNamespaces; - public GraphTargetItem object; - public GraphTargetItem index; - public boolean attr; - - public IndexAVM2Item(boolean attr, GraphTargetItem object, GraphTargetItem index, GraphTargetItem storeValue, List openedNamespaces) { - super(storeValue); - this.object = object; - this.index = index; - this.openedNamespaces = openedNamespaces; - this.attr = attr; - } - - private int allNsSet(ABC abc) { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return null; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - @Override - public AssignableAVM2Item copy() { - return new IndexAVM2Item(attr, object, index, assignedValue, openedNamespaces); - } - - @Override - public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { - Reference obj_temp = new Reference<>(-1); - Reference index_temp = new Reference<>(-1); - Reference val_temp = new Reference<>(-1); - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - int indexPropIndex = g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.MULTINAMELA : Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true); - - return toSourceMerge(localData, generator, - object, dupSetTemp(localData, generator, obj_temp), - index, dupSetTemp(localData, generator, index_temp), - ins(new GetPropertyIns(), indexPropIndex), - post ? ins(new ConvertDIns()) : null, - (!post) ? (decrement ? ins(new DecrementIns()) : ins(new IncrementIns())) : null, - needsReturn ? ins(new DupIns()) : null, - post ? (decrement ? ins(new DecrementIns()) : ins(new IncrementIns())) : null, - setTemp(localData, generator, val_temp), - getTemp(localData, generator, obj_temp), - getTemp(localData, generator, index_temp), - getTemp(localData, generator, val_temp), - ins(new SetPropertyIns(), indexPropIndex), - killTemp(localData, generator, Arrays.asList(val_temp, obj_temp, index_temp)) - ); - - } - - - - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn, boolean call, List callargs, boolean delete, boolean construct) throws CompilationException { - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - int indexPropIndex = g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.MULTINAMELA : Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true); - Reference ret_temp = new Reference<>(-1); - - if (assignedValue != null) { - return toSourceMerge(localData, generator, - object, - index, - assignedValue, - needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, - ins(new SetPropertyIns(), indexPropIndex), - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ret_temp))); - } else { - return toSourceMerge(localData, generator, - object, - call?ins(new DupIns()):null, - index, - construct?callargs:null, - ins(construct?new ConstructPropIns():delete?new DeletePropertyIns():new GetPropertyIns(), indexPropIndex, construct?callargs.size():null), - call?callargs:null, - call?ins(new CallIns(),callargs.size()):null, - needsReturn ? null : ins(new PopIns())); - } - - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, true, false, new ArrayList(),false,false); - } - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, false, false, new ArrayList(),false,false); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.dupSetTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.getTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.killTemp; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IndexAVM2Item extends AssignableAVM2Item { + + private List openedNamespaces; + public GraphTargetItem object; + public GraphTargetItem index; + public boolean attr; + + public IndexAVM2Item(boolean attr, GraphTargetItem object, GraphTargetItem index, GraphTargetItem storeValue, List openedNamespaces) { + super(storeValue); + this.object = object; + this.index = index; + this.openedNamespaces = openedNamespaces; + this.attr = attr; + } + + private int allNsSet(ABC abc) { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return null; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + @Override + public AssignableAVM2Item copy() { + return new IndexAVM2Item(attr, object, index, assignedValue, openedNamespaces); + } + + @Override + public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { + Reference obj_temp = new Reference<>(-1); + Reference index_temp = new Reference<>(-1); + Reference val_temp = new Reference<>(-1); + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + int indexPropIndex = g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.MULTINAMELA : Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true); + + return toSourceMerge(localData, generator, + object, dupSetTemp(localData, generator, obj_temp), + index, dupSetTemp(localData, generator, index_temp), + ins(new GetPropertyIns(), indexPropIndex), + post ? ins(new ConvertDIns()) : null, + (!post) ? (decrement ? ins(new DecrementIns()) : ins(new IncrementIns())) : null, + needsReturn ? ins(new DupIns()) : null, + post ? (decrement ? ins(new DecrementIns()) : ins(new IncrementIns())) : null, + setTemp(localData, generator, val_temp), + getTemp(localData, generator, obj_temp), + getTemp(localData, generator, index_temp), + getTemp(localData, generator, val_temp), + ins(new SetPropertyIns(), indexPropIndex), + killTemp(localData, generator, Arrays.asList(val_temp, obj_temp, index_temp)) + ); + + } + + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn, boolean call, List callargs, boolean delete, boolean construct) throws CompilationException { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + int indexPropIndex = g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.MULTINAMELA : Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true); + Reference ret_temp = new Reference<>(-1); + + if (assignedValue != null) { + return toSourceMerge(localData, generator, + object, + index, + assignedValue, + needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, + ins(new SetPropertyIns(), indexPropIndex), + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ret_temp))); + } else { + return toSourceMerge(localData, generator, + object, + call ? ins(new DupIns()) : null, + index, + construct ? callargs : null, + ins(construct ? new ConstructPropIns() : delete ? new DeletePropertyIns() : new GetPropertyIns(), indexPropIndex, construct ? callargs.size() : null), + call ? callargs : null, + call ? ins(new CallIns(), callargs.size()) : null, + needsReturn ? null : ins(new PopIns())); + } + + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, true, false, new ArrayList(), false, false); + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, false, false, new ArrayList(), false, false); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java index 3d8d8867a..feb1eb92c 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/InterfaceAVM2Item.java @@ -1,72 +1,72 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class InterfaceAVM2Item extends AVM2Item { - - public String name; - public List superInterfaces; - public List methods; - public int namespace; - public boolean isFinal; - public List openedNamespaces; - public String pkg; - public List importedClasses; - - public InterfaceAVM2Item(List importedClasses,String pkg,List openedNamespaces, boolean isFinal, int namespace, String name, List superInterfaces, List traits) { - super(null, NOPRECEDENCE); - this.importedClasses = importedClasses; - this.pkg = pkg; - this.name = name; - this.superInterfaces = superInterfaces; - this.methods = traits; - this.namespace = namespace; - this.isFinal = isFinal; - this.openedNamespaces = openedNamespaces; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - @Override - public boolean needsSemicolon() { - return false; - } - - @Override - public boolean hasReturnValue() { - return false; - } - - @Override - public GraphTargetItem returnType() { - return new UnboundedTypeItem(); //FIXME - } -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class InterfaceAVM2Item extends AVM2Item { + + public String name; + public List superInterfaces; + public List methods; + public int namespace; + public boolean isFinal; + public List openedNamespaces; + public String pkg; + public List importedClasses; + + public InterfaceAVM2Item(List importedClasses, String pkg, List openedNamespaces, boolean isFinal, int namespace, String name, List superInterfaces, List traits) { + super(null, NOPRECEDENCE); + this.importedClasses = importedClasses; + this.pkg = pkg; + this.name = name; + this.superInterfaces = superInterfaces; + this.methods = traits; + this.namespace = namespace; + this.isFinal = isFinal; + this.openedNamespaces = openedNamespaces; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + @Override + public boolean needsSemicolon() { + return false; + } + + @Override + public boolean hasReturnValue() { + return false; + } + + @Override + public GraphTargetItem returnType() { + return new UnboundedTypeItem(); //FIXME + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/MethodAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/MethodAVM2Item.java index 3f6ec3ae0..e15eebd1b 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/MethodAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/MethodAVM2Item.java @@ -1,62 +1,62 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class MethodAVM2Item extends FunctionAVM2Item { - - private boolean isStatic; - private boolean isFinal; - private boolean override; - public String customNamespace; - public boolean isInterface; - - public MethodAVM2Item(String pkg,boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { - super(pkg,isInterface, needsActivation, namespace, hasRest, line, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); - this.isStatic = isStatic; - this.override = override; - this.isFinal = isFinal; - this.customNamespace = customNamespace; - this.isInterface = this.isInterface; - } - - public boolean isOverride() { - return override; - } - - public boolean isStatic() { - return isStatic; - } - - public boolean isFinal() { - return isFinal; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; //todo? - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class MethodAVM2Item extends FunctionAVM2Item { + + private boolean isStatic; + private boolean isFinal; + private boolean override; + public String customNamespace; + public boolean isInterface; + + public MethodAVM2Item(String pkg, boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { + super(pkg, isInterface, needsActivation, namespace, hasRest, line, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); + this.isStatic = isStatic; + this.override = override; + this.isFinal = isFinal; + this.customNamespace = customNamespace; + this.isInterface = this.isInterface; + } + + public boolean isOverride() { + return override; + } + + public boolean isStatic() { + return isStatic; + } + + public boolean isFinal() { + return isFinal; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; //todo? + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java index 5183ff096..7e0828868 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NameAVM2Item.java @@ -1,346 +1,346 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; -import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class NameAVM2Item extends AssignableAVM2Item { - - private String variableName; - - private boolean definition; - private int nsKind = -1; - public List openedNamespaces; - public int line; - public GraphTargetItem type; - private GraphTargetItem ns = null; - private int regNumber = -1; - public boolean unresolved = false; - private int slotNumber = -1; - private int slotScope = 0; - - public GraphTargetItem redirect; - - @Override - public AssignableAVM2Item copy() { - NameAVM2Item c = new NameAVM2Item(type, line, variableName, assignedValue, definition, openedNamespaces); - c.setNs(ns); - c.regNumber = regNumber; - c.unresolved = unresolved; - c.nsKind = nsKind; - return c; - } - - public void setSlotScope(int slotScope) { - this.slotScope = slotScope; - } - - public int getSlotScope() { - return slotScope; - } - - public void setNs(GraphTargetItem ns) { - this.ns = ns; - } - - public void setRegNumber(int regNumber) { - this.regNumber = regNumber; - } - - public int getSlotNumber() { - return slotNumber; - } - - public void setSlotNumber(int slotNumber) { - this.slotNumber = slotNumber; - } - - public int getRegNumber() { - return regNumber; - } - - public GraphTargetItem getNs() { - return ns; - } - - public void appendName(String name) { - this.variableName += "." + name; - } - - public void setDefinition(boolean definition) { - this.definition = definition; - } - - public void setNsKind(int nsKind) { - this.nsKind = nsKind; - } - - public int getNsKind() { - return nsKind; - } - - public String getVariableName() { - return variableName; - } - - public NameAVM2Item(GraphTargetItem type, int line, String variableName, GraphTargetItem storeValue, boolean definition, List openedNamespaces) { - super(storeValue); - this.variableName = variableName; - this.assignedValue = storeValue; - this.definition = definition; - this.line = line; - this.type = type; - this.openedNamespaces = openedNamespaces; - } - - public boolean isDefinition() { - return definition; - } - - public GraphTargetItem getStoreValue() { - return assignedValue; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - private int allNsSet(ABC abc) { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - public static GraphTargetItem getDefaultValue(String type) { - switch (type) { - case "*": - return new UndefinedAVM2Item(null); - case "int": - return new IntegerValueAVM2Item(null, 0L); - case "Number": - return new NanAVM2Item(null); - default: - return new NullAVM2Item(null); - } - } - - public static AVM2Instruction generateCoerce(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem ttype) throws CompilationException { - if (ttype instanceof UnresolvedAVM2Item) { - ttype = ((UnresolvedAVM2Item) ttype).resolved; - } - AVM2Instruction ins; - if (ttype instanceof UnboundedTypeItem) { - ins = ins(new CoerceAIns()); - } else { - switch (ttype.toString()) { - case "int": - ins = ins(new ConvertIIns()); - break; - case "*": - ins = ins(new CoerceAIns()); - break; - case "String": - ins = ins(new CoerceSIns()); - break; - case "Boolean": - ins = ins(new ConvertBIns()); - break; - case "uint": - ins = ins(new ConvertUIns()); - break; - default: - int type_index = AVM2SourceGenerator.resolveType(localData,ttype, ((AVM2SourceGenerator) generator).abc,((AVM2SourceGenerator) generator).allABCs); - ins = ins(new CoerceIns(), type_index); - break; - } - } - return ins; - } - - private List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { - if (variableName != null && regNumber == -1 && slotNumber == -1 && ns == null) { - throw new CompilationException("No register or slot set for " + variableName, line); - } - if (definition && assignedValue == null) { - return new ArrayList<>(); - } - String name = variableName; - boolean attr = false; - if (name != null && name.startsWith("@")) { - name = name.substring(1); - attr = true; - } - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - Reference ns_temp = new Reference<>(-1); - Reference index_temp = new Reference<>(-1); - Reference ret_temp = new Reference<>(-1); - - if (assignedValue != null) { - List basicTypes = Arrays.asList("int", "Number"); - if (slotNumber > -1) { - return toSourceMerge(localData, generator, - ins(new GetScopeObjectIns(), slotScope), - assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(localData,generator, type) : null, needsReturn - ? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber), slotNumber > -1 - ? ins(new SetSlotIns(), slotNumber) - : null, - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ret_temp))); - } else { - - return toSourceMerge(localData, generator, assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(localData,generator, type) : null, needsReturn - ? ins(new DupIns()) : null, generateSetLoc(regNumber)); - } - } else { - return toSourceMerge(localData, generator, generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), - needsReturn ? null : ins(new PopIns())); - } - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if (redirect != null) { - return redirect.toSource(localData, generator); - } - return toSource(localData, generator, true); - - } - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if (redirect != null) { - return redirect.toSourceIgnoreReturnValue(localData, generator); - } - return toSource(localData, generator, false); - } - - @Override - public boolean hasReturnValue() { - if (definition) { - return false; - } - return true; - } - - @Override - public boolean needsSemicolon() { - if (definition) { - return true; - } - return false; - } - - @Override - public String toString() { - return variableName; - } - - @Override - public GraphTargetItem returnType() { - if (type == null) { - return TypeItem.UNBOUNDED; - } - return type; - } - - @Override - public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { - if (redirect != null) { - return ((AssignableAVM2Item) redirect).toSourceChange(localData, generator, post, decrement, needsReturn); - } - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - Reference ns_temp = new Reference<>(-1); - Reference name_temp = new Reference<>(-1); - Reference index_temp = new Reference<>(-1); - Reference ret_temp = new Reference<>(-1); - boolean isInteger = returnType().toString().equals("int"); - /* - - - */ - if (!needsReturn) { - if (slotNumber > -1) { - return toSourceMerge(localData, generator, - ins(new GetScopeObjectIns(), slotScope), - generateGetSlot(slotScope, slotNumber), - (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())), - ins(new SetSlotIns(), slotNumber) - ); - } else { - return toSourceMerge(localData, generator, - ins(isInteger ? new IncLocalIIns() : new IncLocalIns(), regNumber)); - } - } - return toSourceMerge(localData, generator, - slotNumber > -1 ? ins(new GetScopeObjectIns(), slotScope) : null, - //Start get original - generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), - //End get original - !isInteger ? ins(new ConvertDIns()) : null, - //End get original - (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - needsReturn ? ins(new DupIns()) : null, - (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - generateSetLoc(regNumber), - slotNumber > -1 ? ins(new SetSlotIns(), slotNumber) : null - ); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetScopeObjectIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetSlotIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertBIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertUIns; +import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class NameAVM2Item extends AssignableAVM2Item { + + private String variableName; + + private boolean definition; + private int nsKind = -1; + public List openedNamespaces; + public int line; + public GraphTargetItem type; + private GraphTargetItem ns = null; + private int regNumber = -1; + public boolean unresolved = false; + private int slotNumber = -1; + private int slotScope = 0; + + public GraphTargetItem redirect; + + @Override + public AssignableAVM2Item copy() { + NameAVM2Item c = new NameAVM2Item(type, line, variableName, assignedValue, definition, openedNamespaces); + c.setNs(ns); + c.regNumber = regNumber; + c.unresolved = unresolved; + c.nsKind = nsKind; + return c; + } + + public void setSlotScope(int slotScope) { + this.slotScope = slotScope; + } + + public int getSlotScope() { + return slotScope; + } + + public void setNs(GraphTargetItem ns) { + this.ns = ns; + } + + public void setRegNumber(int regNumber) { + this.regNumber = regNumber; + } + + public int getSlotNumber() { + return slotNumber; + } + + public void setSlotNumber(int slotNumber) { + this.slotNumber = slotNumber; + } + + public int getRegNumber() { + return regNumber; + } + + public GraphTargetItem getNs() { + return ns; + } + + public void appendName(String name) { + this.variableName += "." + name; + } + + public void setDefinition(boolean definition) { + this.definition = definition; + } + + public void setNsKind(int nsKind) { + this.nsKind = nsKind; + } + + public int getNsKind() { + return nsKind; + } + + public String getVariableName() { + return variableName; + } + + public NameAVM2Item(GraphTargetItem type, int line, String variableName, GraphTargetItem storeValue, boolean definition, List openedNamespaces) { + super(storeValue); + this.variableName = variableName; + this.assignedValue = storeValue; + this.definition = definition; + this.line = line; + this.type = type; + this.openedNamespaces = openedNamespaces; + } + + public boolean isDefinition() { + return definition; + } + + public GraphTargetItem getStoreValue() { + return assignedValue; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + private int allNsSet(ABC abc) { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + public static GraphTargetItem getDefaultValue(String type) { + switch (type) { + case "*": + return new UndefinedAVM2Item(null); + case "int": + return new IntegerValueAVM2Item(null, 0L); + case "Number": + return new NanAVM2Item(null); + default: + return new NullAVM2Item(null); + } + } + + public static AVM2Instruction generateCoerce(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem ttype) throws CompilationException { + if (ttype instanceof UnresolvedAVM2Item) { + ttype = ((UnresolvedAVM2Item) ttype).resolved; + } + AVM2Instruction ins; + if (ttype instanceof UnboundedTypeItem) { + ins = ins(new CoerceAIns()); + } else { + switch (ttype.toString()) { + case "int": + ins = ins(new ConvertIIns()); + break; + case "*": + ins = ins(new CoerceAIns()); + break; + case "String": + ins = ins(new CoerceSIns()); + break; + case "Boolean": + ins = ins(new ConvertBIns()); + break; + case "uint": + ins = ins(new ConvertUIns()); + break; + default: + int type_index = AVM2SourceGenerator.resolveType(localData, ttype, ((AVM2SourceGenerator) generator).abc, ((AVM2SourceGenerator) generator).allABCs); + ins = ins(new CoerceIns(), type_index); + break; + } + } + return ins; + } + + private List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { + if (variableName != null && regNumber == -1 && slotNumber == -1 && ns == null) { + throw new CompilationException("No register or slot set for " + variableName, line); + } + if (definition && assignedValue == null) { + return new ArrayList<>(); + } + String name = variableName; + boolean attr = false; + if (name != null && name.startsWith("@")) { + name = name.substring(1); + attr = true; + } + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + Reference ns_temp = new Reference<>(-1); + Reference index_temp = new Reference<>(-1); + Reference ret_temp = new Reference<>(-1); + + if (assignedValue != null) { + List basicTypes = Arrays.asList("int", "Number"); + if (slotNumber > -1) { + return toSourceMerge(localData, generator, + ins(new GetScopeObjectIns(), slotScope), + assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(localData, generator, type) : null, needsReturn + ? dupSetTemp(localData, generator, ret_temp) : null, generateSetLoc(regNumber), slotNumber > -1 + ? ins(new SetSlotIns(), slotNumber) + : null, + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ret_temp))); + } else { + + return toSourceMerge(localData, generator, assignedValue, !(("" + assignedValue.returnType()).equals("" + type) && (basicTypes.contains("" + type))) ? generateCoerce(localData, generator, type) : null, needsReturn + ? ins(new DupIns()) : null, generateSetLoc(regNumber)); + } + } else { + return toSourceMerge(localData, generator, generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), + needsReturn ? null : ins(new PopIns())); + } + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (redirect != null) { + return redirect.toSource(localData, generator); + } + return toSource(localData, generator, true); + + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (redirect != null) { + return redirect.toSourceIgnoreReturnValue(localData, generator); + } + return toSource(localData, generator, false); + } + + @Override + public boolean hasReturnValue() { + if (definition) { + return false; + } + return true; + } + + @Override + public boolean needsSemicolon() { + if (definition) { + return true; + } + return false; + } + + @Override + public String toString() { + return variableName; + } + + @Override + public GraphTargetItem returnType() { + if (type == null) { + return TypeItem.UNBOUNDED; + } + return type; + } + + @Override + public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { + if (redirect != null) { + return ((AssignableAVM2Item) redirect).toSourceChange(localData, generator, post, decrement, needsReturn); + } + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + Reference ns_temp = new Reference<>(-1); + Reference name_temp = new Reference<>(-1); + Reference index_temp = new Reference<>(-1); + Reference ret_temp = new Reference<>(-1); + boolean isInteger = returnType().toString().equals("int"); + /* + + + */ + if (!needsReturn) { + if (slotNumber > -1) { + return toSourceMerge(localData, generator, + ins(new GetScopeObjectIns(), slotScope), + generateGetSlot(slotScope, slotNumber), + (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())), + ins(new SetSlotIns(), slotNumber) + ); + } else { + return toSourceMerge(localData, generator, + ins(isInteger ? new IncLocalIIns() : new IncLocalIns(), regNumber)); + } + } + return toSourceMerge(localData, generator, + slotNumber > -1 ? ins(new GetScopeObjectIns(), slotScope) : null, + //Start get original + generateGetLoc(regNumber), generateGetSlot(slotScope, slotNumber), + //End get original + !isInteger ? ins(new ConvertDIns()) : null, + //End get original + (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + needsReturn ? ins(new DupIns()) : null, + (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + generateSetLoc(regNumber), + slotNumber > -1 ? ins(new SetSlotIns(), slotNumber) : null + ); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java index 68e4aea61..e6a33eccc 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/NamespacedAVM2Item.java @@ -1,231 +1,231 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertSIns; -import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.dupSetTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.getTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.killTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.setTemp; -import static com.jpexs.decompiler.flash.abc.avm2.parser.script.NameAVM2Item.generateCoerce; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class NamespacedAVM2Item extends AssignableAVM2Item { - - public GraphTargetItem ns; - public String name; - public GraphTargetItem nameItem; - public GraphTargetItem obj; - public boolean attr; - public List openedNamespaces; - - public NamespacedAVM2Item(GraphTargetItem ns, String name, GraphTargetItem nameItem, GraphTargetItem obj, boolean attr, List openedNamespaces, GraphTargetItem storeValue) { - super(storeValue); - this.ns = ns; - this.nameItem = nameItem; - this.name = name; - this.obj = obj; - this.attr = attr; - this.openedNamespaces = openedNamespaces; - } - - private int allNsSet(ABC abc) { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - @Override - public AssignableAVM2Item copy() { - return new NamespacedAVM2Item(ns, name, nameItem, obj, attr, openedNamespaces, assignedValue); - } - - @Override - public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - boolean isInteger = returnType().toString().equals("int"); - Reference ns_temp = new Reference<>(-1); - Reference name_temp = new Reference<>(-1); - Reference ret_temp = new Reference<>(-1); - /*if (name == null && index != null) { - return toSourceMerge(localData, generator, - ns, generateCoerce(generator, new TypeItem("Namespace")), index, ins(new ConvertSIns()), - ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), - dupSetTemp(localData, generator, name_temp), - ns, generateCoerce(generator, new TypeItem("Namespace")), - dupSetTemp(localData, generator, ns_temp), - ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), - !isInteger ? ins(new ConvertDIns()) : null, - //End get original - (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - needsReturn ? ins(new DupIns()) : null, - (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - setTemp(localData, generator, ret_temp), - getTemp(localData, generator, name_temp), - getTemp(localData, generator, ns_temp), - getTemp(localData, generator, ret_temp), - ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), - killTemp(localData, generator, Arrays.asList(ret_temp, name_temp, ns_temp))); - } else - */ - if (name != null) { - return toSourceMerge(localData, generator, - ns, generateCoerce(localData, generator, new TypeItem("Namespace")), - ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), - dupSetTemp(localData, generator, name_temp), - ns, generateCoerce(localData, generator, new TypeItem("Namespace")), - dupSetTemp(localData, generator, ns_temp), - //Start get original - //getTemp(localData, generator, ns_temp), generateCoerce(generator, "Namespace"), ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAME, g.abc.constants.getStringId(variableName, true), 0, 0, 0, new ArrayList()), true)), - //getTemp(localData, generator, ns_temp), generateCoerce(generator, "Namespace"), - ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), - !isInteger ? ins(new ConvertDIns()) : null, - //End get original - (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - needsReturn ? ins(new DupIns()) : null, - (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - setTemp(localData, generator, ret_temp), - getTemp(localData, generator, name_temp), - getTemp(localData, generator, ns_temp), - getTemp(localData, generator, ret_temp), - ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), - killTemp(localData, generator, Arrays.asList(ret_temp, name_temp, ns_temp)) - ); - } else { - return new ArrayList<>(); - } - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return null; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.UNBOUNDED; - } - - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn, boolean call, List callargs, boolean delete,boolean construct) throws CompilationException { - AVM2SourceGenerator g = (AVM2SourceGenerator) generator; - Reference ns_temp = new Reference<>(-1); - Reference index_temp = new Reference<>(-1); - Reference ret_temp = new Reference<>(-1); - - Reference obj_temp = new Reference<>(-1); - - if (name == null) { - if (assignedValue != null) { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, nameItem, ins(new ConvertSIns()), obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), - ns, generateCoerce(localData, generator, new TypeItem("Namespace")), nameItem, ins(new ConvertSIns()), assignedValue, - needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, - ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) - ); - } else { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, nameItem, ins(new ConvertSIns()), obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), - call ? dupSetTemp(localData, generator, obj_temp) : null, - ns, generateCoerce(localData,generator, new TypeItem("Namespace")), nameItem, ins(new ConvertSIns()), - construct?callargs:null, - ins(construct?new ConstructPropIns():delete?new DeletePropertyIns():new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true),construct?callargs.size():null), - call ? getTemp(localData, generator, obj_temp) : null, - call ? callargs : null, - call ? ins(new CallIns(), callargs.size()) : null, - needsReturn ? null : ins(new PopIns()), - killTemp(localData, generator, Arrays.asList(obj_temp)) - ); - } - } else { - if (assignedValue != null) { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? generateCoerce(localData,generator, new TypeItem("Namespace")) : null, obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), - ns, generateCoerce(localData,generator, new TypeItem("Namespace")), assignedValue, - needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, - ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) - ); - } else { - return toSourceMerge(localData, generator, - obj == null ? ns : null, obj == null ? generateCoerce(localData,generator, new TypeItem("Namespace")) : null, obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), - call ? dupSetTemp(localData, generator, obj_temp) : null, - ns, generateCoerce(localData,generator, new TypeItem("Namespace")), - construct?callargs:null, - ins(construct?new ConstructPropIns():delete?new DeletePropertyIns():new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true),construct?callargs.size():null), - call ? getTemp(localData, generator, obj_temp) : null, - call ? callargs : null, - call ? ins(new CallIns(), callargs.size()) : null, - needsReturn ? null : ins(new PopIns()), - killTemp(localData, generator, Arrays.asList(obj_temp)) - ); - } - } - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, true, false, new ArrayList(),false,false); - } - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, false, false, new ArrayList(),false,false); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.ConstructPropIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.DeletePropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertSIns; +import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.dupSetTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.getTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.killTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.AssignableAVM2Item.setTemp; +import static com.jpexs.decompiler.flash.abc.avm2.parser.script.NameAVM2Item.generateCoerce; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class NamespacedAVM2Item extends AssignableAVM2Item { + + public GraphTargetItem ns; + public String name; + public GraphTargetItem nameItem; + public GraphTargetItem obj; + public boolean attr; + public List openedNamespaces; + + public NamespacedAVM2Item(GraphTargetItem ns, String name, GraphTargetItem nameItem, GraphTargetItem obj, boolean attr, List openedNamespaces, GraphTargetItem storeValue) { + super(storeValue); + this.ns = ns; + this.nameItem = nameItem; + this.name = name; + this.obj = obj; + this.attr = attr; + this.openedNamespaces = openedNamespaces; + } + + private int allNsSet(ABC abc) { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + @Override + public AssignableAVM2Item copy() { + return new NamespacedAVM2Item(ns, name, nameItem, obj, attr, openedNamespaces, assignedValue); + } + + @Override + public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + boolean isInteger = returnType().toString().equals("int"); + Reference ns_temp = new Reference<>(-1); + Reference name_temp = new Reference<>(-1); + Reference ret_temp = new Reference<>(-1); + /*if (name == null && index != null) { + return toSourceMerge(localData, generator, + ns, generateCoerce(generator, new TypeItem("Namespace")), index, ins(new ConvertSIns()), + ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), + dupSetTemp(localData, generator, name_temp), + ns, generateCoerce(generator, new TypeItem("Namespace")), + dupSetTemp(localData, generator, ns_temp), + ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), + !isInteger ? ins(new ConvertDIns()) : null, + //End get original + (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + needsReturn ? ins(new DupIns()) : null, + (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + setTemp(localData, generator, ret_temp), + getTemp(localData, generator, name_temp), + getTemp(localData, generator, ns_temp), + getTemp(localData, generator, ret_temp), + ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), + killTemp(localData, generator, Arrays.asList(ret_temp, name_temp, ns_temp))); + } else + */ + if (name != null) { + return toSourceMerge(localData, generator, + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), + ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), + dupSetTemp(localData, generator, name_temp), + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), + dupSetTemp(localData, generator, ns_temp), + //Start get original + //getTemp(localData, generator, ns_temp), generateCoerce(generator, "Namespace"), ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAME, g.abc.constants.getStringId(variableName, true), 0, 0, 0, new ArrayList()), true)), + //getTemp(localData, generator, ns_temp), generateCoerce(generator, "Namespace"), + ins(new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), + !isInteger ? ins(new ConvertDIns()) : null, + //End get original + (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + needsReturn ? ins(new DupIns()) : null, + (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + setTemp(localData, generator, ret_temp), + getTemp(localData, generator, name_temp), + getTemp(localData, generator, ns_temp), + getTemp(localData, generator, ret_temp), + ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, 0, 0, allNsSet(g.abc), 0, new ArrayList()), true)), + killTemp(localData, generator, Arrays.asList(ret_temp, name_temp, ns_temp)) + ); + } else { + return new ArrayList<>(); + } + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return null; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.UNBOUNDED; + } + + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn, boolean call, List callargs, boolean delete, boolean construct) throws CompilationException { + AVM2SourceGenerator g = (AVM2SourceGenerator) generator; + Reference ns_temp = new Reference<>(-1); + Reference index_temp = new Reference<>(-1); + Reference ret_temp = new Reference<>(-1); + + Reference obj_temp = new Reference<>(-1); + + if (name == null) { + if (assignedValue != null) { + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, nameItem, ins(new ConvertSIns()), obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), nameItem, ins(new ConvertSIns()), assignedValue, + needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, + ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) + ); + } else { + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, nameItem, ins(new ConvertSIns()), obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true)), + call ? dupSetTemp(localData, generator, obj_temp) : null, + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), nameItem, ins(new ConvertSIns()), + construct ? callargs : null, + ins(construct ? new ConstructPropIns() : delete ? new DeletePropertyIns() : new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(Multiname.RTQNAMEL, 0, 0, 0, 0, new ArrayList()), true), construct ? callargs.size() : null), + call ? getTemp(localData, generator, obj_temp) : null, + call ? callargs : null, + call ? ins(new CallIns(), callargs.size()) : null, + needsReturn ? null : ins(new PopIns()), + killTemp(localData, generator, Arrays.asList(obj_temp)) + ); + } + } else { + if (assignedValue != null) { + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), assignedValue, + needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, + ins(new SetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ns_temp, index_temp, ret_temp)) + ); + } else { + return toSourceMerge(localData, generator, + obj == null ? ns : null, obj == null ? generateCoerce(localData, generator, new TypeItem("Namespace")) : null, obj != null ? obj : ins(new FindPropertyStrictIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true)), + call ? dupSetTemp(localData, generator, obj_temp) : null, + ns, generateCoerce(localData, generator, new TypeItem("Namespace")), + construct ? callargs : null, + ins(construct ? new ConstructPropIns() : delete ? new DeletePropertyIns() : new GetPropertyIns(), g.abc.constants.getMultinameId(new Multiname(attr ? Multiname.RTQNAMEA : Multiname.RTQNAME, g.abc.constants.getStringId(name, true), 0, 0, 0, new ArrayList()), true), construct ? callargs.size() : null), + call ? getTemp(localData, generator, obj_temp) : null, + call ? callargs : null, + call ? ins(new CallIns(), callargs.size()) : null, + needsReturn ? null : ins(new PopIns()), + killTemp(localData, generator, Arrays.asList(obj_temp)) + ); + } + } + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, true, false, new ArrayList(), false, false); + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, false, false, new ArrayList(), false, false); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java index 56e887e8d..7dea854e7 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PackageAVM2Item.java @@ -1,60 +1,60 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class PackageAVM2Item extends AVM2Item { - - public List items; - public String packageName; - public List importedClasses=new ArrayList(); - public int publicNs = 0; - - public PackageAVM2Item(int publicNs,List importedClasses, String packageName, List items) { - super(null, NOPRECEDENCE); - this.publicNs = publicNs; - this.importedClasses = importedClasses; - this.items = items; - this.packageName = packageName; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - @Override - public GraphTargetItem returnType() { - return new UnboundedTypeItem(); //FIXME - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class PackageAVM2Item extends AVM2Item { + + public List items; + public String packageName; + public List importedClasses = new ArrayList(); + public int publicNs = 0; + + public PackageAVM2Item(int publicNs, List importedClasses, String packageName, List items) { + super(null, NOPRECEDENCE); + this.publicNs = publicNs; + this.importedClasses = importedClasses; + this.items = items; + this.packageName = packageName; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + @Override + public GraphTargetItem returnType() { + return new UnboundedTypeItem(); //FIXME + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java index a454f8fd5..aa3ca0ae4 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/PropertyAVM2Item.java @@ -1,688 +1,685 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; -import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; -import com.jpexs.decompiler.flash.abc.types.InstanceInfo; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class PropertyAVM2Item extends AssignableAVM2Item { - - public String propertyName; - public GraphTargetItem object; - public ABC abc; - public List otherABCs; - private List openedNamespaces; - private List callStack; - public List scopeStack = new ArrayList(); - - @Override - public AssignableAVM2Item copy() { - PropertyAVM2Item p = new PropertyAVM2Item(object, propertyName, abc, otherABCs, openedNamespaces, callStack); - return p; - } - - public PropertyAVM2Item(GraphTargetItem object, String propertyName, ABC abc, List otherABCs, List openedNamespaces, List callStack) { - this.propertyName = propertyName; - this.object = object; - this.otherABCs = otherABCs; - this.abc = abc; - this.openedNamespaces = openedNamespaces; - this.callStack = callStack; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - private int allNsSet() { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - - public static GraphTargetItem multinameToType(int m_index,ConstantPool constants){ - if(m_index == 0){ - return TypeItem.UNBOUNDED; - } - Multiname m = constants.constant_multiname.get(m_index); - if(m.kind == Multiname.TYPENAME){ - GraphTargetItem obj = multinameToType(m.qname_index,constants); - List params =new ArrayList<>(); - for(int pm:m.params){ - params.add(multinameToType(pm, constants)); - } - return new ApplyTypeAVM2Item(null, obj, params); - }else { - return new TypeItem(m.getNameWithNamespace(constants)); - } - } - - public void resolve(SourceGeneratorLocalData localData, Reference objectType, Reference propertyType, Reference propertyIndex, Reference propertyValue) { - GraphTargetItem thisType = new TypeItem(localData.getFullClass()); - GraphTargetItem objType = null; - GraphTargetItem objSubType = null; - ValueKind propValue = null; - if (object != null) { - GraphTargetItem oretType = object.returnType(); - if (oretType instanceof UnresolvedAVM2Item) { - UnresolvedAVM2Item ur = (UnresolvedAVM2Item) oretType; - oretType = ur.resolved; - } - if (oretType instanceof ApplyTypeAVM2Item) { - ApplyTypeAVM2Item t = (ApplyTypeAVM2Item) oretType; - objType = t; - } else if (oretType instanceof TypeItem) { - TypeItem t = (TypeItem) oretType; - objType = t; - } else { - objType = new TypeItem(oretType.toString()); - } - } - GraphTargetItem propType = null; - int propIndex = 0; - if (!propertyName.startsWith("@")) { - - if (scopeStack.isEmpty()) { //Everything is multiname when with command - if (objType == null) { - - /*for (GraphTargetItem s : scopeStack) { - String oType = s.returnType().toString(); - String name = oType; - String nsname = ""; - if(name.contains(".")){ - nsname = name.substring(0,name.lastIndexOf(".")); - name = name.substring(name.lastIndexOf(".")+1); - } - - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(otherABCs); - loopabc: - for (ABC a : abcs) { - for (int h = 0; h < a.instance_info.size(); h++) { - InstanceInfo ii = a.instance_info.get(h); - Multiname n = a.constants.constant_multiname.get(ii.name_index); - if (name.equals(n.getName(a.constants, new ArrayList())) && n.getNamespace(a.constants).hasName(nsname,a.constants)) { - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropType = new Reference<>(""); - if (AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, name, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropType)) { - objType = "".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal(); - propType = outPropType.getVal(); - propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, - abc.constants.getStringId(propertyName, true), - abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), 0, true), 0, 0, new ArrayList()), true - ); - - break loopabc; - } - } - } - } - }*/ - } - - GraphTargetItem ttype = objType; - if(ttype == null){ - ttype = thisType; - } - - - - { - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(otherABCs); - if (ttype.equals(new TypeItem("__AS3__.vec.Vector"))) { - switch ("" + objSubType) { - case "int": - ttype = new TypeItem("__AS3__.vec.Vector$int"); - break; - case "Number": - ttype = new TypeItem("__AS3__.vec.Vector$double"); - break; - case "uint": - ttype = new TypeItem("__AS3__.vec.Vector$uint"); - break; - default: - ttype = new TypeItem("__AS3__.vec.Vector$object"); - } - } - loopa: - for (ABC a : abcs) { - for (InstanceInfo ii : a.instance_info) { - if(ii.deleted){ - continue; - } - Multiname m = ii.getName(a.constants); - if (multinameToType(ii.name_index, a.constants).equals(ttype)) { - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropNsIndex = new Reference<>(0); - Reference outPropType = new Reference<>(null); - Reference outPropValue = new Reference<>(null); - if (AVM2SourceGenerator.searchPrototypeChain(false, abcs, m.getNamespace(a.constants).getName(a.constants), m.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) { - objType = new TypeItem("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()); - propType = outPropType.getVal(); - propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, - abc.constants.getStringId(propertyName, true), - abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), outPropNsIndex.getVal(), true), 0, 0, new ArrayList()), true - ); - propValue = outPropValue.getVal(); - - break loopa; - } - } - } - } - } - - if (objType == null) { - for (MethodBody b : callStack) { - for (int i = 0; i < b.traits.traits.size(); i++) { - Trait t = b.traits.traits.get(i); - if (t.getName(abc).getName(abc.constants, new ArrayList()).equals(propertyName)) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - objType = new TypeItem("Function"); - propType = multinameToType(tsc.type_index, abc.constants); - propIndex = tsc.name_index; - if (!localData.traitUsages.containsKey(b)) { - localData.traitUsages.put(b, new ArrayList()); - } - localData.traitUsages.get(b).add(i); - } - } - } - } - if (objType == null) { - loopobjType: - for (int i = 0; i < openedNamespaces.size(); i++) { - int nsindex = openedNamespaces.get(i); - int nsKind = abc.constants.constant_namespace.get(openedNamespaces.get(i)).kind; - String nsname = abc.constants.constant_namespace.get(openedNamespaces.get(i)).getName(abc.constants); - int name_index = 0; - for (int m = 1; m < abc.constants.constant_multiname.size(); m++) { - Multiname mname = abc.constants.constant_multiname.get(m); - if (mname.kind == Multiname.QNAME && mname.getName(abc.constants, new ArrayList()).equals(propertyName) && mname.namespace_index == nsindex) { - name_index = m; - break; - } - } - if (name_index > 0) { - for (int c = 0; c < abc.instance_info.size(); c++) { - if(abc.instance_info.get(c).deleted){ - continue; - } - for (Trait t : abc.instance_info.get(c).instance_traits.traits) { - if (t.name_index == name_index) { - objType = multinameToType(abc.instance_info.get(c).name_index, abc.constants); - propType = AVM2SourceGenerator.getTraitReturnType(abc, t); - propIndex = t.name_index; - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - propValue = new ValueKind(tsc.value_index, tsc.value_kind); - } - break loopobjType; - } - } - for (Trait t : abc.class_info.get(c).static_traits.traits) { - if (t.name_index == name_index) { - objType = multinameToType(abc.instance_info.get(c).name_index,abc.constants); - propType = AVM2SourceGenerator.getTraitReturnType(abc, t); - propIndex = t.name_index; - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - propValue = new ValueKind(tsc.value_index, tsc.value_kind); - } - break loopobjType; - } - } - } - - for (ScriptInfo si : abc.script_info) { - if(si.deleted){ - continue; - } - for (Trait t : si.traits.traits) { - if (t.name_index == name_index) { - objType = new TypeItem("Object"); - propType = AVM2SourceGenerator.getTraitReturnType(abc, t); - propIndex = t.name_index; - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - propValue = new ValueKind(tsc.value_index, tsc.value_kind); - } - break loopobjType; - } - } - } - } - if (nsKind == Namespace.KIND_PACKAGE) { - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(otherABCs); - loopabc: - for (ABC a : otherABCs) { - for (int h = 0; h < a.instance_info.size(); h++) { - InstanceInfo ii = a.instance_info.get(h); - if(ii.deleted){ - continue; - } - Multiname n = a.constants.constant_multiname.get(ii.name_index); - if (n.getNamespace(a.constants).kind == Namespace.KIND_PACKAGE && n.getNamespace(a.constants).getName(a.constants).equals(nsname)) { - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropNsIndex = new Reference<>(0); - Reference outPropType = new Reference<>(null); - Reference outPropValue = new Reference<>(null); - - if (propertyName != null && AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, n.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) { - objType = new TypeItem("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()); - propType = outPropType.getVal(); - propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, - abc.constants.getStringId(propertyName, true), - abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), outPropNsIndex.getVal(), true), 0, 0, new ArrayList()), true - ); - propValue = outPropValue.getVal(); - break loopobjType; - } - } - } - } - } - } - } - } - - } - } - - if (propIndex == 0) { - String pname = propertyName; - boolean attr = pname.startsWith("@"); - if (attr) { - pname = pname.substring(1); - } - propIndex = abc.constants.getMultinameId(new Multiname(attr ? (pname.isEmpty() ? Multiname.MULTINAMELA : Multiname.MULTINAMEA) : Multiname.MULTINAME, - abc.constants.getStringId("*".equals(pname) ? null : pname, true), 0, //Note: name = * is for .@* attribute - attr && pname.isEmpty() ? abc.constants.getNamespaceSetId(new NamespaceSet(new int[]{abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(localData.pkg, true)), 0, true)}), true) : allNsSet(), 0, new ArrayList()), true); - propType = TypeItem.UNBOUNDED; - objType = TypeItem.UNBOUNDED; - propValue = null; - - } - propertyValue.setVal(propValue); - propertyIndex.setVal(propIndex); - propertyType.setVal(propType); - objectType.setVal(objType); - } - - public int resolveProperty(SourceGeneratorLocalData localData) { - Reference objType = new Reference<>(null); - Reference propType = new Reference<>(null); - Reference propIndex = new Reference<>(0); - Reference outPropValue = new Reference<>(null); - resolve(localData, objType, propType, propIndex, outPropValue); - return propIndex.getVal(); - } - - /* - private String resolveObjectType() { - String objType = object == null ? null : object.returnType().toString(); - if (objType == null) { - loopo: - for (int i = 0; i < openedNamespaces.size(); i++) { - int nsindex = openedNamespaces.get(i); - int nsKind = abc.constants.constant_namespace.get(openedNamespaces.get(i)).kind; - String nsname = abc.constants.constant_namespace.get(openedNamespaces.get(i)).getName(abc.constants); - int name_index = 0; - for (int m = 1; m < abc.constants.constant_multiname.size(); m++) { - Multiname mname = abc.constants.constant_multiname.get(m); - if (mname.kind == Multiname.QNAME && mname.getName(abc.constants, new ArrayList()).equals(propertyName) && mname.namespace_index == nsindex) { - name_index = m; - break; - } - } - if (name_index > 0) { - for (int s = 0; s < abc.script_info.size(); s++) { - for (Trait t : abc.script_info.get(s).traits.traits) { - if (t.name_index == name_index) { - return getTraitReturnType(abc, t).toString(); - } - } - } - for (int c = 0; c < abc.instance_info.size(); c++) { - for (Trait t : abc.instance_info.get(c).instance_traits.traits) { - if (t.name_index == name_index) { - return getTraitReturnType(abc, t).toString(); - } - } - for (Trait t : abc.class_info.get(c).static_traits.traits) { - if (t.name_index == name_index) { - return getTraitReturnType(abc, t).toString(); - } - } - } - } - if (nsKind == Namespace.KIND_PACKAGE) { - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(otherABCs); - loopabc: - for (ABC a : otherABCs) { - for (int h = 0; h < a.instance_info.size(); h++) { - InstanceInfo ii = a.instance_info.get(h); - Multiname n = a.constants.constant_multiname.get(ii.name_index); - if (n.getNamespace(a.constants).kind == Namespace.KIND_PACKAGE && n.getNamespace(a.constants).getName(a.constants).equals(nsname)) { - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - if (AVM2SourceGenerator.searchPrototypeChain(abcs, nsname, n.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind)) { - return "".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal(); - } - } - } - } - } - } - } - if (objType == null) { - throw new RuntimeException("Unresolved object type"); - } - return objType; - }*/ - - /* - public GraphTargetItem resolvePropertyType() { - if (index != null) { - return TypeItem.UNBOUNDED; - } - - String objType = resolveObjectType(); - for (ABC a : abcs) { - int ci = a.findClassByName(objType); - if (ci != -1) { - for (Trait t : a.instance_info.get(ci).instance_traits.traits) { - String tnames = t.getName(a).getName(a.constants, new ArrayList()); - if (tnames.equals(propertyName)) { - if (t instanceof TraitSlotConst) { - TraitSlotConst tsc = (TraitSlotConst) t; - if (tsc.type_index == 0) { - return TypeItem.UNBOUNDED; - } - return new TypeItem(a.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(a.constants)); - } - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; - if (tmgs.kindType == Trait.TRAIT_GETTER) { - return new TypeItem(a.constants.constant_multiname.get(a.method_info.get(tmgs.method_info).ret_type).getNameWithNamespace(a.constants)); - } - if (tmgs.kindType == Trait.TRAIT_SETTER) { - return new TypeItem(a.constants.constant_multiname.get(a.method_info.get(tmgs.method_info).param_types[0]).getNameWithNamespace(a.constants)); - } - } - if (t instanceof TraitFunction) { - return new TypeItem("Function"); - } - return TypeItem.UNBOUNDED; - } - } - break; - } - } - return TypeItem.UNBOUNDED; - } - */ - /* public int resolveProperty() { - if (index != null) { - return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, - abc.constants.getStringId(propertyName, true), 0, - allNsSet(), 0, new ArrayList()), true); - } - - String objType = resolveObjectType(); - for (ABC a : abcs) { - int ci = a.findClassByName(objType); - if (ci != -1) { - for (Trait t : a.instance_info.get(ci).instance_traits.traits) { - Multiname tname = t.getName(a); - String tnames = t.getName(a).getName(a.constants, new ArrayList()); - if (tnames.equals(propertyName)) { - return abc.constants.getMultinameId(new Multiname(tname.kind, - abc.constants.getStringId(tnames, true), - abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - for (Trait t : a.class_info.get(ci).static_traits.traits) { - Multiname tname = t.getName(a); - String tnames = t.getName(a).getName(a.constants, new ArrayList()); - if (tnames.equals(propertyName)) { - return abc.constants.getMultinameId(new Multiname(tname.kind, - abc.constants.getStringId(tnames, true), - abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - break; - } - } - - for (ABC a : abcs) { - for (ScriptInfo si : a.script_info) { - for (Trait t : si.traits.traits) { - Multiname tname = t.getName(a); - String tnames = t.getName(a).getName(a.constants, new ArrayList()); - if (tnames.equals(propertyName)) { - return abc.constants.getMultinameId(new Multiname(tname.kind, - abc.constants.getStringId(tnames, true), - abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); - } - } - } - } - - return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, - abc.constants.getStringId(propertyName, true), 0, - allNsSet(), 0, new ArrayList()), true); - }*/ - @Override - public GraphTargetItem returnType() { - Reference objType = new Reference<>(null); - Reference propType = new Reference<>(null); - Reference propIndex = new Reference<>(0); - Reference outPropValue = new Reference<>(null); - resolve(new SourceGeneratorLocalData(new HashMap(), 0, false, 0)/*???*/, objType, propType, propIndex, outPropValue); - - return propType.getVal(); - } - - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { - - Reference objType = new Reference<>(null); - Reference propType = new Reference<>(null); - Reference propIndex = new Reference<>(0); - Reference outPropValue = new Reference<>(null); - resolve(localData, objType, propType, propIndex, outPropValue); - - int propertyId = propIndex.getVal(); - Object obj = resolveObject(localData, generator); - Reference ret_temp = new Reference<>(-1); - if (assignedValue != null) { - GraphTargetItem targetType = propType.getVal(); - String srcType = assignedValue.returnType().toString(); - GraphTargetItem coerced = assignedValue; - if (!targetType.equals(srcType) && !propertyName.startsWith("@")) { - coerced = new CoerceAVM2Item(null, assignedValue, targetType); - } - return toSourceMerge(localData, generator, obj, coerced, - needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, - ins(new SetPropertyIns(), propertyId), - needsReturn ? getTemp(localData, generator, ret_temp) : null, - killTemp(localData, generator, Arrays.asList(ret_temp))); - } else { - if (obj instanceof AVM2Instruction && (((AVM2Instruction) obj).definition instanceof FindPropertyStrictIns)) { - return toSourceMerge(localData, generator, ins(new GetLexIns(), propertyId), - needsReturn ? null : ins(new PopIns()) - ); - } - return toSourceMerge(localData, generator, obj, ins(new GetPropertyIns(), propertyId), - needsReturn ? null : ins(new PopIns()) - ); - } - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, true); - } - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSource(localData, generator, false); - } - - @Override - public String toString() { - return "" + object + "." + propertyName; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - public Object resolveObject(SourceGeneratorLocalData localData, SourceGenerator generator) { - Object obj = object; - - if (obj == null) { - String cname; - String pkgName = ""; - cname = localData.currentClass; - pkgName = localData.pkg; - Reference outName = new Reference<>(""); - Reference outNs = new Reference<>(""); - Reference outPropNs = new Reference<>(""); - Reference outPropNsKind = new Reference<>(1); - Reference outPropNsIndex = new Reference<>(0); - Reference outPropType = new Reference<>(null); - Reference outPropValue = new Reference<>(null); - List abcs = new ArrayList<>(); - abcs.add(abc); - abcs.addAll(otherABCs); - if (cname != null && AVM2SourceGenerator.searchPrototypeChain(true, abcs, pkgName, cname, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue) && (localData.currentClass.equals("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()))) { - NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), 0, "this", null, false, openedNamespaces); - nobj.setRegNumber(0); - obj = nobj; - } else { - Reference objType = new Reference<>(null); - Reference propType = new Reference<>(null); - Reference propIndex = new Reference<>(0); - Reference propValue = new Reference<>(null); - resolve(localData, objType, propType, propIndex, outPropValue); - obj = ins(new FindPropertyStrictIns(), propIndex.getVal()); - } - } - return obj; - } - - @Override - public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { - - Reference objType = new Reference<>(null); - Reference propType = new Reference<>(null); - Reference propIndex = new Reference<>(0); - Reference outPropValue = new Reference<>(null); - resolve(localData, objType, propType, propIndex, outPropValue); - - int propertyId = propIndex.getVal(); - Object obj = resolveObject(localData, generator); - - Reference ret_temp = new Reference<>(-1); - Reference obj_temp = new Reference<>(-1); - - boolean isInteger = propType.getVal().equals("int"); - - List ret = toSourceMerge(localData, generator, obj, dupSetTemp(localData, generator, obj_temp), - //Start get original - //getTemp(localData, generator, obj_temp), - //index!=null?getTemp(localData, generator, index_temp):null, - ins(new GetPropertyIns(), propertyId), - (!isInteger && post) ? ins(new ConvertDIns()) : null, - //End get original - (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - needsReturn ? ins(new DupIns()) : null, - (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, - setTemp(localData, generator, ret_temp), - getTemp(localData, generator, obj_temp), - getTemp(localData, generator, ret_temp), - ins(new SetPropertyIns(), propertyId), - //needsReturn?getTemp(localData, generator, ret_temp):null, - killTemp(localData, generator, Arrays.asList(ret_temp, obj_temp))); - return ret; - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.DecrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.arithmetic.IncrementIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.SetPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.DupIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertDIns; +import com.jpexs.decompiler.flash.abc.avm2.model.ApplyTypeAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.CoerceAVM2Item; +import com.jpexs.decompiler.flash.abc.types.InstanceInfo; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class PropertyAVM2Item extends AssignableAVM2Item { + + public String propertyName; + public GraphTargetItem object; + public ABC abc; + public List otherABCs; + private List openedNamespaces; + private List callStack; + public List scopeStack = new ArrayList(); + + @Override + public AssignableAVM2Item copy() { + PropertyAVM2Item p = new PropertyAVM2Item(object, propertyName, abc, otherABCs, openedNamespaces, callStack); + return p; + } + + public PropertyAVM2Item(GraphTargetItem object, String propertyName, ABC abc, List otherABCs, List openedNamespaces, List callStack) { + this.propertyName = propertyName; + this.object = object; + this.otherABCs = otherABCs; + this.abc = abc; + this.openedNamespaces = openedNamespaces; + this.callStack = callStack; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + private int allNsSet() { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + public static GraphTargetItem multinameToType(int m_index, ConstantPool constants) { + if (m_index == 0) { + return TypeItem.UNBOUNDED; + } + Multiname m = constants.constant_multiname.get(m_index); + if (m.kind == Multiname.TYPENAME) { + GraphTargetItem obj = multinameToType(m.qname_index, constants); + List params = new ArrayList<>(); + for (int pm : m.params) { + params.add(multinameToType(pm, constants)); + } + return new ApplyTypeAVM2Item(null, obj, params); + } else { + return new TypeItem(m.getNameWithNamespace(constants)); + } + } + + public void resolve(SourceGeneratorLocalData localData, Reference objectType, Reference propertyType, Reference propertyIndex, Reference propertyValue) { + GraphTargetItem thisType = new TypeItem(localData.getFullClass()); + GraphTargetItem objType = null; + GraphTargetItem objSubType = null; + ValueKind propValue = null; + if (object != null) { + GraphTargetItem oretType = object.returnType(); + if (oretType instanceof UnresolvedAVM2Item) { + UnresolvedAVM2Item ur = (UnresolvedAVM2Item) oretType; + oretType = ur.resolved; + } + if (oretType instanceof ApplyTypeAVM2Item) { + ApplyTypeAVM2Item t = (ApplyTypeAVM2Item) oretType; + objType = t; + } else if (oretType instanceof TypeItem) { + TypeItem t = (TypeItem) oretType; + objType = t; + } else { + objType = new TypeItem(oretType.toString()); + } + } + GraphTargetItem propType = null; + int propIndex = 0; + if (!propertyName.startsWith("@")) { + + if (scopeStack.isEmpty()) { //Everything is multiname when with command + if (objType == null) { + + /*for (GraphTargetItem s : scopeStack) { + String oType = s.returnType().toString(); + String name = oType; + String nsname = ""; + if(name.contains(".")){ + nsname = name.substring(0,name.lastIndexOf(".")); + name = name.substring(name.lastIndexOf(".")+1); + } + + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(otherABCs); + loopabc: + for (ABC a : abcs) { + for (int h = 0; h < a.instance_info.size(); h++) { + InstanceInfo ii = a.instance_info.get(h); + Multiname n = a.constants.constant_multiname.get(ii.name_index); + if (name.equals(n.getName(a.constants, new ArrayList())) && n.getNamespace(a.constants).hasName(nsname,a.constants)) { + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropType = new Reference<>(""); + if (AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, name, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropType)) { + objType = "".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal(); + propType = outPropType.getVal(); + propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, + abc.constants.getStringId(propertyName, true), + abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), 0, true), 0, 0, new ArrayList()), true + ); + + break loopabc; + } + } + } + } + }*/ + } + + GraphTargetItem ttype = objType; + if (ttype == null) { + ttype = thisType; + } + + { + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(otherABCs); + if (ttype.equals(new TypeItem("__AS3__.vec.Vector"))) { + switch ("" + objSubType) { + case "int": + ttype = new TypeItem("__AS3__.vec.Vector$int"); + break; + case "Number": + ttype = new TypeItem("__AS3__.vec.Vector$double"); + break; + case "uint": + ttype = new TypeItem("__AS3__.vec.Vector$uint"); + break; + default: + ttype = new TypeItem("__AS3__.vec.Vector$object"); + } + } + loopa: + for (ABC a : abcs) { + for (InstanceInfo ii : a.instance_info) { + if (ii.deleted) { + continue; + } + Multiname m = ii.getName(a.constants); + if (multinameToType(ii.name_index, a.constants).equals(ttype)) { + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropNsIndex = new Reference<>(0); + Reference outPropType = new Reference<>(null); + Reference outPropValue = new Reference<>(null); + if (AVM2SourceGenerator.searchPrototypeChain(false, abcs, m.getNamespace(a.constants).getName(a.constants), m.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) { + objType = new TypeItem("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()); + propType = outPropType.getVal(); + propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, + abc.constants.getStringId(propertyName, true), + abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), outPropNsIndex.getVal(), true), 0, 0, new ArrayList()), true + ); + propValue = outPropValue.getVal(); + + break loopa; + } + } + } + } + } + + if (objType == null) { + for (MethodBody b : callStack) { + for (int i = 0; i < b.traits.traits.size(); i++) { + Trait t = b.traits.traits.get(i); + if (t.getName(abc).getName(abc.constants, new ArrayList()).equals(propertyName)) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + objType = new TypeItem("Function"); + propType = multinameToType(tsc.type_index, abc.constants); + propIndex = tsc.name_index; + if (!localData.traitUsages.containsKey(b)) { + localData.traitUsages.put(b, new ArrayList()); + } + localData.traitUsages.get(b).add(i); + } + } + } + } + if (objType == null) { + loopobjType: + for (int i = 0; i < openedNamespaces.size(); i++) { + int nsindex = openedNamespaces.get(i); + int nsKind = abc.constants.constant_namespace.get(openedNamespaces.get(i)).kind; + String nsname = abc.constants.constant_namespace.get(openedNamespaces.get(i)).getName(abc.constants); + int name_index = 0; + for (int m = 1; m < abc.constants.constant_multiname.size(); m++) { + Multiname mname = abc.constants.constant_multiname.get(m); + if (mname.kind == Multiname.QNAME && mname.getName(abc.constants, new ArrayList()).equals(propertyName) && mname.namespace_index == nsindex) { + name_index = m; + break; + } + } + if (name_index > 0) { + for (int c = 0; c < abc.instance_info.size(); c++) { + if (abc.instance_info.get(c).deleted) { + continue; + } + for (Trait t : abc.instance_info.get(c).instance_traits.traits) { + if (t.name_index == name_index) { + objType = multinameToType(abc.instance_info.get(c).name_index, abc.constants); + propType = AVM2SourceGenerator.getTraitReturnType(abc, t); + propIndex = t.name_index; + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + propValue = new ValueKind(tsc.value_index, tsc.value_kind); + } + break loopobjType; + } + } + for (Trait t : abc.class_info.get(c).static_traits.traits) { + if (t.name_index == name_index) { + objType = multinameToType(abc.instance_info.get(c).name_index, abc.constants); + propType = AVM2SourceGenerator.getTraitReturnType(abc, t); + propIndex = t.name_index; + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + propValue = new ValueKind(tsc.value_index, tsc.value_kind); + } + break loopobjType; + } + } + } + + for (ScriptInfo si : abc.script_info) { + if (si.deleted) { + continue; + } + for (Trait t : si.traits.traits) { + if (t.name_index == name_index) { + objType = new TypeItem("Object"); + propType = AVM2SourceGenerator.getTraitReturnType(abc, t); + propIndex = t.name_index; + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + propValue = new ValueKind(tsc.value_index, tsc.value_kind); + } + break loopobjType; + } + } + } + } + if (nsKind == Namespace.KIND_PACKAGE) { + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(otherABCs); + loopabc: + for (ABC a : otherABCs) { + for (int h = 0; h < a.instance_info.size(); h++) { + InstanceInfo ii = a.instance_info.get(h); + if (ii.deleted) { + continue; + } + Multiname n = a.constants.constant_multiname.get(ii.name_index); + if (n.getNamespace(a.constants).kind == Namespace.KIND_PACKAGE && n.getNamespace(a.constants).getName(a.constants).equals(nsname)) { + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropNsIndex = new Reference<>(0); + Reference outPropType = new Reference<>(null); + Reference outPropValue = new Reference<>(null); + + if (propertyName != null && AVM2SourceGenerator.searchPrototypeChain(false, abcs, nsname, n.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue)) { + objType = new TypeItem("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()); + propType = outPropType.getVal(); + propIndex = abc.constants.getMultinameId(new Multiname(Multiname.QNAME, + abc.constants.getStringId(propertyName, true), + abc.constants.getNamespaceId(new Namespace(outPropNsKind.getVal(), abc.constants.getStringId(outPropNs.getVal(), true)), outPropNsIndex.getVal(), true), 0, 0, new ArrayList()), true + ); + propValue = outPropValue.getVal(); + break loopobjType; + } + } + } + } + } + } + } + } + + } + } + + if (propIndex == 0) { + String pname = propertyName; + boolean attr = pname.startsWith("@"); + if (attr) { + pname = pname.substring(1); + } + propIndex = abc.constants.getMultinameId(new Multiname(attr ? (pname.isEmpty() ? Multiname.MULTINAMELA : Multiname.MULTINAMEA) : Multiname.MULTINAME, + abc.constants.getStringId("*".equals(pname) ? null : pname, true), 0, //Note: name = * is for .@* attribute + attr && pname.isEmpty() ? abc.constants.getNamespaceSetId(new NamespaceSet(new int[]{abc.constants.getNamespaceId(new Namespace(Namespace.KIND_PACKAGE_INTERNAL, abc.constants.getStringId(localData.pkg, true)), 0, true)}), true) : allNsSet(), 0, new ArrayList()), true); + propType = TypeItem.UNBOUNDED; + objType = TypeItem.UNBOUNDED; + propValue = null; + + } + propertyValue.setVal(propValue); + propertyIndex.setVal(propIndex); + propertyType.setVal(propType); + objectType.setVal(objType); + } + + public int resolveProperty(SourceGeneratorLocalData localData) { + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndex = new Reference<>(0); + Reference outPropValue = new Reference<>(null); + resolve(localData, objType, propType, propIndex, outPropValue); + return propIndex.getVal(); + } + + /* + private String resolveObjectType() { + String objType = object == null ? null : object.returnType().toString(); + if (objType == null) { + loopo: + for (int i = 0; i < openedNamespaces.size(); i++) { + int nsindex = openedNamespaces.get(i); + int nsKind = abc.constants.constant_namespace.get(openedNamespaces.get(i)).kind; + String nsname = abc.constants.constant_namespace.get(openedNamespaces.get(i)).getName(abc.constants); + int name_index = 0; + for (int m = 1; m < abc.constants.constant_multiname.size(); m++) { + Multiname mname = abc.constants.constant_multiname.get(m); + if (mname.kind == Multiname.QNAME && mname.getName(abc.constants, new ArrayList()).equals(propertyName) && mname.namespace_index == nsindex) { + name_index = m; + break; + } + } + if (name_index > 0) { + for (int s = 0; s < abc.script_info.size(); s++) { + for (Trait t : abc.script_info.get(s).traits.traits) { + if (t.name_index == name_index) { + return getTraitReturnType(abc, t).toString(); + } + } + } + for (int c = 0; c < abc.instance_info.size(); c++) { + for (Trait t : abc.instance_info.get(c).instance_traits.traits) { + if (t.name_index == name_index) { + return getTraitReturnType(abc, t).toString(); + } + } + for (Trait t : abc.class_info.get(c).static_traits.traits) { + if (t.name_index == name_index) { + return getTraitReturnType(abc, t).toString(); + } + } + } + } + if (nsKind == Namespace.KIND_PACKAGE) { + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(otherABCs); + loopabc: + for (ABC a : otherABCs) { + for (int h = 0; h < a.instance_info.size(); h++) { + InstanceInfo ii = a.instance_info.get(h); + Multiname n = a.constants.constant_multiname.get(ii.name_index); + if (n.getNamespace(a.constants).kind == Namespace.KIND_PACKAGE && n.getNamespace(a.constants).getName(a.constants).equals(nsname)) { + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + if (AVM2SourceGenerator.searchPrototypeChain(abcs, nsname, n.getName(a.constants, new ArrayList()), propertyName, outName, outNs, outPropNs, outPropNsKind)) { + return "".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal(); + } + } + } + } + } + } + } + if (objType == null) { + throw new RuntimeException("Unresolved object type"); + } + return objType; + }*/ + + /* + public GraphTargetItem resolvePropertyType() { + if (index != null) { + return TypeItem.UNBOUNDED; + } + + String objType = resolveObjectType(); + for (ABC a : abcs) { + int ci = a.findClassByName(objType); + if (ci != -1) { + for (Trait t : a.instance_info.get(ci).instance_traits.traits) { + String tnames = t.getName(a).getName(a.constants, new ArrayList()); + if (tnames.equals(propertyName)) { + if (t instanceof TraitSlotConst) { + TraitSlotConst tsc = (TraitSlotConst) t; + if (tsc.type_index == 0) { + return TypeItem.UNBOUNDED; + } + return new TypeItem(a.constants.constant_multiname.get(tsc.type_index).getNameWithNamespace(a.constants)); + } + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; + if (tmgs.kindType == Trait.TRAIT_GETTER) { + return new TypeItem(a.constants.constant_multiname.get(a.method_info.get(tmgs.method_info).ret_type).getNameWithNamespace(a.constants)); + } + if (tmgs.kindType == Trait.TRAIT_SETTER) { + return new TypeItem(a.constants.constant_multiname.get(a.method_info.get(tmgs.method_info).param_types[0]).getNameWithNamespace(a.constants)); + } + } + if (t instanceof TraitFunction) { + return new TypeItem("Function"); + } + return TypeItem.UNBOUNDED; + } + } + break; + } + } + return TypeItem.UNBOUNDED; + } + */ + /* public int resolveProperty() { + if (index != null) { + return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAMEL, + abc.constants.getStringId(propertyName, true), 0, + allNsSet(), 0, new ArrayList()), true); + } + + String objType = resolveObjectType(); + for (ABC a : abcs) { + int ci = a.findClassByName(objType); + if (ci != -1) { + for (Trait t : a.instance_info.get(ci).instance_traits.traits) { + Multiname tname = t.getName(a); + String tnames = t.getName(a).getName(a.constants, new ArrayList()); + if (tnames.equals(propertyName)) { + return abc.constants.getMultinameId(new Multiname(tname.kind, + abc.constants.getStringId(tnames, true), + abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + for (Trait t : a.class_info.get(ci).static_traits.traits) { + Multiname tname = t.getName(a); + String tnames = t.getName(a).getName(a.constants, new ArrayList()); + if (tnames.equals(propertyName)) { + return abc.constants.getMultinameId(new Multiname(tname.kind, + abc.constants.getStringId(tnames, true), + abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + break; + } + } + + for (ABC a : abcs) { + for (ScriptInfo si : a.script_info) { + for (Trait t : si.traits.traits) { + Multiname tname = t.getName(a); + String tnames = t.getName(a).getName(a.constants, new ArrayList()); + if (tnames.equals(propertyName)) { + return abc.constants.getMultinameId(new Multiname(tname.kind, + abc.constants.getStringId(tnames, true), + abc.constants.getNamespaceId(new Namespace(tname.getNamespace(a.constants).kind, abc.constants.getStringId(tname.getNamespace(a.constants).getName(a.constants), true)), 0, true), 0, 0, new ArrayList()), true); + } + } + } + } + + return abc.constants.getMultinameId(new Multiname(Multiname.MULTINAME, + abc.constants.getStringId(propertyName, true), 0, + allNsSet(), 0, new ArrayList()), true); + }*/ + @Override + public GraphTargetItem returnType() { + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndex = new Reference<>(0); + Reference outPropValue = new Reference<>(null); + resolve(new SourceGeneratorLocalData(new HashMap(), 0, false, 0)/*???*/, objType, propType, propIndex, outPropValue); + + return propType.getVal(); + } + + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator, boolean needsReturn) throws CompilationException { + + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndex = new Reference<>(0); + Reference outPropValue = new Reference<>(null); + resolve(localData, objType, propType, propIndex, outPropValue); + + int propertyId = propIndex.getVal(); + Object obj = resolveObject(localData, generator); + Reference ret_temp = new Reference<>(-1); + if (assignedValue != null) { + GraphTargetItem targetType = propType.getVal(); + String srcType = assignedValue.returnType().toString(); + GraphTargetItem coerced = assignedValue; + if (!targetType.equals(srcType) && !propertyName.startsWith("@")) { + coerced = new CoerceAVM2Item(null, assignedValue, targetType); + } + return toSourceMerge(localData, generator, obj, coerced, + needsReturn ? dupSetTemp(localData, generator, ret_temp) : null, + ins(new SetPropertyIns(), propertyId), + needsReturn ? getTemp(localData, generator, ret_temp) : null, + killTemp(localData, generator, Arrays.asList(ret_temp))); + } else { + if (obj instanceof AVM2Instruction && (((AVM2Instruction) obj).definition instanceof FindPropertyStrictIns)) { + return toSourceMerge(localData, generator, ins(new GetLexIns(), propertyId), + needsReturn ? null : ins(new PopIns()) + ); + } + return toSourceMerge(localData, generator, obj, ins(new GetPropertyIns(), propertyId), + needsReturn ? null : ins(new PopIns()) + ); + } + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, true); + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSource(localData, generator, false); + } + + @Override + public String toString() { + return "" + object + "." + propertyName; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + public Object resolveObject(SourceGeneratorLocalData localData, SourceGenerator generator) { + Object obj = object; + + if (obj == null) { + String cname; + String pkgName = ""; + cname = localData.currentClass; + pkgName = localData.pkg; + Reference outName = new Reference<>(""); + Reference outNs = new Reference<>(""); + Reference outPropNs = new Reference<>(""); + Reference outPropNsKind = new Reference<>(1); + Reference outPropNsIndex = new Reference<>(0); + Reference outPropType = new Reference<>(null); + Reference outPropValue = new Reference<>(null); + List abcs = new ArrayList<>(); + abcs.add(abc); + abcs.addAll(otherABCs); + if (cname != null && AVM2SourceGenerator.searchPrototypeChain(true, abcs, pkgName, cname, propertyName, outName, outNs, outPropNs, outPropNsKind, outPropNsIndex, outPropType, outPropValue) && (localData.currentClass.equals("".equals(outNs.getVal()) ? outName.getVal() : outNs.getVal() + "." + outName.getVal()))) { + NameAVM2Item nobj = new NameAVM2Item(new TypeItem(localData.getFullClass()), 0, "this", null, false, openedNamespaces); + nobj.setRegNumber(0); + obj = nobj; + } else { + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndex = new Reference<>(0); + Reference propValue = new Reference<>(null); + resolve(localData, objType, propType, propIndex, outPropValue); + obj = ins(new FindPropertyStrictIns(), propIndex.getVal()); + } + } + return obj; + } + + @Override + public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { + + Reference objType = new Reference<>(null); + Reference propType = new Reference<>(null); + Reference propIndex = new Reference<>(0); + Reference outPropValue = new Reference<>(null); + resolve(localData, objType, propType, propIndex, outPropValue); + + int propertyId = propIndex.getVal(); + Object obj = resolveObject(localData, generator); + + Reference ret_temp = new Reference<>(-1); + Reference obj_temp = new Reference<>(-1); + + boolean isInteger = propType.getVal().equals("int"); + + List ret = toSourceMerge(localData, generator, obj, dupSetTemp(localData, generator, obj_temp), + //Start get original + //getTemp(localData, generator, obj_temp), + //index!=null?getTemp(localData, generator, index_temp):null, + ins(new GetPropertyIns(), propertyId), + (!isInteger && post) ? ins(new ConvertDIns()) : null, + //End get original + (!post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + needsReturn ? ins(new DupIns()) : null, + (post) ? (decrement ? ins(isInteger ? new DecrementIIns() : new DecrementIns()) : ins(isInteger ? new IncrementIIns() : new IncrementIns())) : null, + setTemp(localData, generator, ret_temp), + getTemp(localData, generator, obj_temp), + getTemp(localData, generator, ret_temp), + ins(new SetPropertyIns(), propertyId), + //needsReturn?getTemp(localData, generator, ret_temp):null, + killTemp(localData, generator, Arrays.asList(ret_temp, obj_temp))); + return ret; + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SetterAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SetterAVM2Item.java index bcb2ad8a3..519d54d7a 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SetterAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SetterAVM2Item.java @@ -1,32 +1,32 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class SetterAVM2Item extends MethodAVM2Item { - - public SetterAVM2Item(String pkg,boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { - super(pkg,isInterface, customNamespace, needsActivation, hasRest, line, override, isFinal, isStatic, namespace, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class SetterAVM2Item extends MethodAVM2Item { + + public SetterAVM2Item(String pkg, boolean isInterface, String customNamespace, boolean needsActivation, boolean hasRest, int line, boolean override, boolean isFinal, boolean isStatic, int namespace, String methodName, List paramTypes, List paramNames, List paramValues, List body, List subvariables, GraphTargetItem retType) { + super(pkg, isInterface, customNamespace, needsActivation, hasRest, line, override, isFinal, isStatic, namespace, methodName, paramTypes, paramNames, paramValues, body, subvariables, retType); + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java index 3a009721d..73a8c2b69 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/SlotAVM2Item.java @@ -1,73 +1,73 @@ -/* - * Copyright (C) 2010-2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; - -/** - * - * @author JPEXS - */ -public class SlotAVM2Item extends AVM2Item { - - private final int namespace; - private boolean isStatic; - public String var; - public GraphTargetItem type; - public String customNamespace; - public int line; - public String pkg; - - public int getNamespace() { - return namespace; - } - - public boolean isStatic() { - return isStatic; - } - - public SlotAVM2Item(String pkg,String customNamespace, boolean isStatic, int namespace, String var, GraphTargetItem type, GraphTargetItem value, int line) { - super(null, NOPRECEDENCE); - this.pkg = pkg; - this.line = line; - this.namespace = namespace; - this.value = value; - this.isStatic = isStatic; - this.var = var; - this.type = type; - this.customNamespace = customNamespace; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; //TODO - } - - @Override - public GraphTargetItem returnType() { - return type; - } - - @Override - public boolean hasReturnValue() { - return true; - } - -} +/* + * Copyright (C) 2010-2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; + +/** + * + * @author JPEXS + */ +public class SlotAVM2Item extends AVM2Item { + + private final int namespace; + private boolean isStatic; + public String var; + public GraphTargetItem type; + public String customNamespace; + public int line; + public String pkg; + + public int getNamespace() { + return namespace; + } + + public boolean isStatic() { + return isStatic; + } + + public SlotAVM2Item(String pkg, String customNamespace, boolean isStatic, int namespace, String var, GraphTargetItem type, GraphTargetItem value, int line) { + super(null, NOPRECEDENCE); + this.pkg = pkg; + this.line = line; + this.namespace = namespace; + this.value = value; + this.isStatic = isStatic; + this.var = var; + this.type = type; + this.customNamespace = customNamespace; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; //TODO + } + + @Override + public GraphTargetItem returnType() { + return type; + } + + @Override + public boolean hasReturnValue() { + return true; + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java index 38bf3d99f..1c5aba921 100644 --- a/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java +++ b/src/com/jpexs/decompiler/flash/abc/avm2/parser/script/UnresolvedAVM2Item.java @@ -1,520 +1,518 @@ -/* - * Copyright (C) 2014 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.abc.avm2.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; -import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; -import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class UnresolvedAVM2Item extends AssignableAVM2Item { - - private String name; - - private int nsKind = -1; - public List openedNamespaces; - public int line; - public GraphTargetItem type; - //private GraphTargetItem ns = null; - public GraphTargetItem resolved; - public GraphTargetItem resolvedRoot; - private boolean mustBeType; - public List importedClasses; - public List scopeStack = new ArrayList(); - public List subtypes; - - @Override - public AssignableAVM2Item copy() { - UnresolvedAVM2Item c = new UnresolvedAVM2Item(subtypes, importedClasses, mustBeType, type, line, name, assignedValue, openedNamespaces); - //c.setNs(ns); - c.nsKind = nsKind; - c.resolved = resolved; - return c; - } - - public void setSlotScope(int slotScope) { - if (resolved instanceof NameAVM2Item) { - ((NameAVM2Item) resolved).setSlotScope(slotScope); - } - } - - public int getSlotScope() { - if (resolved instanceof NameAVM2Item) { - return ((NameAVM2Item) resolved).getSlotScope(); - } - return -1; - } - - /*public void setNs(GraphTargetItem ns) { - this.ns = ns; - }*/ - public void setRegNumber(int regNumber) { - if (resolved instanceof NameAVM2Item) { - ((NameAVM2Item) resolved).setRegNumber(regNumber); - } - } - - public int getSlotNumber() { - if (resolved instanceof NameAVM2Item) { - return ((NameAVM2Item) resolved).getSlotNumber(); - } - return -1; - } - - public void setSlotNumber(int slotNumber) { - if (resolved instanceof NameAVM2Item) { - ((NameAVM2Item) resolved).setSlotNumber(slotNumber); - } - } - - public int getRegNumber() { - if (resolved instanceof NameAVM2Item) { - return ((NameAVM2Item) resolved).getRegNumber(); - } - return -1; - } - /* - public GraphTargetItem getNs() { - return ns; - } - */ - - public void appendName(String name) { - this.name += "." + name; - } - - public void setDefinition(boolean definition) { - if (resolved instanceof NameAVM2Item) { - ((NameAVM2Item) resolved).setDefinition(definition); - } - } - - public void setNsKind(int nsKind) { - this.nsKind = nsKind; - } - - public int getNsKind() { - return nsKind; - } - - @Override - public void setAssignedValue(GraphTargetItem storeValue) { - this.assignedValue = storeValue; - } - - public String getVariableName() { - return name; - } - - public void setVariableName(String name) { - this.name = name; - } - - public UnresolvedAVM2Item(List subtypes, List importedClasses, boolean mustBeType, GraphTargetItem type, int line, String name, GraphTargetItem storeValue, List openedNamespaces) { - super(storeValue); - this.name = name; - this.assignedValue = storeValue; - this.line = line; - this.type = type; - this.openedNamespaces = openedNamespaces; - this.mustBeType = mustBeType; - this.importedClasses = importedClasses; - this.subtypes = subtypes; - } - - public boolean isDefinition() { - if (resolved instanceof NameAVM2Item) { - return ((NameAVM2Item) resolved).isDefinition(); - } - return false; - } - - public GraphTargetItem getStoreValue() { - return assignedValue; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - return writer; - } - - private int allNsSet(ABC abc) { - int nssa[] = new int[openedNamespaces.size()]; - for (int i = 0; i < openedNamespaces.size(); i++) { - nssa[i] = openedNamespaces.get(i); - } - return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); - } - - public static GraphTargetItem getDefaultValue(String type) { - switch (type) { - case "*": - return new UndefinedAVM2Item(null); - case "int": - return new IntegerValueAVM2Item(null, 0L); - case "Number": - return new NanAVM2Item(null); - default: - return new NullAVM2Item(null); - } - } - - public static AVM2Instruction generateCoerce(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem type) throws CompilationException { - AVM2Instruction ins; - switch (type.toString()) { - case "int": - ins = ins(new ConvertIIns()); - break; - case "*": - ins = ins(new CoerceAIns()); - break; - case "String": - ins = ins(new CoerceSIns()); - break; - default: - int type_index = AVM2SourceGenerator.resolveType(localData, type, ((AVM2SourceGenerator) generator).abc,((AVM2SourceGenerator) generator).allABCs); - ins = ins(new CoerceIns(), type_index); - break; - } - return ins; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if (resolved == null) { - throw new RuntimeException("Unresolved"); - } - return resolved.toSource(localData, generator); - } - - @Override - public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if (resolved == null) { - throw new RuntimeException("Unresolved"); - } - return resolved.toSourceIgnoreReturnValue(localData, generator); - } - - @Override - public boolean hasReturnValue() { - if (resolved != null) { - return resolved.hasReturnValue(); - } - return true; - } - - @Override - public boolean needsSemicolon() { - if (resolved != null) { - return resolved.needsSemicolon(); - } - return false; - } - - @Override - public String toString() { - if (resolved != null) { - return resolved.toString(); - } - return name; - } - - @Override - public GraphTargetItem returnType() { - if (resolved != null) { - return resolved.returnType(); - } - if (type == null) { - return TypeItem.UNBOUNDED; - } - return type; - } - - @Override - public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { - if (resolved == null) { - throw new RuntimeException("Unresolved"); - } - if (resolved instanceof AssignableAVM2Item) { - return ((AssignableAVM2Item) resolved).toSourceChange(localData, generator, post, decrement, needsReturn); - } - throw new RuntimeException("Cannot assign"); - } - - public GraphTargetItem resolve(GraphTargetItem thisType, List paramTypes, List paramNames, ABC abc, List otherAbcs, List callStack, List variables) throws CompilationException { - List parts = new ArrayList<>(); - if (name.contains(".")) { - String partsArr[] = name.split("\\."); - parts.addAll(Arrays.asList(partsArr)); - } else { - parts.add(name); - } - - if (scopeStack.isEmpty()) { //Everything is multiname property in with command - - //search for variable - for (AssignableAVM2Item a : variables) { - if (a instanceof NameAVM2Item) { - NameAVM2Item n = (NameAVM2Item) a; - if (n.isDefinition() && parts.get(0).equals(n.getVariableName())) { - NameAVM2Item ret = new NameAVM2Item(n.type, n.line, parts.get(0), null, false, openedNamespaces); - ret.setSlotScope(n.getSlotScope()); - ret.setSlotNumber(n.getSlotNumber()); - ret.setRegNumber(n.getRegNumber()); - resolved = ret; - for (int i = 1; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == 1) { - ret.setAssignedValue(assignedValue); - } - ret.setNs(n.getNs()); - return resolvedRoot=ret; - } - } - } - } - //Search for types in imported classes - for (String imp : importedClasses) { - String impName = imp; - String impPkg = ""; - if (impName.contains(".")) { - impPkg = impName.substring(0, impName.lastIndexOf('.')); - impName = impName.substring(impName.lastIndexOf('.') + 1); - } - if (impName.equals(parts.get(0))) { - TypeItem ret = new TypeItem(imp); - /*for (GraphTargetItem s : subtypes) { - if (!subtypes.isEmpty() && parts.size() > 1) { - continue; - } - UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); - su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); - if (!(su.resolved instanceof TypeItem)) { - throw new CompilationException("Not a type", line); - } - TypeItem st = (TypeItem) su.resolved; - ret.subtypes.add(st.fullTypeName); - }*/ - resolved = ret; - for (int i = 1; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == 1 && assignedValue != null) { - throw new CompilationException("Cannot assign type", line); - } - return resolvedRoot=ret; - } - } - - //Search all fully qualitfied types - List allAbcs = new ArrayList<>(); - allAbcs.add(abc); - allAbcs.addAll(otherAbcs); - for (int i = 0; i < parts.size(); i++) { - String fname = Helper.joinStrings(parts.subList(0, i + 1), "."); - for (ABC a : allAbcs) { - for (int c = 0; c < a.instance_info.size(); c++) { - if(a.instance_info.get(c).deleted){ - continue; - } - if (a.instance_info.get(c).name_index>0 && fname.equals(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants))) { - if (!subtypes.isEmpty() && parts.size() > i + 1) { - continue; - } - TypeItem ret = new TypeItem(fname); - /*for (String s : subtypes) { - UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); - su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); - if (!(su.resolved instanceof TypeItem)) { - throw new CompilationException("Not a type", line); - } - TypeItem st = (TypeItem) su.resolved; - ret.subtypes.add(st.fullTypeName); - }*/ - resolved = ret; - for (int j = i + 1; j < parts.size(); j++) { - resolved = new PropertyAVM2Item(resolved, parts.get(j), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (j == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == i + 1 && assignedValue != null) { - throw new CompilationException("Cannot assign type", line); - } - - return resolvedRoot=ret; - } - } - } - } - - //Search for types in opened namespaces - for (int ni : openedNamespaces) { - Namespace ons = abc.constants.getNamespace(ni); - if(ons == null){ - continue; - } - for (ABC a : allAbcs) { - for (int c = 0; c < a.instance_info.size(); c++) { - if(a.instance_info.get(c).deleted){ - continue; - } - if ((a.instance_info.get(c).getName(a.constants)!=null && a == abc && a.instance_info.get(c).getName(a.constants).namespace_index == ni) - || - (ons.kind != Namespace.KIND_PRIVATE && a.instance_info.get(c).getName(a.constants)!=null &&a.instance_info.get(c).getName(a.constants).getNamespace(a.constants)!=null && a.instance_info.get(c).getName(a.constants).getNamespace(a.constants).hasName(ons.getName(abc.constants), a.constants))) { - String cname = a.instance_info.get(c).getName(a.constants).getName(a.constants, new ArrayList()); - if (parts.get(0).equals(cname)) { - if (!subtypes.isEmpty() && parts.size() > 1) { - continue; - } - TypeItem ret = new TypeItem(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants)); - /*for (String s : subtypes) { - UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); - su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); - if (!(su.resolved instanceof TypeItem)) { - throw new CompilationException("Not a type", line); - } - TypeItem st = (TypeItem) su.resolved; - ret.subtypes.add(st.fullTypeName); - }*/ - resolved = ret; - for (int i = 1; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == 1 && assignedValue != null) { - throw new CompilationException("Cannot assign type", line); - } - - return resolvedRoot=ret; - } - } - } - } - } - - if (parts.get(0).equals("this")) { - if (thisType == null) { - throw new CompilationException("Cannot use this in that context", line); - } - NameAVM2Item ret = new NameAVM2Item(thisType, line, parts.get(0), null, false, openedNamespaces); - resolved = ret; - for (int i = 1; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == 1) { - ret.setAssignedValue(assignedValue); - } - return resolvedRoot=ret; - } - - if (paramNames.contains(parts.get(0)) || parts.get(0).equals("arguments")) { - int ind = paramNames.indexOf(parts.get(0)); - GraphTargetItem t = TypeItem.UNBOUNDED; - if(ind == -1){ - - }else if(ind < paramTypes.size()){ - t = paramTypes.get(ind); - } //else rest parameter - - GraphTargetItem ret = new NameAVM2Item(t, line, parts.get(0), null, false, openedNamespaces); - resolved = ret; - for (int i = 1; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).assignedValue = assignedValue; - } - } - if (parts.size() == 1) { - ((NameAVM2Item) ret).setAssignedValue(assignedValue); - } - return resolvedRoot=ret; - } - - - if (/*!subtypes.isEmpty() && */parts.size() == 1 && parts.get(0).equals("Vector")) { - TypeItem ret = new TypeItem("__AS3__.vec.Vector"); - /*for (String s : subtypes) { - UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); - su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); - if (!(su.resolved instanceof TypeItem)) { - throw new CompilationException("Not a type", line); - } - TypeItem st = (TypeItem) su.resolved; - ret.subtypes.add(st.fullTypeName); - }*/ - resolved = ret; - return resolvedRoot=ret; - } - - if (mustBeType) { - throw new CompilationException("Not a type", line); - } - resolved = null; - GraphTargetItem ret = null; - for (int i = 0; i < parts.size(); i++) { - resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, callStack); - if (ret == null) { - ((PropertyAVM2Item) resolved).scopeStack = scopeStack; - ret = resolved; - } - if (i == parts.size() - 1) { - ((PropertyAVM2Item) resolved).setAssignedValue(assignedValue); - } - } - return resolvedRoot=ret; - } - -} +/* + * Copyright (C) 2014 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.abc.avm2.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceSIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns; +import static com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item.ins; +import com.jpexs.decompiler.flash.abc.avm2.model.IntegerValueAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NanAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NullAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.UndefinedAVM2Item; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class UnresolvedAVM2Item extends AssignableAVM2Item { + + private String name; + + private int nsKind = -1; + public List openedNamespaces; + public int line; + public GraphTargetItem type; + //private GraphTargetItem ns = null; + public GraphTargetItem resolved; + public GraphTargetItem resolvedRoot; + private boolean mustBeType; + public List importedClasses; + public List scopeStack = new ArrayList(); + public List subtypes; + + @Override + public AssignableAVM2Item copy() { + UnresolvedAVM2Item c = new UnresolvedAVM2Item(subtypes, importedClasses, mustBeType, type, line, name, assignedValue, openedNamespaces); + //c.setNs(ns); + c.nsKind = nsKind; + c.resolved = resolved; + return c; + } + + public void setSlotScope(int slotScope) { + if (resolved instanceof NameAVM2Item) { + ((NameAVM2Item) resolved).setSlotScope(slotScope); + } + } + + public int getSlotScope() { + if (resolved instanceof NameAVM2Item) { + return ((NameAVM2Item) resolved).getSlotScope(); + } + return -1; + } + + /*public void setNs(GraphTargetItem ns) { + this.ns = ns; + }*/ + public void setRegNumber(int regNumber) { + if (resolved instanceof NameAVM2Item) { + ((NameAVM2Item) resolved).setRegNumber(regNumber); + } + } + + public int getSlotNumber() { + if (resolved instanceof NameAVM2Item) { + return ((NameAVM2Item) resolved).getSlotNumber(); + } + return -1; + } + + public void setSlotNumber(int slotNumber) { + if (resolved instanceof NameAVM2Item) { + ((NameAVM2Item) resolved).setSlotNumber(slotNumber); + } + } + + public int getRegNumber() { + if (resolved instanceof NameAVM2Item) { + return ((NameAVM2Item) resolved).getRegNumber(); + } + return -1; + } + /* + public GraphTargetItem getNs() { + return ns; + } + */ + + public void appendName(String name) { + this.name += "." + name; + } + + public void setDefinition(boolean definition) { + if (resolved instanceof NameAVM2Item) { + ((NameAVM2Item) resolved).setDefinition(definition); + } + } + + public void setNsKind(int nsKind) { + this.nsKind = nsKind; + } + + public int getNsKind() { + return nsKind; + } + + @Override + public void setAssignedValue(GraphTargetItem storeValue) { + this.assignedValue = storeValue; + } + + public String getVariableName() { + return name; + } + + public void setVariableName(String name) { + this.name = name; + } + + public UnresolvedAVM2Item(List subtypes, List importedClasses, boolean mustBeType, GraphTargetItem type, int line, String name, GraphTargetItem storeValue, List openedNamespaces) { + super(storeValue); + this.name = name; + this.assignedValue = storeValue; + this.line = line; + this.type = type; + this.openedNamespaces = openedNamespaces; + this.mustBeType = mustBeType; + this.importedClasses = importedClasses; + this.subtypes = subtypes; + } + + public boolean isDefinition() { + if (resolved instanceof NameAVM2Item) { + return ((NameAVM2Item) resolved).isDefinition(); + } + return false; + } + + public GraphTargetItem getStoreValue() { + return assignedValue; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + return writer; + } + + private int allNsSet(ABC abc) { + int nssa[] = new int[openedNamespaces.size()]; + for (int i = 0; i < openedNamespaces.size(); i++) { + nssa[i] = openedNamespaces.get(i); + } + return abc.constants.getNamespaceSetId(new NamespaceSet(nssa), true); + } + + public static GraphTargetItem getDefaultValue(String type) { + switch (type) { + case "*": + return new UndefinedAVM2Item(null); + case "int": + return new IntegerValueAVM2Item(null, 0L); + case "Number": + return new NanAVM2Item(null); + default: + return new NullAVM2Item(null); + } + } + + public static AVM2Instruction generateCoerce(SourceGeneratorLocalData localData, SourceGenerator generator, GraphTargetItem type) throws CompilationException { + AVM2Instruction ins; + switch (type.toString()) { + case "int": + ins = ins(new ConvertIIns()); + break; + case "*": + ins = ins(new CoerceAIns()); + break; + case "String": + ins = ins(new CoerceSIns()); + break; + default: + int type_index = AVM2SourceGenerator.resolveType(localData, type, ((AVM2SourceGenerator) generator).abc, ((AVM2SourceGenerator) generator).allABCs); + ins = ins(new CoerceIns(), type_index); + break; + } + return ins; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (resolved == null) { + throw new RuntimeException("Unresolved"); + } + return resolved.toSource(localData, generator); + } + + @Override + public List toSourceIgnoreReturnValue(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (resolved == null) { + throw new RuntimeException("Unresolved"); + } + return resolved.toSourceIgnoreReturnValue(localData, generator); + } + + @Override + public boolean hasReturnValue() { + if (resolved != null) { + return resolved.hasReturnValue(); + } + return true; + } + + @Override + public boolean needsSemicolon() { + if (resolved != null) { + return resolved.needsSemicolon(); + } + return false; + } + + @Override + public String toString() { + if (resolved != null) { + return resolved.toString(); + } + return name; + } + + @Override + public GraphTargetItem returnType() { + if (resolved != null) { + return resolved.returnType(); + } + if (type == null) { + return TypeItem.UNBOUNDED; + } + return type; + } + + @Override + public List toSourceChange(SourceGeneratorLocalData localData, SourceGenerator generator, boolean post, boolean decrement, boolean needsReturn) throws CompilationException { + if (resolved == null) { + throw new RuntimeException("Unresolved"); + } + if (resolved instanceof AssignableAVM2Item) { + return ((AssignableAVM2Item) resolved).toSourceChange(localData, generator, post, decrement, needsReturn); + } + throw new RuntimeException("Cannot assign"); + } + + public GraphTargetItem resolve(GraphTargetItem thisType, List paramTypes, List paramNames, ABC abc, List otherAbcs, List callStack, List variables) throws CompilationException { + List parts = new ArrayList<>(); + if (name.contains(".")) { + String partsArr[] = name.split("\\."); + parts.addAll(Arrays.asList(partsArr)); + } else { + parts.add(name); + } + + if (scopeStack.isEmpty()) { //Everything is multiname property in with command + + //search for variable + for (AssignableAVM2Item a : variables) { + if (a instanceof NameAVM2Item) { + NameAVM2Item n = (NameAVM2Item) a; + if (n.isDefinition() && parts.get(0).equals(n.getVariableName())) { + NameAVM2Item ret = new NameAVM2Item(n.type, n.line, parts.get(0), null, false, openedNamespaces); + ret.setSlotScope(n.getSlotScope()); + ret.setSlotNumber(n.getSlotNumber()); + ret.setRegNumber(n.getRegNumber()); + resolved = ret; + for (int i = 1; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == 1) { + ret.setAssignedValue(assignedValue); + } + ret.setNs(n.getNs()); + return resolvedRoot = ret; + } + } + } + } + //Search for types in imported classes + for (String imp : importedClasses) { + String impName = imp; + String impPkg = ""; + if (impName.contains(".")) { + impPkg = impName.substring(0, impName.lastIndexOf('.')); + impName = impName.substring(impName.lastIndexOf('.') + 1); + } + if (impName.equals(parts.get(0))) { + TypeItem ret = new TypeItem(imp); + /*for (GraphTargetItem s : subtypes) { + if (!subtypes.isEmpty() && parts.size() > 1) { + continue; + } + UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); + su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); + if (!(su.resolved instanceof TypeItem)) { + throw new CompilationException("Not a type", line); + } + TypeItem st = (TypeItem) su.resolved; + ret.subtypes.add(st.fullTypeName); + }*/ + resolved = ret; + for (int i = 1; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == 1 && assignedValue != null) { + throw new CompilationException("Cannot assign type", line); + } + return resolvedRoot = ret; + } + } + + //Search all fully qualitfied types + List allAbcs = new ArrayList<>(); + allAbcs.add(abc); + allAbcs.addAll(otherAbcs); + for (int i = 0; i < parts.size(); i++) { + String fname = Helper.joinStrings(parts.subList(0, i + 1), "."); + for (ABC a : allAbcs) { + for (int c = 0; c < a.instance_info.size(); c++) { + if (a.instance_info.get(c).deleted) { + continue; + } + if (a.instance_info.get(c).name_index > 0 && fname.equals(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants))) { + if (!subtypes.isEmpty() && parts.size() > i + 1) { + continue; + } + TypeItem ret = new TypeItem(fname); + /*for (String s : subtypes) { + UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); + su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); + if (!(su.resolved instanceof TypeItem)) { + throw new CompilationException("Not a type", line); + } + TypeItem st = (TypeItem) su.resolved; + ret.subtypes.add(st.fullTypeName); + }*/ + resolved = ret; + for (int j = i + 1; j < parts.size(); j++) { + resolved = new PropertyAVM2Item(resolved, parts.get(j), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (j == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == i + 1 && assignedValue != null) { + throw new CompilationException("Cannot assign type", line); + } + + return resolvedRoot = ret; + } + } + } + } + + //Search for types in opened namespaces + for (int ni : openedNamespaces) { + Namespace ons = abc.constants.getNamespace(ni); + if (ons == null) { + continue; + } + for (ABC a : allAbcs) { + for (int c = 0; c < a.instance_info.size(); c++) { + if (a.instance_info.get(c).deleted) { + continue; + } + if ((a.instance_info.get(c).getName(a.constants) != null && a == abc && a.instance_info.get(c).getName(a.constants).namespace_index == ni) + || (ons.kind != Namespace.KIND_PRIVATE && a.instance_info.get(c).getName(a.constants) != null && a.instance_info.get(c).getName(a.constants).getNamespace(a.constants) != null && a.instance_info.get(c).getName(a.constants).getNamespace(a.constants).hasName(ons.getName(abc.constants), a.constants))) { + String cname = a.instance_info.get(c).getName(a.constants).getName(a.constants, new ArrayList()); + if (parts.get(0).equals(cname)) { + if (!subtypes.isEmpty() && parts.size() > 1) { + continue; + } + TypeItem ret = new TypeItem(a.instance_info.get(c).getName(a.constants).getNameWithNamespace(a.constants)); + /*for (String s : subtypes) { + UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); + su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); + if (!(su.resolved instanceof TypeItem)) { + throw new CompilationException("Not a type", line); + } + TypeItem st = (TypeItem) su.resolved; + ret.subtypes.add(st.fullTypeName); + }*/ + resolved = ret; + for (int i = 1; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == 1 && assignedValue != null) { + throw new CompilationException("Cannot assign type", line); + } + + return resolvedRoot = ret; + } + } + } + } + } + + if (parts.get(0).equals("this")) { + if (thisType == null) { + throw new CompilationException("Cannot use this in that context", line); + } + NameAVM2Item ret = new NameAVM2Item(thisType, line, parts.get(0), null, false, openedNamespaces); + resolved = ret; + for (int i = 1; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == 1) { + ret.setAssignedValue(assignedValue); + } + return resolvedRoot = ret; + } + + if (paramNames.contains(parts.get(0)) || parts.get(0).equals("arguments")) { + int ind = paramNames.indexOf(parts.get(0)); + GraphTargetItem t = TypeItem.UNBOUNDED; + if (ind == -1) { + + } else if (ind < paramTypes.size()) { + t = paramTypes.get(ind); + } //else rest parameter + + GraphTargetItem ret = new NameAVM2Item(t, line, parts.get(0), null, false, openedNamespaces); + resolved = ret; + for (int i = 1; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, new ArrayList()); + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).assignedValue = assignedValue; + } + } + if (parts.size() == 1) { + ((NameAVM2Item) ret).setAssignedValue(assignedValue); + } + return resolvedRoot = ret; + } + + if (/*!subtypes.isEmpty() && */parts.size() == 1 && parts.get(0).equals("Vector")) { + TypeItem ret = new TypeItem("__AS3__.vec.Vector"); + /*for (String s : subtypes) { + UnresolvedAVM2Item su = new UnresolvedAVM2Item(new ArrayList(), importedClasses, true, null, line, s, null, openedNamespaces); + su.resolve(thisType, paramTypes, paramNames, abc, otherAbcs, callStack, variables); + if (!(su.resolved instanceof TypeItem)) { + throw new CompilationException("Not a type", line); + } + TypeItem st = (TypeItem) su.resolved; + ret.subtypes.add(st.fullTypeName); + }*/ + resolved = ret; + return resolvedRoot = ret; + } + + if (mustBeType) { + throw new CompilationException("Not a type", line); + } + resolved = null; + GraphTargetItem ret = null; + for (int i = 0; i < parts.size(); i++) { + resolved = new PropertyAVM2Item(resolved, parts.get(i), abc, otherAbcs, openedNamespaces, callStack); + if (ret == null) { + ((PropertyAVM2Item) resolved).scopeStack = scopeStack; + ret = resolved; + } + if (i == parts.size() - 1) { + ((PropertyAVM2Item) resolved).setAssignedValue(assignedValue); + } + } + return resolvedRoot = ret; + } + +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java b/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java index ab9aad7a3..e3e87e3b0 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java +++ b/src/com/jpexs/decompiler/flash/abc/types/InstanceInfo.java @@ -1,109 +1,109 @@ -/* - * Copyright (C) 2010-2014 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.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; - -public class InstanceInfo { - - public int name_index; - public int super_index; - public int flags; // 1 = sealed, 0 = dynamic, 2 = final, 4 = interface, 8 = ProtectedNs - public int protectedNS; //if flags & 8 - public int[] interfaces; - public int iinit_index; // MethodInfo - constructor - public Traits instance_traits = new Traits(); - public static final int CLASS_SEALED = 1; //not dynamic - public static final int CLASS_FINAL = 2; - public static final int CLASS_INTERFACE = 4; - public static final int CLASS_PROTECTEDNS = 8; - - public boolean deleted; - - @Override - public String toString() { - return "name_index=" + name_index + " super_index=" + super_index + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - String supIndexStr = "[nothing]"; - if (super_index > 0) { - supIndexStr = abc.constants.getMultiname(super_index).toString(abc.constants, fullyQualifiedNames); - } - return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames); - } - - public String getClassHeaderStr(ABC abc, List fullyQualifiedNames) { - String supIndexStr = ""; - if (super_index > 0) { - supIndexStr = " extends " + abc.constants.getMultiname(super_index).getName(abc.constants, fullyQualifiedNames);////+" flags="+flags+" protectedNS="+protectedNS+" interfaces="+Helper.intArrToString(interfaces)+" method_index="+iinit_index - } - String implStr = ""; - if (interfaces.length > 0) { - if (isInterface()) { - implStr = " extends "; - } else { - implStr = " implements "; - } - for (int i = 0; i < interfaces.length; i++) { - if (i > 0) { - implStr += ", "; - } - implStr += abc.constants.getMultiname(interfaces[i]).getName(abc.constants, fullyQualifiedNames); - } - } - String modifiers; - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - modifiers = ns.getPrefix(abc); - if (!modifiers.isEmpty()) { - modifiers += " "; - } - - if (isFinal()) { - modifiers += "final "; - } - if (!isInterface() && isDynamic()) { - modifiers += "dynamic "; - } - String objType = "class "; - if (isInterface()) { - objType = "interface "; - } - return modifiers + objType + abc.constants.getMultiname(name_index).getName(abc.constants, new ArrayList()/* No full names here*/) + supIndexStr + implStr; - } - - public Multiname getName(ConstantPool constants) { - return constants.getMultiname(name_index); - } - - public boolean isInterface() { - return ((flags & CLASS_INTERFACE) == CLASS_INTERFACE); - } - - public boolean isDynamic() { - return (flags & CLASS_SEALED) == 0; - } - - public boolean isFinal() { - return (flags & CLASS_FINAL) == CLASS_FINAL; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; + +public class InstanceInfo { + + public int name_index; + public int super_index; + public int flags; // 1 = sealed, 0 = dynamic, 2 = final, 4 = interface, 8 = ProtectedNs + public int protectedNS; //if flags & 8 + public int[] interfaces; + public int iinit_index; // MethodInfo - constructor + public Traits instance_traits = new Traits(); + public static final int CLASS_SEALED = 1; //not dynamic + public static final int CLASS_FINAL = 2; + public static final int CLASS_INTERFACE = 4; + public static final int CLASS_PROTECTEDNS = 8; + + public boolean deleted; + + @Override + public String toString() { + return "name_index=" + name_index + " super_index=" + super_index + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + String supIndexStr = "[nothing]"; + if (super_index > 0) { + supIndexStr = abc.constants.getMultiname(super_index).toString(abc.constants, fullyQualifiedNames); + } + return "name_index=" + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " super_index=" + supIndexStr + " flags=" + flags + " protectedNS=" + protectedNS + " interfaces=" + Helper.intArrToString(interfaces) + " method_index=" + iinit_index + "\r\n" + instance_traits.toString(abc, fullyQualifiedNames); + } + + public String getClassHeaderStr(ABC abc, List fullyQualifiedNames) { + String supIndexStr = ""; + if (super_index > 0) { + supIndexStr = " extends " + abc.constants.getMultiname(super_index).getName(abc.constants, fullyQualifiedNames);////+" flags="+flags+" protectedNS="+protectedNS+" interfaces="+Helper.intArrToString(interfaces)+" method_index="+iinit_index + } + String implStr = ""; + if (interfaces.length > 0) { + if (isInterface()) { + implStr = " extends "; + } else { + implStr = " implements "; + } + for (int i = 0; i < interfaces.length; i++) { + if (i > 0) { + implStr += ", "; + } + implStr += abc.constants.getMultiname(interfaces[i]).getName(abc.constants, fullyQualifiedNames); + } + } + String modifiers; + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + modifiers = ns.getPrefix(abc); + if (!modifiers.isEmpty()) { + modifiers += " "; + } + + if (isFinal()) { + modifiers += "final "; + } + if (!isInterface() && isDynamic()) { + modifiers += "dynamic "; + } + String objType = "class "; + if (isInterface()) { + objType = "interface "; + } + return modifiers + objType + abc.constants.getMultiname(name_index).getName(abc.constants, new ArrayList()/* No full names here*/) + supIndexStr + implStr; + } + + public Multiname getName(ConstantPool constants) { + return constants.getMultiname(name_index); + } + + public boolean isInterface() { + return ((flags & CLASS_INTERFACE) == CLASS_INTERFACE); + } + + public boolean isDynamic() { + return (flags & CLASS_SEALED) == 0; + } + + public boolean isFinal() { + return (flags & CLASS_FINAL) == CLASS_FINAL; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java b/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java index b3518dc2c..e93a260f2 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java +++ b/src/com/jpexs/decompiler/flash/abc/types/MethodInfo.java @@ -1,326 +1,325 @@ -/* - * Copyright (C) 2010-2014 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.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.helpers.Helper; -import java.util.HashMap; -import java.util.List; - -public class MethodInfo { - - - public boolean deleted; - - public void delete(ABC abc,boolean d){ - this.deleted = true; - if(body!=null){ - for(AVM2Instruction ins:body.code.code){ - if(ins.definition instanceof NewFunctionIns){ - abc.method_info.get(ins.operands[0]).delete(abc,d); - } - } - } - } - public int[] param_types; - public int ret_type; - public int name_index; //0=no name - // 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional 16=ignore_rest, 32=explicit, 64=setsdxns, 128=has_paramnames - public static int FLAG_NEED_ARGUMENTS = 1; - public static int FLAG_NEED_ACTIVATION = 2; - public static int FLAG_NEED_REST = 4; - public static int FLAG_HAS_OPTIONAL = 8; - public static int FLAG_IGNORE_REST = 16; - public static int FLAG_EXPLICIT = 32; - public static int FLAG_SETSDXNS = 64; - public static int FLAG_HAS_PARAMNAMES = 128; - - public int flags; - public ValueKind[] optional; - public int[] paramNames; - private MethodBody body; - - public void setFlagIgnore_Rest() { - flags |= FLAG_IGNORE_REST; - } - - public void setFlagExplicit() { - flags |= FLAG_EXPLICIT; - } - - public void setFlagNeed_Arguments() { - flags |= FLAG_NEED_ARGUMENTS; - } - - public void setFlagSetsdxns() { - flags |= FLAG_SETSDXNS; - } - - public void setFlagSetsdxns(boolean val) { - if (val) { - setFlagSetsdxns(); - } else { - unsetFlagSetsdxns(); - } - } - - public void unsetFlagSetsdxns() { - if (flagSetsdxns()) { - flags -= FLAG_SETSDXNS; - } - } - - public void setFlagNeed_activation() { - flags |= FLAG_NEED_ACTIVATION; - } - - public void setFlagNeed_activation(boolean val) { - if (val) { - setFlagNeed_activation(); - } else { - unsetFlagNeed_activation(); - } - } - - public void unsetFlagNeed_activation() { - if (flagNeed_activation()) { - flags -= FLAG_NEED_ACTIVATION; - } - } - - public void setFlagNeed_rest() { - flags |= FLAG_NEED_REST; - } - - public void unsetFlagNeed_rest() { - if (flagNeed_rest()) { - flags -= FLAG_NEED_REST; - } - } - - public void setFlagNeed_rest(boolean val) { - if (val) { - setFlagNeed_rest(); - } else { - unsetFlagNeed_rest(); - } - } - - public void setFlagHas_optional() { - flags |= FLAG_HAS_OPTIONAL; - } - - public void unsetFlagHas_optional() { - if (flagHas_optional()) { - flags -= FLAG_HAS_OPTIONAL; - } - } - - public void setFlagHas_optional(boolean val) { - if (val) { - setFlagHas_optional(); - } else { - unsetFlagHas_optional(); - } - } - - public void setFlagHas_paramnames() { - flags |= FLAG_HAS_PARAMNAMES; - } - - public void unsetFlagHas_paramnames() { - if (flagHas_paramnames()) { - flags -= FLAG_HAS_PARAMNAMES; - } - } - - public void setFlagHas_paramnames(boolean val) { - if (val) { - setFlagHas_paramnames(); - } else { - unsetFlagHas_paramnames(); - } - } - - public boolean flagNeed_arguments() { - return (flags & FLAG_NEED_ARGUMENTS) == FLAG_NEED_ARGUMENTS; - } - - public boolean flagNeed_activation() { - return (flags & FLAG_NEED_ACTIVATION) == FLAG_NEED_ACTIVATION; - } - - public boolean flagNeed_rest() { - return (flags & FLAG_NEED_REST) == FLAG_NEED_REST; - } - - public boolean flagHas_optional() { - return (flags & FLAG_HAS_OPTIONAL) == FLAG_HAS_OPTIONAL; - } - - public boolean flagIgnore_rest() { - return (flags & FLAG_IGNORE_REST) == FLAG_IGNORE_REST; - } - - public boolean flagExplicit() { - return (flags & FLAG_EXPLICIT) == FLAG_EXPLICIT; - } - - public boolean flagSetsdxns() { - return (flags & FLAG_SETSDXNS) == FLAG_SETSDXNS; - } - - public boolean flagHas_paramnames() { - return (flags & FLAG_HAS_PARAMNAMES) == FLAG_HAS_PARAMNAMES; - } - - public MethodInfo(int[] param_types, int ret_type, int name_index, int flags, ValueKind[] optional, int[] paramNames) { - this.param_types = param_types; - this.ret_type = ret_type; - this.name_index = name_index; - this.flags = flags; - this.optional = optional; - this.paramNames = paramNames; - } - - @Override - public String toString() { - String optionalStr = "["; - if (optional != null) { - for (int i = 0; i < optional.length; i++) { - if (i > 0) { - optionalStr += ","; - } - optionalStr += optional[i].toString(); - } - } - optionalStr += "]"; - return "MethodInfo: param_types=" + Helper.intArrToString(param_types) + " ret_type=" + ret_type + " name_index=" + name_index + " flags=" + flags + " optional=" + optionalStr + " paramNames=" + Helper.intArrToString(paramNames); - } - - public String toString(ConstantPool constants, List fullyQualifiedNames) { - String optionalStr = "["; - if (optional != null) { - for (int i = 0; i < optional.length; i++) { - if (i > 0) { - optionalStr += ","; - } - optionalStr += optional[i].toString(constants); - } - } - optionalStr += "]"; - - String param_typesStr = ""; - for (int i = 0; i < param_types.length; i++) { - if (i > 0) { - param_typesStr += ","; - } - if (param_types[i] == 0) { - param_typesStr += "*"; - } else { - param_typesStr += constants.getMultiname(param_types[i]).toString(constants, fullyQualifiedNames); - } - } - - String paramNamesStr = ""; - for (int i = 0; i < paramNames.length; i++) { - if (i > 0) { - paramNamesStr += ","; - } - paramNamesStr += constants.getString(paramNames[i]); - } - - String ret_typeStr = ""; - if (ret_type == 0) { - ret_typeStr += "*"; - } else { - ret_typeStr += constants.getMultiname(ret_type).toString(constants, fullyQualifiedNames); - } - - return "param_types=" + param_typesStr + " ret_type=" + ret_typeStr + " name=\"" + constants.getString(name_index) + "\" flags=" + flags + " optional=" + optionalStr + " paramNames=" + paramNamesStr; - } - - public String getName(ConstantPool constants) { - if (name_index == 0) { - return "UNKNOWN"; - } - return constants.getString(name_index); - } - - public GraphTextWriter getParamStr(GraphTextWriter writer, ConstantPool constants, MethodBody body, ABC abc, List fullyQualifiedNames) { - HashMap localRegNames = new HashMap<>(); - if (body != null) { - localRegNames = body.code.getLocalRegNamesFromDebug(abc); - } - for (int i = 0; i < param_types.length; i++) { - if (i > 0) { - writer.appendNoHilight(", "); - } - if (!localRegNames.isEmpty()) { - writer.appendNoHilight(localRegNames.get(i + 1)); - } else if ((paramNames.length > i) && (paramNames[i] != 0) && Configuration.paramNamesEnable.get()) { - writer.appendNoHilight(constants.getString(paramNames[i])); - } else { - writer.appendNoHilight("param" + (i + 1)); - } - writer.appendNoHilight(":"); - if (param_types[i] == 0) { - writer.hilightSpecial("*", "param", i); - } else { - writer.hilightSpecial(constants.getMultiname(param_types[i]).getName(constants, fullyQualifiedNames), "param", i); - } - if (optional != null) { - if (i >= param_types.length - optional.length) { - int optionalIndex = i - (param_types.length - optional.length); - writer.appendNoHilight(" = "); - writer.hilightSpecial(optional[optionalIndex].toString(constants), "optional", optionalIndex); - } - } - } - if (flagNeed_rest()) { - String restAdd = ""; - if ((param_types != null) && (param_types.length > 0)) { - restAdd += ", "; - } - restAdd += "... "; - if (!localRegNames.isEmpty()) { - restAdd += localRegNames.get(param_types.length + 1); - } else { - restAdd += "rest"; - } - writer.hilightSpecial(restAdd, "flag.NEED_REST"); - } - return writer; - } - - public GraphTextWriter getReturnTypeStr(GraphTextWriter writer, ConstantPool constants, List fullyQualifiedNames) { - return writer.hilightSpecial(ret_type == 0 ? "*" : constants.getMultiname(ret_type).getName(constants, fullyQualifiedNames), "returns"); - } - - public void setBody(MethodBody body) { - this.body = body; - } - - public MethodBody getBody() { - return body; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.helpers.Helper; +import java.util.HashMap; +import java.util.List; + +public class MethodInfo { + + public boolean deleted; + + public void delete(ABC abc, boolean d) { + this.deleted = true; + if (body != null) { + for (AVM2Instruction ins : body.code.code) { + if (ins.definition instanceof NewFunctionIns) { + abc.method_info.get(ins.operands[0]).delete(abc, d); + } + } + } + } + public int[] param_types; + public int ret_type; + public int name_index; //0=no name + // 1=need_arguments, 2=need_activation, 4=need_rest 8=has_optional 16=ignore_rest, 32=explicit, 64=setsdxns, 128=has_paramnames + public static int FLAG_NEED_ARGUMENTS = 1; + public static int FLAG_NEED_ACTIVATION = 2; + public static int FLAG_NEED_REST = 4; + public static int FLAG_HAS_OPTIONAL = 8; + public static int FLAG_IGNORE_REST = 16; + public static int FLAG_EXPLICIT = 32; + public static int FLAG_SETSDXNS = 64; + public static int FLAG_HAS_PARAMNAMES = 128; + + public int flags; + public ValueKind[] optional; + public int[] paramNames; + private MethodBody body; + + public void setFlagIgnore_Rest() { + flags |= FLAG_IGNORE_REST; + } + + public void setFlagExplicit() { + flags |= FLAG_EXPLICIT; + } + + public void setFlagNeed_Arguments() { + flags |= FLAG_NEED_ARGUMENTS; + } + + public void setFlagSetsdxns() { + flags |= FLAG_SETSDXNS; + } + + public void setFlagSetsdxns(boolean val) { + if (val) { + setFlagSetsdxns(); + } else { + unsetFlagSetsdxns(); + } + } + + public void unsetFlagSetsdxns() { + if (flagSetsdxns()) { + flags -= FLAG_SETSDXNS; + } + } + + public void setFlagNeed_activation() { + flags |= FLAG_NEED_ACTIVATION; + } + + public void setFlagNeed_activation(boolean val) { + if (val) { + setFlagNeed_activation(); + } else { + unsetFlagNeed_activation(); + } + } + + public void unsetFlagNeed_activation() { + if (flagNeed_activation()) { + flags -= FLAG_NEED_ACTIVATION; + } + } + + public void setFlagNeed_rest() { + flags |= FLAG_NEED_REST; + } + + public void unsetFlagNeed_rest() { + if (flagNeed_rest()) { + flags -= FLAG_NEED_REST; + } + } + + public void setFlagNeed_rest(boolean val) { + if (val) { + setFlagNeed_rest(); + } else { + unsetFlagNeed_rest(); + } + } + + public void setFlagHas_optional() { + flags |= FLAG_HAS_OPTIONAL; + } + + public void unsetFlagHas_optional() { + if (flagHas_optional()) { + flags -= FLAG_HAS_OPTIONAL; + } + } + + public void setFlagHas_optional(boolean val) { + if (val) { + setFlagHas_optional(); + } else { + unsetFlagHas_optional(); + } + } + + public void setFlagHas_paramnames() { + flags |= FLAG_HAS_PARAMNAMES; + } + + public void unsetFlagHas_paramnames() { + if (flagHas_paramnames()) { + flags -= FLAG_HAS_PARAMNAMES; + } + } + + public void setFlagHas_paramnames(boolean val) { + if (val) { + setFlagHas_paramnames(); + } else { + unsetFlagHas_paramnames(); + } + } + + public boolean flagNeed_arguments() { + return (flags & FLAG_NEED_ARGUMENTS) == FLAG_NEED_ARGUMENTS; + } + + public boolean flagNeed_activation() { + return (flags & FLAG_NEED_ACTIVATION) == FLAG_NEED_ACTIVATION; + } + + public boolean flagNeed_rest() { + return (flags & FLAG_NEED_REST) == FLAG_NEED_REST; + } + + public boolean flagHas_optional() { + return (flags & FLAG_HAS_OPTIONAL) == FLAG_HAS_OPTIONAL; + } + + public boolean flagIgnore_rest() { + return (flags & FLAG_IGNORE_REST) == FLAG_IGNORE_REST; + } + + public boolean flagExplicit() { + return (flags & FLAG_EXPLICIT) == FLAG_EXPLICIT; + } + + public boolean flagSetsdxns() { + return (flags & FLAG_SETSDXNS) == FLAG_SETSDXNS; + } + + public boolean flagHas_paramnames() { + return (flags & FLAG_HAS_PARAMNAMES) == FLAG_HAS_PARAMNAMES; + } + + public MethodInfo(int[] param_types, int ret_type, int name_index, int flags, ValueKind[] optional, int[] paramNames) { + this.param_types = param_types; + this.ret_type = ret_type; + this.name_index = name_index; + this.flags = flags; + this.optional = optional; + this.paramNames = paramNames; + } + + @Override + public String toString() { + String optionalStr = "["; + if (optional != null) { + for (int i = 0; i < optional.length; i++) { + if (i > 0) { + optionalStr += ","; + } + optionalStr += optional[i].toString(); + } + } + optionalStr += "]"; + return "MethodInfo: param_types=" + Helper.intArrToString(param_types) + " ret_type=" + ret_type + " name_index=" + name_index + " flags=" + flags + " optional=" + optionalStr + " paramNames=" + Helper.intArrToString(paramNames); + } + + public String toString(ConstantPool constants, List fullyQualifiedNames) { + String optionalStr = "["; + if (optional != null) { + for (int i = 0; i < optional.length; i++) { + if (i > 0) { + optionalStr += ","; + } + optionalStr += optional[i].toString(constants); + } + } + optionalStr += "]"; + + String param_typesStr = ""; + for (int i = 0; i < param_types.length; i++) { + if (i > 0) { + param_typesStr += ","; + } + if (param_types[i] == 0) { + param_typesStr += "*"; + } else { + param_typesStr += constants.getMultiname(param_types[i]).toString(constants, fullyQualifiedNames); + } + } + + String paramNamesStr = ""; + for (int i = 0; i < paramNames.length; i++) { + if (i > 0) { + paramNamesStr += ","; + } + paramNamesStr += constants.getString(paramNames[i]); + } + + String ret_typeStr = ""; + if (ret_type == 0) { + ret_typeStr += "*"; + } else { + ret_typeStr += constants.getMultiname(ret_type).toString(constants, fullyQualifiedNames); + } + + return "param_types=" + param_typesStr + " ret_type=" + ret_typeStr + " name=\"" + constants.getString(name_index) + "\" flags=" + flags + " optional=" + optionalStr + " paramNames=" + paramNamesStr; + } + + public String getName(ConstantPool constants) { + if (name_index == 0) { + return "UNKNOWN"; + } + return constants.getString(name_index); + } + + public GraphTextWriter getParamStr(GraphTextWriter writer, ConstantPool constants, MethodBody body, ABC abc, List fullyQualifiedNames) { + HashMap localRegNames = new HashMap<>(); + if (body != null) { + localRegNames = body.code.getLocalRegNamesFromDebug(abc); + } + for (int i = 0; i < param_types.length; i++) { + if (i > 0) { + writer.appendNoHilight(", "); + } + if (!localRegNames.isEmpty()) { + writer.appendNoHilight(localRegNames.get(i + 1)); + } else if ((paramNames.length > i) && (paramNames[i] != 0) && Configuration.paramNamesEnable.get()) { + writer.appendNoHilight(constants.getString(paramNames[i])); + } else { + writer.appendNoHilight("param" + (i + 1)); + } + writer.appendNoHilight(":"); + if (param_types[i] == 0) { + writer.hilightSpecial("*", "param", i); + } else { + writer.hilightSpecial(constants.getMultiname(param_types[i]).getName(constants, fullyQualifiedNames), "param", i); + } + if (optional != null) { + if (i >= param_types.length - optional.length) { + int optionalIndex = i - (param_types.length - optional.length); + writer.appendNoHilight(" = "); + writer.hilightSpecial(optional[optionalIndex].toString(constants), "optional", optionalIndex); + } + } + } + if (flagNeed_rest()) { + String restAdd = ""; + if ((param_types != null) && (param_types.length > 0)) { + restAdd += ", "; + } + restAdd += "... "; + if (!localRegNames.isEmpty()) { + restAdd += localRegNames.get(param_types.length + 1); + } else { + restAdd += "rest"; + } + writer.hilightSpecial(restAdd, "flag.NEED_REST"); + } + return writer; + } + + public GraphTextWriter getReturnTypeStr(GraphTextWriter writer, ConstantPool constants, List fullyQualifiedNames) { + return writer.hilightSpecial(ret_type == 0 ? "*" : constants.getMultiname(ret_type).getName(constants, fullyQualifiedNames), "returns"); + } + + public void setBody(MethodBody body) { + this.body = body; + } + + public MethodBody getBody() { + return body; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/Multiname.java b/src/com/jpexs/decompiler/flash/abc/types/Multiname.java index d7a21e589..4e92cdf17 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/Multiname.java +++ b/src/com/jpexs/decompiler/flash/abc/types/Multiname.java @@ -1,300 +1,300 @@ -/* - * Copyright (C) 2010-2014 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.abc.types; - -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.helpers.Helper; -import java.util.List; - -public class Multiname { - - public static final int QNAME = 7; - public static final int QNAMEA = 0x0d; - public static final int RTQNAME = 0x0f; - public static final int RTQNAMEA = 0x10; - public static final int RTQNAMEL = 0x11; - public static final int RTQNAMELA = 0x12; - public static final int MULTINAME = 0x09; - public static final int MULTINAMEA = 0x0e; - public static final int MULTINAMEL = 0x1b; - public static final int MULTINAMELA = 0x1c; - public static final int TYPENAME = 0x1d; - private static final int[] multinameKinds = new int[]{QNAME, QNAMEA, MULTINAME, MULTINAMEA, RTQNAME, RTQNAMEA, MULTINAMEL, RTQNAMEL, RTQNAMELA, MULTINAMELA, TYPENAME}; - private static final String[] multinameKindNames = new String[]{"Qname", "QnameA", "Multiname", "MultinameA", "RTQname", "RTQnameA", "MultinameL", "RTQnameL", "RTQnameLA", "MultinameLA", "TypeName"}; - public int kind = -1; - public int name_index = -1; - public int namespace_index = -1; - public int namespace_set_index = -1; - public int qname_index = -1; //for TypeName - public List params; //for TypeName - public boolean deleted; - - public boolean validType() { - boolean cnt = false; - for (int i = 0; i < multinameKinds.length; i++) { - if (multinameKinds[i] == kind) { - cnt = true; - } - } - return cnt; - } - - public Multiname(int kind, int name_index, int namespace_index, int namespace_set_index, int qname_index, List params) { - this.kind = kind; - this.name_index = name_index; - this.namespace_index = namespace_index; - this.namespace_set_index = namespace_set_index; - this.qname_index = qname_index; - this.params = params; - if (!validType()) { - throw new RuntimeException("Invalid multiname kind:" + kind); - } - } - - public boolean isAttribute() { - if (kind == QNAMEA) { - return true; - } - if (kind == MULTINAMEA) { - return true; - } - if (kind == RTQNAMEA) { - return true; - } - if (kind == RTQNAMELA) { - return true; - } - if (kind == MULTINAMELA) { - return true; - } - return false; - } - - public boolean isRuntime() { - if (kind == RTQNAME) { - return true; - } - if (kind == RTQNAMEA) { - return true; - } - if (kind == MULTINAMEL) { - return true; - } - if (kind == MULTINAMELA) { - return true; - } - return false; - } - - public boolean needsName() { - if (kind == RTQNAMEL) { - return true; - } - if (kind == RTQNAMELA) { - return true; - } - if (kind == MULTINAMEL) { - return true; - } - if (kind == MULTINAMELA) { - return true; - } - return false; - } - - public boolean needsNs() { - if (kind == RTQNAME) { - return true; - } - if (kind == RTQNAMEA) { - return true; - } - if (kind == RTQNAMEL) { - return true; - } - if (kind == RTQNAMELA) { - return true; - } - return false; - } - - public String getKindStr() { - String kindStr = "?"; - for (int k = 0; k < multinameKinds.length; k++) { - if (multinameKinds[k] == kind) { - kindStr = multinameKindNames[k]; - break; - } - } - return kindStr; - } - - @Override - public String toString() { - String kindStr = getKindStr(); - return "kind=" + kindStr + " name_index=" + name_index + " namespace_index=" + namespace_index + " namespace_set_index=" + namespace_set_index + " qname_index=" + qname_index + " params_size:" + params.size(); - - } - - private static String namespaceToString(ConstantPool constants, int index) { - if (index == 0) { - return "null"; - } - int type = constants.getNamespace(index).kind; - int name_index = constants.getNamespace(index).name_index; - String name = name_index == 0 ? null : constants.getNamespace(index).getName(constants); - int sub = -1; - for (int n = 1; n < constants.getNamespaceCount(); n++) { - if (constants.getNamespace(n).kind == type && constants.getNamespace(n).hasName(constants.getNamespace(index).getName(constants), constants)) { - sub++; - } - if (n == index) { - break; - } - } - return constants.getNamespace(index).getKindStr() + "(" + (name == null ? "null" : "\"" + Helper.escapeString(name) + "\"") + (sub > 0 ? ",\"" + sub + "\"" : "") + ")"; - } - - private static String namespaceSetToString(ConstantPool constants, int index) { - if (index == 0) { - return "null"; - } - String ret = "["; - for (int n = 0; n < constants.getNamespaceSet(index).namespaces.length; n++) { - if (n > 0) { - ret += ","; - } - int ns = constants.getNamespaceSet(index).namespaces[n]; - ret += namespaceToString(constants, ns); - } - ret += "]"; - return ret; - } - - private static String multinameToString(ConstantPool constants, int index, List fullyQualifiedNames) { - if (index == 0) { - return "null"; - } - return constants.getMultiname(index).toString(constants, fullyQualifiedNames); - } - - public String toString(ConstantPool constants, List fullyQualifiedNames) { - - switch (kind) { - case QNAME: - case QNAMEA: - return getKindStr() + "(" + namespaceToString(constants, namespace_index) + "," + (name_index==0?"null":"\"" + Helper.escapeString(constants.getString(name_index)) + "\"") + ")"; - case RTQNAME: - case RTQNAMEA: - return getKindStr() + "(" + (name_index==0?"null":"\"" + Helper.escapeString(constants.getString(name_index))) + "\"" + ")"; - case RTQNAMEL: - case RTQNAMELA: - return getKindStr() + "()"; - case MULTINAME: - case MULTINAMEA: - return getKindStr() + "(" + (name_index==0?"null":"\"" + Helper.escapeString(constants.getString(name_index)) + "\"") + "," + namespaceSetToString(constants, namespace_set_index) + ")"; - case MULTINAMEL: - case MULTINAMELA: - return getKindStr() + "(" + namespaceSetToString(constants, namespace_set_index) + ")"; - case TYPENAME: - String tret = getKindStr() + "("; - tret += multinameToString(constants, qname_index, fullyQualifiedNames); - tret += "<"; - for (int i = 0; i < params.size(); i++) { - if (i > 0) { - tret += ","; - } - tret += multinameToString(constants, params.get(i), fullyQualifiedNames); - } - tret += ">"; - tret += ")"; - return tret; - } - return null; - } - - private String typeNameToStr(ConstantPool constants, List fullyQualifiedNames) { - if (constants.getMultiname(qname_index).name_index == name_index) { - return "ambiguousTypeName"; - } - String typeNameStr = constants.getMultiname(qname_index).getName(constants, fullyQualifiedNames); - if (!params.isEmpty()) { - typeNameStr += ".<"; - for (int i = 0; i < params.size(); i++) { - if (i > 0) { - typeNameStr += ","; - } - if (params.get(i) == 0) { - typeNameStr += "*"; - } else { - typeNameStr += constants.getMultiname(params.get(i)).getName(constants, fullyQualifiedNames); - } - } - typeNameStr += ">"; - } - return typeNameStr; - } - - public String getName(ConstantPool constants, List fullyQualifiedNames) { - if (kind == TYPENAME) { - return typeNameToStr(constants, fullyQualifiedNames); - } - if (name_index == -1) { - return ""; - } - if (name_index == 0) { - return (isAttribute() ? "@*" : "*"); - } else { - String name = constants.getString(name_index); - if ((fullyQualifiedNames != null) && fullyQualifiedNames.contains(name)) { - return getNameWithNamespace(constants); - } - return (isAttribute() ? "@" : "") + name; - } - } - - public String getNameWithNamespace(ConstantPool constants) { - String ret = ""; - Namespace ns = getNamespace(constants); - if (ns != null) { - String nsname = ns.getName(constants); - if (nsname != null && !nsname.isEmpty()) { - ret += nsname + "."; - } - } - ret += getName(constants, null); - return ret; - } - - public Namespace getNamespace(ConstantPool constants) { - if ((namespace_index == 0) || (namespace_index == -1)) { - return null; - } else { - return constants.getNamespace(namespace_index); - } - } - - public NamespaceSet getNamespaceSet(ConstantPool constants) { - if (namespace_set_index == 0) { - return null; - } else if (namespace_set_index == -1) { - return null; - } else { - return constants.getNamespaceSet(namespace_set_index); - } - } -} +/* + * Copyright (C) 2010-2014 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.abc.types; + +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.helpers.Helper; +import java.util.List; + +public class Multiname { + + public static final int QNAME = 7; + public static final int QNAMEA = 0x0d; + public static final int RTQNAME = 0x0f; + public static final int RTQNAMEA = 0x10; + public static final int RTQNAMEL = 0x11; + public static final int RTQNAMELA = 0x12; + public static final int MULTINAME = 0x09; + public static final int MULTINAMEA = 0x0e; + public static final int MULTINAMEL = 0x1b; + public static final int MULTINAMELA = 0x1c; + public static final int TYPENAME = 0x1d; + private static final int[] multinameKinds = new int[]{QNAME, QNAMEA, MULTINAME, MULTINAMEA, RTQNAME, RTQNAMEA, MULTINAMEL, RTQNAMEL, RTQNAMELA, MULTINAMELA, TYPENAME}; + private static final String[] multinameKindNames = new String[]{"Qname", "QnameA", "Multiname", "MultinameA", "RTQname", "RTQnameA", "MultinameL", "RTQnameL", "RTQnameLA", "MultinameLA", "TypeName"}; + public int kind = -1; + public int name_index = -1; + public int namespace_index = -1; + public int namespace_set_index = -1; + public int qname_index = -1; //for TypeName + public List params; //for TypeName + public boolean deleted; + + public boolean validType() { + boolean cnt = false; + for (int i = 0; i < multinameKinds.length; i++) { + if (multinameKinds[i] == kind) { + cnt = true; + } + } + return cnt; + } + + public Multiname(int kind, int name_index, int namespace_index, int namespace_set_index, int qname_index, List params) { + this.kind = kind; + this.name_index = name_index; + this.namespace_index = namespace_index; + this.namespace_set_index = namespace_set_index; + this.qname_index = qname_index; + this.params = params; + if (!validType()) { + throw new RuntimeException("Invalid multiname kind:" + kind); + } + } + + public boolean isAttribute() { + if (kind == QNAMEA) { + return true; + } + if (kind == MULTINAMEA) { + return true; + } + if (kind == RTQNAMEA) { + return true; + } + if (kind == RTQNAMELA) { + return true; + } + if (kind == MULTINAMELA) { + return true; + } + return false; + } + + public boolean isRuntime() { + if (kind == RTQNAME) { + return true; + } + if (kind == RTQNAMEA) { + return true; + } + if (kind == MULTINAMEL) { + return true; + } + if (kind == MULTINAMELA) { + return true; + } + return false; + } + + public boolean needsName() { + if (kind == RTQNAMEL) { + return true; + } + if (kind == RTQNAMELA) { + return true; + } + if (kind == MULTINAMEL) { + return true; + } + if (kind == MULTINAMELA) { + return true; + } + return false; + } + + public boolean needsNs() { + if (kind == RTQNAME) { + return true; + } + if (kind == RTQNAMEA) { + return true; + } + if (kind == RTQNAMEL) { + return true; + } + if (kind == RTQNAMELA) { + return true; + } + return false; + } + + public String getKindStr() { + String kindStr = "?"; + for (int k = 0; k < multinameKinds.length; k++) { + if (multinameKinds[k] == kind) { + kindStr = multinameKindNames[k]; + break; + } + } + return kindStr; + } + + @Override + public String toString() { + String kindStr = getKindStr(); + return "kind=" + kindStr + " name_index=" + name_index + " namespace_index=" + namespace_index + " namespace_set_index=" + namespace_set_index + " qname_index=" + qname_index + " params_size:" + params.size(); + + } + + private static String namespaceToString(ConstantPool constants, int index) { + if (index == 0) { + return "null"; + } + int type = constants.getNamespace(index).kind; + int name_index = constants.getNamespace(index).name_index; + String name = name_index == 0 ? null : constants.getNamespace(index).getName(constants); + int sub = -1; + for (int n = 1; n < constants.getNamespaceCount(); n++) { + if (constants.getNamespace(n).kind == type && constants.getNamespace(n).hasName(constants.getNamespace(index).getName(constants), constants)) { + sub++; + } + if (n == index) { + break; + } + } + return constants.getNamespace(index).getKindStr() + "(" + (name == null ? "null" : "\"" + Helper.escapeString(name) + "\"") + (sub > 0 ? ",\"" + sub + "\"" : "") + ")"; + } + + private static String namespaceSetToString(ConstantPool constants, int index) { + if (index == 0) { + return "null"; + } + String ret = "["; + for (int n = 0; n < constants.getNamespaceSet(index).namespaces.length; n++) { + if (n > 0) { + ret += ","; + } + int ns = constants.getNamespaceSet(index).namespaces[n]; + ret += namespaceToString(constants, ns); + } + ret += "]"; + return ret; + } + + private static String multinameToString(ConstantPool constants, int index, List fullyQualifiedNames) { + if (index == 0) { + return "null"; + } + return constants.getMultiname(index).toString(constants, fullyQualifiedNames); + } + + public String toString(ConstantPool constants, List fullyQualifiedNames) { + + switch (kind) { + case QNAME: + case QNAMEA: + return getKindStr() + "(" + namespaceToString(constants, namespace_index) + "," + (name_index == 0 ? "null" : "\"" + Helper.escapeString(constants.getString(name_index)) + "\"") + ")"; + case RTQNAME: + case RTQNAMEA: + return getKindStr() + "(" + (name_index == 0 ? "null" : "\"" + Helper.escapeString(constants.getString(name_index))) + "\"" + ")"; + case RTQNAMEL: + case RTQNAMELA: + return getKindStr() + "()"; + case MULTINAME: + case MULTINAMEA: + return getKindStr() + "(" + (name_index == 0 ? "null" : "\"" + Helper.escapeString(constants.getString(name_index)) + "\"") + "," + namespaceSetToString(constants, namespace_set_index) + ")"; + case MULTINAMEL: + case MULTINAMELA: + return getKindStr() + "(" + namespaceSetToString(constants, namespace_set_index) + ")"; + case TYPENAME: + String tret = getKindStr() + "("; + tret += multinameToString(constants, qname_index, fullyQualifiedNames); + tret += "<"; + for (int i = 0; i < params.size(); i++) { + if (i > 0) { + tret += ","; + } + tret += multinameToString(constants, params.get(i), fullyQualifiedNames); + } + tret += ">"; + tret += ")"; + return tret; + } + return null; + } + + private String typeNameToStr(ConstantPool constants, List fullyQualifiedNames) { + if (constants.getMultiname(qname_index).name_index == name_index) { + return "ambiguousTypeName"; + } + String typeNameStr = constants.getMultiname(qname_index).getName(constants, fullyQualifiedNames); + if (!params.isEmpty()) { + typeNameStr += ".<"; + for (int i = 0; i < params.size(); i++) { + if (i > 0) { + typeNameStr += ","; + } + if (params.get(i) == 0) { + typeNameStr += "*"; + } else { + typeNameStr += constants.getMultiname(params.get(i)).getName(constants, fullyQualifiedNames); + } + } + typeNameStr += ">"; + } + return typeNameStr; + } + + public String getName(ConstantPool constants, List fullyQualifiedNames) { + if (kind == TYPENAME) { + return typeNameToStr(constants, fullyQualifiedNames); + } + if (name_index == -1) { + return ""; + } + if (name_index == 0) { + return (isAttribute() ? "@*" : "*"); + } else { + String name = constants.getString(name_index); + if ((fullyQualifiedNames != null) && fullyQualifiedNames.contains(name)) { + return getNameWithNamespace(constants); + } + return (isAttribute() ? "@" : "") + name; + } + } + + public String getNameWithNamespace(ConstantPool constants) { + String ret = ""; + Namespace ns = getNamespace(constants); + if (ns != null) { + String nsname = ns.getName(constants); + if (nsname != null && !nsname.isEmpty()) { + ret += nsname + "."; + } + } + ret += getName(constants, null); + return ret; + } + + public Namespace getNamespace(ConstantPool constants) { + if ((namespace_index == 0) || (namespace_index == -1)) { + return null; + } else { + return constants.getNamespace(namespace_index); + } + } + + public NamespaceSet getNamespaceSet(ConstantPool constants) { + if (namespace_set_index == 0) { + return null; + } else if (namespace_set_index == -1) { + return null; + } else { + return constants.getNamespaceSet(namespace_set_index); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/Namespace.java b/src/com/jpexs/decompiler/flash/abc/types/Namespace.java index 2bef4b1b7..bb998c943 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/Namespace.java +++ b/src/com/jpexs/decompiler/flash/abc/types/Namespace.java @@ -1,125 +1,125 @@ -/* - * Copyright (C) 2010-2014 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.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; - -public class Namespace { - - public static final int KIND_NAMESPACE = 8; - public static final int KIND_PRIVATE = 5; - public static final int KIND_PACKAGE = 22; - public static final int KIND_PACKAGE_INTERNAL = 23; - public static final int KIND_PROTECTED = 24; - public static final int KIND_EXPLICIT = 25; - public static final int KIND_STATIC_PROTECTED = 26; - public static final int[] nameSpaceKinds = new int[]{KIND_NAMESPACE, KIND_PRIVATE, KIND_PACKAGE, KIND_PACKAGE_INTERNAL, KIND_PROTECTED, KIND_EXPLICIT, KIND_STATIC_PROTECTED}; - public static final String[] nameSpaceKindNames = new String[]{"Namespace", "PrivateNamespace", "PackageNamespace", "PackageInternalNs", "ProtectedNamespace", "ExplicitNamespace", "StaticProtectedNs"}; - public static final String[] namePrefixes = new String[]{"", "private", "public", "", "protected", "explicit", "protected"}; - public int kind; - public int name_index; - public boolean deleted; - - public static String kindToStr(int kind) { - for (int i = 0; i < nameSpaceKinds.length; i++) { - if (nameSpaceKinds[i] == kind) { - return nameSpaceKindNames[i]; - } - } - return null; - } - - public static String kindToPrefix(int kind) { - for (int i = 0; i < nameSpaceKinds.length; i++) { - if (nameSpaceKinds[i] == kind) { - return namePrefixes[i]; - } - } - return null; - } - - public Namespace(int kind, int name_index) { - this.kind = kind; - this.name_index = name_index; - } - - public String getKindStr() { - String kindStr = "?"; - for (int k = 0; k < nameSpaceKinds.length; k++) { - if (nameSpaceKinds[k] == kind) { - kindStr = nameSpaceKindNames[k]; - break; - } - } - return kindStr; - } - - @Override - public String toString() { - - return "Namespace: kind=" + getKindStr() + " name_index=" + name_index; - } - - public String toString(ConstantPool constants) { - return getName(constants); //getPrefix(constants)+" "+getName(constants); - } - - public String getNameWithKind(ConstantPool constants) { - String kindStr = getKindStr(); - String nameStr = constants.getString(name_index); - return kindStr + (nameStr.isEmpty() ? "" : " " + nameStr); - } - - public String getPrefix(ABC abc) { - String kindStr = "?"; - for (int k = 0; k < nameSpaceKinds.length; k++) { - if (nameSpaceKinds[k] == kind) { - kindStr = namePrefixes[k]; - break; - } - } - return kindStr; - } - - public String getName(ConstantPool constants) { - if (name_index == 0) { - return null; - } - return constants.getString(name_index); - } - - public boolean hasName(ConstantPool constants,String name){ - if(name == null){ - return name_index == 0; - } - return name.equals(getName(constants)); - } - - public boolean hasName(String name, ConstantPool constants) { - if (name == null && name_index == 0) { - return true; - } - if (name == null) { - return false; - } - if (name_index == 0) { - return false; - } - return constants.getString(name_index).equals(name); - } -} +/* + * Copyright (C) 2010-2014 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.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; + +public class Namespace { + + public static final int KIND_NAMESPACE = 8; + public static final int KIND_PRIVATE = 5; + public static final int KIND_PACKAGE = 22; + public static final int KIND_PACKAGE_INTERNAL = 23; + public static final int KIND_PROTECTED = 24; + public static final int KIND_EXPLICIT = 25; + public static final int KIND_STATIC_PROTECTED = 26; + public static final int[] nameSpaceKinds = new int[]{KIND_NAMESPACE, KIND_PRIVATE, KIND_PACKAGE, KIND_PACKAGE_INTERNAL, KIND_PROTECTED, KIND_EXPLICIT, KIND_STATIC_PROTECTED}; + public static final String[] nameSpaceKindNames = new String[]{"Namespace", "PrivateNamespace", "PackageNamespace", "PackageInternalNs", "ProtectedNamespace", "ExplicitNamespace", "StaticProtectedNs"}; + public static final String[] namePrefixes = new String[]{"", "private", "public", "", "protected", "explicit", "protected"}; + public int kind; + public int name_index; + public boolean deleted; + + public static String kindToStr(int kind) { + for (int i = 0; i < nameSpaceKinds.length; i++) { + if (nameSpaceKinds[i] == kind) { + return nameSpaceKindNames[i]; + } + } + return null; + } + + public static String kindToPrefix(int kind) { + for (int i = 0; i < nameSpaceKinds.length; i++) { + if (nameSpaceKinds[i] == kind) { + return namePrefixes[i]; + } + } + return null; + } + + public Namespace(int kind, int name_index) { + this.kind = kind; + this.name_index = name_index; + } + + public String getKindStr() { + String kindStr = "?"; + for (int k = 0; k < nameSpaceKinds.length; k++) { + if (nameSpaceKinds[k] == kind) { + kindStr = nameSpaceKindNames[k]; + break; + } + } + return kindStr; + } + + @Override + public String toString() { + + return "Namespace: kind=" + getKindStr() + " name_index=" + name_index; + } + + public String toString(ConstantPool constants) { + return getName(constants); //getPrefix(constants)+" "+getName(constants); + } + + public String getNameWithKind(ConstantPool constants) { + String kindStr = getKindStr(); + String nameStr = constants.getString(name_index); + return kindStr + (nameStr.isEmpty() ? "" : " " + nameStr); + } + + public String getPrefix(ABC abc) { + String kindStr = "?"; + for (int k = 0; k < nameSpaceKinds.length; k++) { + if (nameSpaceKinds[k] == kind) { + kindStr = namePrefixes[k]; + break; + } + } + return kindStr; + } + + public String getName(ConstantPool constants) { + if (name_index == 0) { + return null; + } + return constants.getString(name_index); + } + + public boolean hasName(ConstantPool constants, String name) { + if (name == null) { + return name_index == 0; + } + return name.equals(getName(constants)); + } + + public boolean hasName(String name, ConstantPool constants) { + if (name == null && name_index == 0) { + return true; + } + if (name == null) { + return false; + } + if (name_index == 0) { + return false; + } + return constants.getString(name_index).equals(name); + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java b/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java index d7a1bede2..ff98977e9 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java +++ b/src/com/jpexs/decompiler/flash/abc/types/ScriptInfo.java @@ -1,87 +1,87 @@ -/* - * Copyright (C) 2010-2014 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.abc.types; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import java.util.ArrayList; -import java.util.List; - -public class ScriptInfo { - - public boolean deleted; - public int init_index; //MethodInfo - public Traits traits = new Traits(); - - public List> getPacks(ABC abc, int scriptIndex) { - List> ret = new ArrayList<>(); - - List otherTraits = new ArrayList<>(); - for (int j = 0; j < traits.traits.size(); j++) { - Trait t = traits.traits.get(j); - Multiname name = t.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if (!((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) - || (ns.kind == Namespace.KIND_PACKAGE))) { - otherTraits.add(j); - } - } - for (int j = 0; j < traits.traits.size(); j++) { - Trait t = traits.traits.get(j); - Multiname name = t.getName(abc); - Namespace ns = name.getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) - || (ns.kind == Namespace.KIND_PACKAGE)) { - String packageName = ns.getName(abc.constants); //assume not null package - String objectName = name.getName(abc.constants, new ArrayList()); - List traitIndices = new ArrayList<>(); - - traitIndices.add(j); - if (!otherTraits.isEmpty()) { - traitIndices.addAll(otherTraits); - } - otherTraits = new ArrayList<>(); - ClassPath cp = new ClassPath(packageName, objectName); - ret.add(new MyEntry<>(cp, new ScriptPack(cp, abc, scriptIndex, traitIndices))); - } - } - return ret; - } - - public int removeTraps(int scriptIndex, ABC abc, String path) throws InterruptedException { - return traits.removeTraps(scriptIndex, -1, true, abc, path); - } - - @Override - public String toString() { - return "method_index=" + init_index + "\r\n" + traits.toString(); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - return "method_index=" + init_index + "\r\n" + traits.toString(abc, fullyQualifiedNames); - } - - public void delete(ABC abc,boolean d){ - deleted = d; - abc.method_info.get(init_index).delete(abc,d); - traits.delete(abc,d); - } -} +/* + * Copyright (C) 2010-2014 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.abc.types; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import java.util.ArrayList; +import java.util.List; + +public class ScriptInfo { + + public boolean deleted; + public int init_index; //MethodInfo + public Traits traits = new Traits(); + + public List> getPacks(ABC abc, int scriptIndex) { + List> ret = new ArrayList<>(); + + List otherTraits = new ArrayList<>(); + for (int j = 0; j < traits.traits.size(); j++) { + Trait t = traits.traits.get(j); + Multiname name = t.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if (!((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) + || (ns.kind == Namespace.KIND_PACKAGE))) { + otherTraits.add(j); + } + } + for (int j = 0; j < traits.traits.size(); j++) { + Trait t = traits.traits.get(j); + Multiname name = t.getName(abc); + Namespace ns = name.getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE_INTERNAL) + || (ns.kind == Namespace.KIND_PACKAGE)) { + String packageName = ns.getName(abc.constants); //assume not null package + String objectName = name.getName(abc.constants, new ArrayList()); + List traitIndices = new ArrayList<>(); + + traitIndices.add(j); + if (!otherTraits.isEmpty()) { + traitIndices.addAll(otherTraits); + } + otherTraits = new ArrayList<>(); + ClassPath cp = new ClassPath(packageName, objectName); + ret.add(new MyEntry<>(cp, new ScriptPack(cp, abc, scriptIndex, traitIndices))); + } + } + return ret; + } + + public int removeTraps(int scriptIndex, ABC abc, String path) throws InterruptedException { + return traits.removeTraps(scriptIndex, -1, true, abc, path); + } + + @Override + public String toString() { + return "method_index=" + init_index + "\r\n" + traits.toString(); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + return "method_index=" + init_index + "\r\n" + traits.toString(abc, fullyQualifiedNames); + } + + public void delete(ABC abc, boolean d) { + deleted = d; + abc.method_info.get(init_index).delete(abc, d); + traits.delete(abc, d); + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java b/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java index b6422efbb..14bf711e2 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/Trait.java @@ -1,180 +1,180 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.helpers.Helper; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -public abstract class Trait implements Serializable { - - public int name_index; - public int kindType; - public int kindFlags; - public int[] metadata = new int[0]; - public long fileOffset; - public byte[] bytes; - public static final int ATTR_Final = 0x1; - public static final int ATTR_Override = 0x2; - public static final int ATTR_Metadata = 0x4; - public static final int TRAIT_SLOT = 0; - public static final int TRAIT_METHOD = 1; - public static final int TRAIT_GETTER = 2; - public static final int TRAIT_SETTER = 3; - public static final int TRAIT_CLASS = 4; - public static final int TRAIT_FUNCTION = 5; - public static final int TRAIT_CONST = 6; - - public abstract void delete(ABC abc,boolean d); - - public String getModifiers(List abcTags, ABC abc, boolean isStatic) { - String ret = ""; - if ((kindFlags & ATTR_Override) > 0) { - ret += "override"; - } - Multiname m = getName(abc); - if (m != null) { - String nsname = ""; - //if (abc.constants.getNamespace(m.namespace_index).kind == Namespace.KIND_NAMESPACE) { - { - for (ABCContainerTag abcTag : abcTags) { - if (m.namespace_index == -1) { - break; - } - nsname = abcTag.getABC().nsValueToName(abc.constants.getNamespace(m.namespace_index).getName(abc.constants)); - if (nsname == null) { - break; - } - if (nsname.contains(".")) { - nsname = nsname.substring(nsname.lastIndexOf('.') + 1); - } - if (!nsname.isEmpty()) { - break; - } - } - } - Namespace ns = m.getNamespace(abc.constants); - - if (nsname.contains(":")) { - nsname = ""; - } - - if ((!nsname.isEmpty()) && (!nsname.equals("-"))) { - } else { - if (ns != null) { - if (ns.kind == Namespace.KIND_NAMESPACE) { - nsname = ns.getName(abc.constants); - } - } - } - - if (nsname != null && (!nsname.contains(":")) && (!nsname.isEmpty())) { - ret += " " + nsname; - } - if (ns != null) { - ret += " " + ns.getPrefix(abc); - } - } - if (isStatic) { - if ((this instanceof TraitSlotConst) && ((TraitSlotConst) this).isNamespace()) { - //static is automatic - } else { - ret += " static"; - } - } - if ((kindFlags & ATTR_Final) > 0) { - if (!isStatic) { - ret += " final"; - } - } - return ret.trim(); - } - - @Override - public String toString() { - return "name_index=" + name_index + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); - } - - public String toString(ABC abc, List fullyQualifiedNames) { - return abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); - } - - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - writer.appendNoHilight(abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata)); - return writer; - } - - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - } - - public GraphTextWriter toStringPackaged(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - String nsname = ns.getName(abc.constants); - writer.appendNoHilight("package " + nsname).newLine(); //assume not null name - writer.appendNoHilight("{").newLine(); - writer.indent(); - toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - writer.unindent(); - writer.appendNoHilight("}"); - writer.newLine(); - } - return writer; - } - - public void convertPackaged(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); - if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { - convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - } - - public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - return writer; - } - - public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - - public Multiname getName(ABC abc) { - if (name_index == 0) { - return null; - } else { - return abc.constants.getMultiname(name_index); - } - } - - public abstract int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException; - - public String getPath(ABC abc) { - Multiname name = getName(abc); - Namespace ns = name.getNamespace(abc.constants); - String packageName = ns.getName(abc.constants); - String objectName = name.getName(abc.constants, new ArrayList()); - return packageName + "." + objectName; //assume not null name - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.Helper; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public abstract class Trait implements Serializable { + + public int name_index; + public int kindType; + public int kindFlags; + public int[] metadata = new int[0]; + public long fileOffset; + public byte[] bytes; + public static final int ATTR_Final = 0x1; + public static final int ATTR_Override = 0x2; + public static final int ATTR_Metadata = 0x4; + public static final int TRAIT_SLOT = 0; + public static final int TRAIT_METHOD = 1; + public static final int TRAIT_GETTER = 2; + public static final int TRAIT_SETTER = 3; + public static final int TRAIT_CLASS = 4; + public static final int TRAIT_FUNCTION = 5; + public static final int TRAIT_CONST = 6; + + public abstract void delete(ABC abc, boolean d); + + public String getModifiers(List abcTags, ABC abc, boolean isStatic) { + String ret = ""; + if ((kindFlags & ATTR_Override) > 0) { + ret += "override"; + } + Multiname m = getName(abc); + if (m != null) { + String nsname = ""; + //if (abc.constants.getNamespace(m.namespace_index).kind == Namespace.KIND_NAMESPACE) { + { + for (ABCContainerTag abcTag : abcTags) { + if (m.namespace_index == -1) { + break; + } + nsname = abcTag.getABC().nsValueToName(abc.constants.getNamespace(m.namespace_index).getName(abc.constants)); + if (nsname == null) { + break; + } + if (nsname.contains(".")) { + nsname = nsname.substring(nsname.lastIndexOf('.') + 1); + } + if (!nsname.isEmpty()) { + break; + } + } + } + Namespace ns = m.getNamespace(abc.constants); + + if (nsname.contains(":")) { + nsname = ""; + } + + if ((!nsname.isEmpty()) && (!nsname.equals("-"))) { + } else { + if (ns != null) { + if (ns.kind == Namespace.KIND_NAMESPACE) { + nsname = ns.getName(abc.constants); + } + } + } + + if (nsname != null && (!nsname.contains(":")) && (!nsname.isEmpty())) { + ret += " " + nsname; + } + if (ns != null) { + ret += " " + ns.getPrefix(abc); + } + } + if (isStatic) { + if ((this instanceof TraitSlotConst) && ((TraitSlotConst) this).isNamespace()) { + //static is automatic + } else { + ret += " static"; + } + } + if ((kindFlags & ATTR_Final) > 0) { + if (!isStatic) { + ret += " final"; + } + } + return ret.trim(); + } + + @Override + public String toString() { + return "name_index=" + name_index + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); + } + + public String toString(ABC abc, List fullyQualifiedNames) { + return abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata); + } + + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + writer.appendNoHilight(abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " kind=" + kindType + " metadata=" + Helper.intArrToString(metadata)); + return writer; + } + + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + } + + public GraphTextWriter toStringPackaged(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + String nsname = ns.getName(abc.constants); + writer.appendNoHilight("package " + nsname).newLine(); //assume not null name + writer.appendNoHilight("{").newLine(); + writer.indent(); + toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + writer.unindent(); + writer.appendNoHilight("}"); + writer.newLine(); + } + return writer; + } + + public void convertPackaged(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + Namespace ns = abc.constants.getMultiname(name_index).getNamespace(abc.constants); + if ((ns.kind == Namespace.KIND_PACKAGE) || (ns.kind == Namespace.KIND_PACKAGE_INTERNAL)) { + convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + } + + public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + return writer; + } + + public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + + public Multiname getName(ABC abc) { + if (name_index == 0) { + return null; + } else { + return abc.constants.getMultiname(name_index); + } + } + + public abstract int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException; + + public String getPath(ABC abc) { + Multiname name = getName(abc); + Namespace ns = name.getNamespace(abc.constants); + String packageName = ns.getName(abc.constants); + String objectName = name.getName(abc.constants, new ArrayList()); + return packageName + "." + objectName; //assume not null name + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java index aa184c8cd..e202fb06c 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitClass.java @@ -1,568 +1,565 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.NamespaceSet; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -public class TraitClass extends Trait implements TraitWithSlot { - - public int slot_id; - public int class_info; - private static final String[] builtInClasses = {"ArgumentError", "arguments", "Array", "Boolean", "Class", "Date", "DefinitionError", "Error", "EvalError", "Function", "int", "JSON", "Math", "Namespace", "Number", "Object", "QName", "RangeError", "ReferenceError", "RegExp", "SecurityError", "String", "SyntaxError", "TypeError", "uint", "URIError", "VerifyError", "XML", "XMLList"}; - - private boolean classInitializerIsEmpty; - - @Override - public void delete(ABC abc, boolean d) { - abc.class_info.get(class_info).deleted = d; - abc.instance_info.get(class_info).deleted = d; - - - abc.class_info.get(class_info).static_traits.delete(abc,d); - abc.method_info.get(abc.class_info.get(class_info).cinit_index).delete(abc, d); - - abc.instance_info.get(class_info).instance_traits.delete(abc,d); - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).delete(abc, d); - - abc.constants.constant_namespace.get(abc.instance_info.get(class_info).protectedNS).deleted = d; - - abc.constants.constant_multiname.get(name_index).deleted = d; - } - - - - @Override - public int getSlotIndex() { - return slot_id; - } - - private static boolean isBuiltInClass(String name) { - for (String g : builtInClasses) { - if (g.equals(name)) { - return true; - } - } - return false; - } - - @Override - public String toString(ABC abc, List fullyQualifiedNames) { - return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata); - } - - private boolean parseUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - String newimport = ns.getName(abc.constants); - /*if ((ns.kind != Namespace.KIND_PACKAGE) - && (ns.kind != Namespace.KIND_NAMESPACE) - && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { - return false; - }*/ - /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { - String oldimport = newimport; - newimport = null; - for (ABCContainerTag abcTag : abcTags) { - String newname = abcTag.getABC().nsValueToName(oldimport); - if (newname.equals("-")) { - return true; - } - if (!newname.isEmpty()) { - newimport = newname; - break; - } - } - if (newimport == null) { - newimport = oldimport; - newimport += "." + name; - } - if (newimport != null && newimport.isEmpty()) { - newimport = null; - } - if (newimport != null) { - /* if(ns.kind==Namespace.KIND_PACKAGE){ - newimport+=".*"; - }*/ - - if (!imports.contains(newimport)) { - if (newimport.contains(":")) { - return true; - } - String pkg = ""; - if (newimport.contains(".")) { - pkg = newimport.substring(0, newimport.lastIndexOf('.')); - } - String usname = newimport; - if (usname.contains(".")) { - usname = usname.substring(usname.lastIndexOf('.') + 1); - } - if (ns.kind == Namespace.KIND_PACKAGE) { - if (!pkg.equals(ignorePackage)) { - if (!pkg.equals("__AS3__.vec")) { //Automatic import - imports.add(newimport); - } - } - } - if (ns.kind == Namespace.KIND_NAMESPACE) { - if (!usname.equals("*")) { - /*if (!uses.contains(usname)) { - uses.add(usname); - }*/ - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - } - } - return true; - } - } - return false; - } - - private void parseImportsUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { - Namespace ns = abc.constants.getNamespace(namespace_index); - if (name.isEmpty()) { - name = "*"; - } - String newimport = ns.getName(abc.constants); - if (parseUsagesFromNS(abcTags, abc, imports, uses, namespace_index, ignorePackage, name)) { - return; - } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { - return; - } - if (newimport == null) { - newimport = ""; - } - //if (!newimport.equals("")) { - newimport += "." + name; - if (newimport.contains(":")) { - return; - } - if (!imports.contains(newimport)) { - String pkg = newimport.substring(0, newimport.lastIndexOf('.')); - if (pkg.equals("__AS3__.vec")) { //special case - is imported always - return; - } - if (!pkg.equals(ignorePackage)) { - imports.add(newimport); - } - } - //} - } - - private void parseUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } else { - for (int n : nss.namespaces) { - parseUsagesFromNS(abcTags, abc, imports, uses, n, ignorePackage, ""); - } - } - } - } - } - - private void parseImportsUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { - if (m != null) { - if (m.kind == Multiname.TYPENAME) { - if (m.qname_index != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); - } - for (Integer i : m.params) { - if (i != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); - } - } - return; - } - Namespace ns = m.getNamespace(abc.constants); - String name = m.getName(abc.constants, fullyQualifiedNames); - NamespaceSet nss = m.getNamespaceSet(abc.constants); - if (ns != null) { - parseImportsUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); - } - if (nss != null) { - if (nss.namespaces.length == 1) { - parseImportsUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); - } - } - } - } - - private void parseImportsUsagesFromMethodInfo(List abcTags, ABC abc, int method_index, List imports, List uses, String ignorePackage, List fullyQualifiedNames, List visitedMethods) { - if ((method_index < 0) || (method_index >= abc.method_info.size())) { - return; - } - visitedMethods.add(method_index); - if (abc.method_info.get(method_index).ret_type != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); - } - for (int t : abc.method_info.get(method_index).param_types) { - if (t != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); - } - } - MethodBody body = abc.findBody(method_index); - if (body != null) { - parseImportsUsagesFromTraits(abcTags, abc, body.traits, imports, uses, ignorePackage, fullyQualifiedNames); - for (ABCException ex : body.exceptions) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); - } - for (AVM2Instruction ins : body.code.code) { - if (ins.definition instanceof NewFunctionIns) { - if (ins.operands[0] != method_index) { - if (!visitedMethods.contains(ins.operands[0])) { - parseImportsUsagesFromMethodInfo(abcTags, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); - } - } - } - if ((ins.definition instanceof FindPropertyStrictIns) - || (ins.definition instanceof FindPropertyIns) - || (ins.definition instanceof GetLexIns) - || (ins.definition instanceof CoerceIns) - || (ins.definition instanceof AsTypeIns)) { - int m = ins.operands[0]; - if (m != 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); - } - } else { - for (int k = 0; k < ins.definition.operands.length; k++) { - - if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { - int multinameIndex = ins.operands[k]; - parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); - } - } - } - } - } - } - - private void parseImportsUsagesFromTraits(List abcTags, ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { - for (Trait t : ts.traits) { - parseImportsUsagesFromTrait(abcTags, abc, t, imports, uses, ignorePackage, fullyQualifiedNames); - } - } - - private void parseImportsUsagesFromTrait(List abcTags, ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { - if (t instanceof TraitMethodGetterSetter) { - TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(tm.name_index), ignorePackage, fullyQualifiedNames); - if (tm.method_info != 0) { - parseImportsUsagesFromMethodInfo(abcTags, abc, tm.method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList()); - } - } - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, t.getName(abc), ignorePackage, fullyQualifiedNames); - if (t instanceof TraitSlotConst) { - TraitSlotConst ts = (TraitSlotConst) t; - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.name_index), ignorePackage, fullyQualifiedNames); - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.type_index), ignorePackage, fullyQualifiedNames); - } - } - - private List getImportsUsages(List abcTags, ABC abc, List imports, List uses, List fullyQualifiedNames) { - //constructor - - String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).name_index), packageName, fullyQualifiedNames); - - if (abc.instance_info.get(class_info).super_index > 0) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).super_index), packageName, fullyQualifiedNames); - } - for (int i : abc.instance_info.get(class_info).interfaces) { - parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); - } - - //static - parseImportsUsagesFromTraits(abcTags, abc, abc.class_info.get(class_info).static_traits, imports, uses, packageName, fullyQualifiedNames); - - //static initializer - parseImportsUsagesFromMethodInfo(abcTags, abc, abc.class_info.get(class_info).cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); - - //instance - parseImportsUsagesFromTraits(abcTags, abc, abc.instance_info.get(class_info).instance_traits, imports, uses, packageName, fullyQualifiedNames); - - //instance initializer - parseImportsUsagesFromMethodInfo(abcTags, abc, abc.instance_info.get(class_info).iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); - return imports; - } - - @Override - public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { - String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); - return writer.appendNoHilight(classHeader); - } - - @Override - public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { - } - - @Override - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - - writer.startClass(class_info); - String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name - List namesInThisPackage = new ArrayList<>(); - for (ABCContainerTag tag : abcTags) { - for (ScriptInfo si : tag.getABC().script_info) { - for (Trait t : si.traits.traits) { - String spath = t.getPath(tag.getABC()); - String pkg = ""; - String name = spath; - if (spath.contains(".")) { - pkg = spath.substring(0, spath.lastIndexOf('.')); - name = spath.substring(spath.lastIndexOf('.') + 1); - } - if (pkg.equals(packageName)) { - namesInThisPackage.add(name); - } - } - - } - } - //imports - List imports = new ArrayList<>(); - List uses = new ArrayList<>(); - getImportsUsages(abcTags, abc, imports, uses, new ArrayList()); - - fullyQualifiedNames = new ArrayList<>(); - - List importnames = new ArrayList<>(); - importnames.addAll(namesInThisPackage); - for (String ipath : imports) { - String name = ipath; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf('.')); - name = name.substring(name.lastIndexOf('.') + 1); - } - if (importnames.contains(name) || ((!pkg.isEmpty()) && isBuiltInClass(name))) { - fullyQualifiedNames.add(name); - } else { - importnames.add(name); - } - } - /*List imports2 = new ArrayList(); - for (String path : imports) { - String name = path; - String pkg = ""; - if (name.contains(".")) { - pkg = name.substring(0, name.lastIndexOf(".")); - name = name.substring(name.lastIndexOf(".") + 1); - } - - if ((!packageName.equals(pkg)) && (!fullyQualifiedNames.contains(name))) { - imports2.add(path); - } - } - imports = imports2;*/ - - for (int i = 0; i < imports.size(); i++) { - String imp = imports.get(i); - String pkg = imp.substring(0, imp.lastIndexOf('.')); - String name = imp.substring(imp.lastIndexOf('.') + 1); - if (name.equals("*")) { - continue; - } - if (imports.contains(pkg + ".*")) { - imports.remove(i); - i--; - } - } - - boolean hasImport = false; - for (String imp : imports) { - if (!imp.startsWith(".")) { - writer.appendNoHilight("import " + imp + ";").newLine(); - hasImport = true; - } - } - if (hasImport) { - writer.newLine(); - } - for (String us : uses) { - writer.appendNoHilight("use namespace " + us + ";").newLine(); - } - if (uses.size() > 0) { - writer.newLine(); - } - - //class header - String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); - if (classHeader.startsWith("private ")) { - classHeader = classHeader.substring("private ".length()); - } - writer.appendNoHilight(classHeader).newLine(); - writer.appendNoHilight("{").newLine(); - writer.indent(); - - int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (bodyIndex != -1) { - if (!classInitializerIsEmpty) { - writer.newLine(); - writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size() + 1); - writer.appendNoHilight("{").newLine(); - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits); - writer.appendNoHilight("}").newLine(); - writer.endTrait(); - } - } else { - //"/*classInitializer*/"; - } - - //constructor - if (!abc.instance_info.get(class_info).isInterface()) { - String modifier = ""; - Multiname m = abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); - if (m != null) { - Namespace ns = m.getNamespace(abc.constants); - if (ns != null) { - modifier = ns.getPrefix(abc) + " "; - if (modifier.equals(" ")) { - modifier = ""; - } - if (modifier.startsWith("private")) { //cannot have private constuctor - modifier = ""; - } - } - } - - writer.newLine(); - writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size()); - writer.appendNoHilight(modifier); - writer.appendNoHilight("function "); - writer.appendNoHilight(abc.constants.getMultiname(abc.instance_info.get(class_info).name_index).getName(abc.constants, new ArrayList()/*do not want full names here*/)); - writer.appendNoHilight("("); - bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - if (bodyIndex != -1) { - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, fullyQualifiedNames); - } else { - abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, null, abc, fullyQualifiedNames); - } - writer.appendNoHilight(") {").newLine(); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits); - } - writer.appendNoHilight("}").newLine(); - writer.endTrait(); - } - - //static variables,constants & methods - abc.class_info.get(class_info).static_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - abc.instance_info.get(class_info).instance_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - writer.unindent(); - writer.appendNoHilight("}"); // class - writer.endClass(); - writer.newLine(); - return writer; - } - - @Override - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - - fullyQualifiedNames = new ArrayList<>(); - - int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (bodyIndex != -1) { - writer.mark(); - abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits, true); - classInitializerIsEmpty = !writer.getMark(); - } - - //constructor - if (!abc.instance_info.get(class_info).isInterface()) { - bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits, true); - } - } - - //static variables,constants & methods - abc.class_info.get(class_info).static_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - - abc.instance_info.get(class_info).instance_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); - } - - @Override - public Multiname getName(ABC abc) { - return abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); - } - - @Override - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { - int iInitializer = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); - int ret = 0; - if (iInitializer != -1) { - ret += abc.bodies.get(iInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, false, path); - } - int sInitializer = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); - if (sInitializer != -1) { - ret += abc.bodies.get(sInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, true, path); - } - ret += abc.instance_info.get(class_info).instance_traits.removeTraps(scriptIndex, class_info, false, abc, path); - ret += abc.class_info.get(class_info).static_traits.removeTraps(scriptIndex, class_info, true, abc, path); - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewFunctionIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.FindPropertyStrictIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.GetLexIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.AsTypeIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceIns; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.NamespaceSet; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +public class TraitClass extends Trait implements TraitWithSlot { + + public int slot_id; + public int class_info; + private static final String[] builtInClasses = {"ArgumentError", "arguments", "Array", "Boolean", "Class", "Date", "DefinitionError", "Error", "EvalError", "Function", "int", "JSON", "Math", "Namespace", "Number", "Object", "QName", "RangeError", "ReferenceError", "RegExp", "SecurityError", "String", "SyntaxError", "TypeError", "uint", "URIError", "VerifyError", "XML", "XMLList"}; + + private boolean classInitializerIsEmpty; + + @Override + public void delete(ABC abc, boolean d) { + abc.class_info.get(class_info).deleted = d; + abc.instance_info.get(class_info).deleted = d; + + abc.class_info.get(class_info).static_traits.delete(abc, d); + abc.method_info.get(abc.class_info.get(class_info).cinit_index).delete(abc, d); + + abc.instance_info.get(class_info).instance_traits.delete(abc, d); + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).delete(abc, d); + + abc.constants.constant_namespace.get(abc.instance_info.get(class_info).protectedNS).deleted = d; + + abc.constants.constant_multiname.get(name_index).deleted = d; + } + + @Override + public int getSlotIndex() { + return slot_id; + } + + private static boolean isBuiltInClass(String name) { + for (String g : builtInClasses) { + if (g.equals(name)) { + return true; + } + } + return false; + } + + @Override + public String toString(ABC abc, List fullyQualifiedNames) { + return "Class " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " class_info=" + class_info + " metadata=" + Helper.intArrToString(metadata); + } + + private boolean parseUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + if (name.isEmpty()) { + name = "*"; + } + String newimport = ns.getName(abc.constants); + /*if ((ns.kind != Namespace.KIND_PACKAGE) + && (ns.kind != Namespace.KIND_NAMESPACE) + && (ns.kind != Namespace.KIND_STATIC_PROTECTED)) { + return false; + }*/ + /*if (ns.kind == Namespace.KIND_NAMESPACE)*/ { + String oldimport = newimport; + newimport = null; + for (ABCContainerTag abcTag : abcTags) { + String newname = abcTag.getABC().nsValueToName(oldimport); + if (newname.equals("-")) { + return true; + } + if (!newname.isEmpty()) { + newimport = newname; + break; + } + } + if (newimport == null) { + newimport = oldimport; + newimport += "." + name; + } + if (newimport != null && newimport.isEmpty()) { + newimport = null; + } + if (newimport != null) { + /* if(ns.kind==Namespace.KIND_PACKAGE){ + newimport+=".*"; + }*/ + + if (!imports.contains(newimport)) { + if (newimport.contains(":")) { + return true; + } + String pkg = ""; + if (newimport.contains(".")) { + pkg = newimport.substring(0, newimport.lastIndexOf('.')); + } + String usname = newimport; + if (usname.contains(".")) { + usname = usname.substring(usname.lastIndexOf('.') + 1); + } + if (ns.kind == Namespace.KIND_PACKAGE) { + if (!pkg.equals(ignorePackage)) { + if (!pkg.equals("__AS3__.vec")) { //Automatic import + imports.add(newimport); + } + } + } + if (ns.kind == Namespace.KIND_NAMESPACE) { + if (!usname.equals("*")) { + /*if (!uses.contains(usname)) { + uses.add(usname); + }*/ + if (!pkg.equals(ignorePackage)) { + imports.add(newimport); + } + } + } + } + return true; + } + } + return false; + } + + private void parseImportsUsagesFromNS(List abcTags, ABC abc, List imports, List uses, int namespace_index, String ignorePackage, String name) { + Namespace ns = abc.constants.getNamespace(namespace_index); + if (name.isEmpty()) { + name = "*"; + } + String newimport = ns.getName(abc.constants); + if (parseUsagesFromNS(abcTags, abc, imports, uses, namespace_index, ignorePackage, name)) { + return; + } else if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { + return; + } + if (newimport == null) { + newimport = ""; + } + //if (!newimport.equals("")) { + newimport += "." + name; + if (newimport.contains(":")) { + return; + } + if (!imports.contains(newimport)) { + String pkg = newimport.substring(0, newimport.lastIndexOf('.')); + if (pkg.equals("__AS3__.vec")) { //special case - is imported always + return; + } + if (!pkg.equals(ignorePackage)) { + imports.add(newimport); + } + } + //} + } + + private void parseUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + if (nss.namespaces.length == 1) { + parseUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + } else { + for (int n : nss.namespaces) { + parseUsagesFromNS(abcTags, abc, imports, uses, n, ignorePackage, ""); + } + } + } + } + } + + private void parseImportsUsagesFromMultiname(List abcTags, ABC abc, List imports, List uses, Multiname m, String ignorePackage, List fullyQualifiedNames) { + if (m != null) { + if (m.kind == Multiname.TYPENAME) { + if (m.qname_index != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m.qname_index), ignorePackage, fullyQualifiedNames); + } + for (Integer i : m.params) { + if (i != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), ignorePackage, fullyQualifiedNames); + } + } + return; + } + Namespace ns = m.getNamespace(abc.constants); + String name = m.getName(abc.constants, fullyQualifiedNames); + NamespaceSet nss = m.getNamespaceSet(abc.constants); + if (ns != null) { + parseImportsUsagesFromNS(abcTags, abc, imports, uses, m.namespace_index, ignorePackage, name); + } + if (nss != null) { + if (nss.namespaces.length == 1) { + parseImportsUsagesFromNS(abcTags, abc, imports, uses, nss.namespaces[0], ignorePackage, name); + } + } + } + } + + private void parseImportsUsagesFromMethodInfo(List abcTags, ABC abc, int method_index, List imports, List uses, String ignorePackage, List fullyQualifiedNames, List visitedMethods) { + if ((method_index < 0) || (method_index >= abc.method_info.size())) { + return; + } + visitedMethods.add(method_index); + if (abc.method_info.get(method_index).ret_type != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.method_info.get(method_index).ret_type), ignorePackage, fullyQualifiedNames); + } + for (int t : abc.method_info.get(method_index).param_types) { + if (t != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(t), ignorePackage, fullyQualifiedNames); + } + } + MethodBody body = abc.findBody(method_index); + if (body != null) { + parseImportsUsagesFromTraits(abcTags, abc, body.traits, imports, uses, ignorePackage, fullyQualifiedNames); + for (ABCException ex : body.exceptions) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ex.type_index), ignorePackage, fullyQualifiedNames); + } + for (AVM2Instruction ins : body.code.code) { + if (ins.definition instanceof NewFunctionIns) { + if (ins.operands[0] != method_index) { + if (!visitedMethods.contains(ins.operands[0])) { + parseImportsUsagesFromMethodInfo(abcTags, abc, ins.operands[0], imports, uses, ignorePackage, fullyQualifiedNames, visitedMethods); + } + } + } + if ((ins.definition instanceof FindPropertyStrictIns) + || (ins.definition instanceof FindPropertyIns) + || (ins.definition instanceof GetLexIns) + || (ins.definition instanceof CoerceIns) + || (ins.definition instanceof AsTypeIns)) { + int m = ins.operands[0]; + if (m != 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames); + } + } else { + for (int k = 0; k < ins.definition.operands.length; k++) { + + if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) { + int multinameIndex = ins.operands[k]; + parseUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames); + } + } + } + } + } + } + + private void parseImportsUsagesFromTraits(List abcTags, ABC abc, Traits ts, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + for (Trait t : ts.traits) { + parseImportsUsagesFromTrait(abcTags, abc, t, imports, uses, ignorePackage, fullyQualifiedNames); + } + } + + private void parseImportsUsagesFromTrait(List abcTags, ABC abc, Trait t, List imports, List uses, String ignorePackage, List fullyQualifiedNames) { + if (t instanceof TraitMethodGetterSetter) { + TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(tm.name_index), ignorePackage, fullyQualifiedNames); + if (tm.method_info != 0) { + parseImportsUsagesFromMethodInfo(abcTags, abc, tm.method_info, imports, uses, ignorePackage, fullyQualifiedNames, new ArrayList()); + } + } + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, t.getName(abc), ignorePackage, fullyQualifiedNames); + if (t instanceof TraitSlotConst) { + TraitSlotConst ts = (TraitSlotConst) t; + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.name_index), ignorePackage, fullyQualifiedNames); + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(ts.type_index), ignorePackage, fullyQualifiedNames); + } + } + + private List getImportsUsages(List abcTags, ABC abc, List imports, List uses, List fullyQualifiedNames) { + //constructor + + String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name + + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).name_index), packageName, fullyQualifiedNames); + + if (abc.instance_info.get(class_info).super_index > 0) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(abc.instance_info.get(class_info).super_index), packageName, fullyQualifiedNames); + } + for (int i : abc.instance_info.get(class_info).interfaces) { + parseImportsUsagesFromMultiname(abcTags, abc, imports, uses, abc.constants.getMultiname(i), packageName, fullyQualifiedNames); + } + + //static + parseImportsUsagesFromTraits(abcTags, abc, abc.class_info.get(class_info).static_traits, imports, uses, packageName, fullyQualifiedNames); + + //static initializer + parseImportsUsagesFromMethodInfo(abcTags, abc, abc.class_info.get(class_info).cinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); + + //instance + parseImportsUsagesFromTraits(abcTags, abc, abc.instance_info.get(class_info).instance_traits, imports, uses, packageName, fullyQualifiedNames); + + //instance initializer + parseImportsUsagesFromMethodInfo(abcTags, abc, abc.instance_info.get(class_info).iinit_index, imports, uses, packageName, fullyQualifiedNames, new ArrayList()); + return imports; + } + + @Override + public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { + String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); + return writer.appendNoHilight(classHeader); + } + + @Override + public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { + } + + @Override + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + + writer.startClass(class_info); + String packageName = abc.instance_info.get(class_info).getName(abc.constants).getNamespace(abc.constants).getName(abc.constants); //assume not null name + List namesInThisPackage = new ArrayList<>(); + for (ABCContainerTag tag : abcTags) { + for (ScriptInfo si : tag.getABC().script_info) { + for (Trait t : si.traits.traits) { + String spath = t.getPath(tag.getABC()); + String pkg = ""; + String name = spath; + if (spath.contains(".")) { + pkg = spath.substring(0, spath.lastIndexOf('.')); + name = spath.substring(spath.lastIndexOf('.') + 1); + } + if (pkg.equals(packageName)) { + namesInThisPackage.add(name); + } + } + + } + } + //imports + List imports = new ArrayList<>(); + List uses = new ArrayList<>(); + getImportsUsages(abcTags, abc, imports, uses, new ArrayList()); + + fullyQualifiedNames = new ArrayList<>(); + + List importnames = new ArrayList<>(); + importnames.addAll(namesInThisPackage); + for (String ipath : imports) { + String name = ipath; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf('.')); + name = name.substring(name.lastIndexOf('.') + 1); + } + if (importnames.contains(name) || ((!pkg.isEmpty()) && isBuiltInClass(name))) { + fullyQualifiedNames.add(name); + } else { + importnames.add(name); + } + } + /*List imports2 = new ArrayList(); + for (String path : imports) { + String name = path; + String pkg = ""; + if (name.contains(".")) { + pkg = name.substring(0, name.lastIndexOf(".")); + name = name.substring(name.lastIndexOf(".") + 1); + } + + if ((!packageName.equals(pkg)) && (!fullyQualifiedNames.contains(name))) { + imports2.add(path); + } + } + imports = imports2;*/ + + for (int i = 0; i < imports.size(); i++) { + String imp = imports.get(i); + String pkg = imp.substring(0, imp.lastIndexOf('.')); + String name = imp.substring(imp.lastIndexOf('.') + 1); + if (name.equals("*")) { + continue; + } + if (imports.contains(pkg + ".*")) { + imports.remove(i); + i--; + } + } + + boolean hasImport = false; + for (String imp : imports) { + if (!imp.startsWith(".")) { + writer.appendNoHilight("import " + imp + ";").newLine(); + hasImport = true; + } + } + if (hasImport) { + writer.newLine(); + } + for (String us : uses) { + writer.appendNoHilight("use namespace " + us + ";").newLine(); + } + if (uses.size() > 0) { + writer.newLine(); + } + + //class header + String classHeader = abc.instance_info.get(class_info).getClassHeaderStr(abc, fullyQualifiedNames); + if (classHeader.startsWith("private ")) { + classHeader = classHeader.substring("private ".length()); + } + writer.appendNoHilight(classHeader).newLine(); + writer.appendNoHilight("{").newLine(); + writer.indent(); + + int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (bodyIndex != -1) { + if (!classInitializerIsEmpty) { + writer.newLine(); + writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size() + 1); + writer.appendNoHilight("{").newLine(); + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits); + writer.appendNoHilight("}").newLine(); + writer.endTrait(); + } + } else { + //"/*classInitializer*/"; + } + + //constructor + if (!abc.instance_info.get(class_info).isInterface()) { + String modifier = ""; + Multiname m = abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); + if (m != null) { + Namespace ns = m.getNamespace(abc.constants); + if (ns != null) { + modifier = ns.getPrefix(abc) + " "; + if (modifier.equals(" ")) { + modifier = ""; + } + if (modifier.startsWith("private")) { //cannot have private constuctor + modifier = ""; + } + } + } + + writer.newLine(); + writer.startTrait(abc.class_info.get(class_info).static_traits.traits.size() + abc.instance_info.get(class_info).instance_traits.traits.size()); + writer.appendNoHilight(modifier); + writer.appendNoHilight("function "); + writer.appendNoHilight(abc.constants.getMultiname(abc.instance_info.get(class_info).name_index).getName(abc.constants, new ArrayList()/*do not want full names here*/)); + writer.appendNoHilight("("); + bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + if (bodyIndex != -1) { + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, abc.bodies.get(bodyIndex), abc, fullyQualifiedNames); + } else { + abc.method_info.get(abc.instance_info.get(class_info).iinit_index).getParamStr(writer, abc.constants, null, abc, fullyQualifiedNames); + } + writer.appendNoHilight(") {").newLine(); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).toString(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits); + } + writer.appendNoHilight("}").newLine(); + writer.endTrait(); + } + + //static variables,constants & methods + abc.class_info.get(class_info).static_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + abc.instance_info.get(class_info).instance_traits.toString(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + writer.unindent(); + writer.appendNoHilight("}"); // class + writer.endClass(); + writer.newLine(); + return writer; + } + + @Override + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + + fullyQualifiedNames = new ArrayList<>(); + + int bodyIndex = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (bodyIndex != -1) { + writer.mark(); + abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".staticinitializer", exportMode, true, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), true, writer, fullyQualifiedNames, abc.class_info.get(class_info).static_traits, true); + classInitializerIsEmpty = !writer.getMark(); + } + + //constructor + if (!abc.instance_info.get(class_info).isInterface()) { + bodyIndex = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).convert(path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames) + ".initializer", exportMode, false, scriptIndex, class_info, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, abc.instance_info.get(class_info).instance_traits, true); + } + } + + //static variables,constants & methods + abc.class_info.get(class_info).static_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, true, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + + abc.instance_info.get(class_info).instance_traits.convert(this, path +/*packageName +*/ "/" + abc.instance_info.get(class_info).getName(abc.constants).getName(abc.constants, fullyQualifiedNames), abcTags, abc, false, exportMode, false, scriptIndex, class_info, writer, fullyQualifiedNames, parallel); + } + + @Override + public Multiname getName(ABC abc) { + return abc.constants.getMultiname(abc.instance_info.get(class_info).name_index); + } + + @Override + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { + int iInitializer = abc.findBodyIndex(abc.instance_info.get(class_info).iinit_index); + int ret = 0; + if (iInitializer != -1) { + ret += abc.bodies.get(iInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, false, path); + } + int sInitializer = abc.findBodyIndex(abc.class_info.get(class_info).cinit_index); + if (sInitializer != -1) { + ret += abc.bodies.get(sInitializer).removeTraps(abc.constants, abc, this, scriptIndex, class_info, true, path); + } + ret += abc.instance_info.get(class_info).instance_traits.removeTraps(scriptIndex, class_info, false, abc, path); + ret += abc.class_info.get(class_info).static_traits.removeTraps(scriptIndex, class_info, true, abc, path); + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java index 3fdbc40a4..3017c3504 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitFunction.java @@ -1,115 +1,112 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.helpers.Helper; -import java.util.List; -import java.util.Stack; - -public class TraitFunction extends Trait implements TraitWithSlot { - - public int slot_id; - public int method_info; - - @Override - public void delete(ABC abc,boolean d) { - abc.constants.constant_multiname.get(name_index).deleted = d; - abc.method_info.get(method_info).delete(abc,d); - } - - - - - @Override - public int getSlotIndex() { - return slot_id; - } - - @Override - public String toString(ABC abc, List fullyQualifiedNames) { - return "Function " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " method_info=" + method_info + " metadata=" + Helper.intArrToString(metadata); - } - - @Override - public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { - String modifier = getModifiers(abcTags, abc, isStatic) + " "; - MethodBody body = abc.findBody(method_info); - if (body == null) { - modifier = "native " + modifier; - } - if (modifier.equals(" ")) { - modifier = ""; - } - writer.appendNoHilight(modifier); - writer.hilightSpecial("function ", "traittype"); - writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), "traitname"); - writer.appendNoHilight("("); - abc.method_info.get(method_info).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames); - writer.appendNoHilight(") : "); - abc.method_info.get(method_info).getReturnTypeStr(writer, abc.constants, fullyQualifiedNames); - return writer; - } - - @Override - public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { - } - - @Override - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - toStringHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - if (abc.instance_info.get(classIndex).isInterface()) { - writer.appendNoHilight(";"); - } else { - writer.appendNoHilight(" {").newLine(); - int bodyIndex = abc.findBodyIndex(method_info); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).toString(path + "." + abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null); - } - writer.newLine(); - writer.appendNoHilight("}"); - } - writer.newLine(); - return writer; - } - - @Override - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - convertHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - if (!abc.instance_info.get(classIndex).isInterface()) { - int bodyIndex = abc.findBodyIndex(method_info); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).convert(path + "." + abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null, true); - } - } - } - - @Override - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { - int bodyIndex = abc.findBodyIndex(method_info); - if (bodyIndex != -1) { - return abc.bodies.get(bodyIndex).removeTraps(abc.constants, abc, this, scriptIndex, classIndex, isStatic, path); - } - return 0; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.helpers.Helper; +import java.util.List; +import java.util.Stack; + +public class TraitFunction extends Trait implements TraitWithSlot { + + public int slot_id; + public int method_info; + + @Override + public void delete(ABC abc, boolean d) { + abc.constants.constant_multiname.get(name_index).deleted = d; + abc.method_info.get(method_info).delete(abc, d); + } + + @Override + public int getSlotIndex() { + return slot_id; + } + + @Override + public String toString(ABC abc, List fullyQualifiedNames) { + return "Function " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " method_info=" + method_info + " metadata=" + Helper.intArrToString(metadata); + } + + @Override + public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { + String modifier = getModifiers(abcTags, abc, isStatic) + " "; + MethodBody body = abc.findBody(method_info); + if (body == null) { + modifier = "native " + modifier; + } + if (modifier.equals(" ")) { + modifier = ""; + } + writer.appendNoHilight(modifier); + writer.hilightSpecial("function ", "traittype"); + writer.hilightSpecial(abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), "traitname"); + writer.appendNoHilight("("); + abc.method_info.get(method_info).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames); + writer.appendNoHilight(") : "); + abc.method_info.get(method_info).getReturnTypeStr(writer, abc.constants, fullyQualifiedNames); + return writer; + } + + @Override + public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { + } + + @Override + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + toStringHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + if (abc.instance_info.get(classIndex).isInterface()) { + writer.appendNoHilight(";"); + } else { + writer.appendNoHilight(" {").newLine(); + int bodyIndex = abc.findBodyIndex(method_info); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).toString(path + "." + abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null); + } + writer.newLine(); + writer.appendNoHilight("}"); + } + writer.newLine(); + return writer; + } + + @Override + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + convertHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + if (!abc.instance_info.get(classIndex).isInterface()) { + int bodyIndex = abc.findBodyIndex(method_info); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).convert(path + "." + abc.constants.getMultiname(name_index).getName(abc.constants, fullyQualifiedNames), exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null, true); + } + } + } + + @Override + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { + int bodyIndex = abc.findBodyIndex(method_info); + if (bodyIndex != -1) { + return abc.bodies.get(bodyIndex).removeTraps(abc.constants, abc, this, scriptIndex, classIndex, isStatic, path); + } + return 0; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java index 22f4f1f01..7aaadd745 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitMethodGetterSetter.java @@ -1,118 +1,117 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.helpers.Helper; -import java.util.List; -import java.util.Stack; - -public class TraitMethodGetterSetter extends Trait { - - public int disp_id; //compiler assigned value that helps overriding - public int method_info; - - - @Override - public void delete(ABC abc,boolean d) { - abc.constants.constant_multiname.get(name_index).deleted = true; - abc.method_info.get(method_info).delete(abc,d); - } - - @Override - public String toString(ABC abc, List fullyQualifiedNames) { - return "0x" + Helper.formatAddress(fileOffset) + " " + Helper.byteArrToString(bytes) + " MethodGetterSetter " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " disp_id=" + disp_id + " method_info=" + method_info + " metadata=" + Helper.intArrToString(metadata); - } - - @Override - public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { - } - - @Override - public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { - String modifier = getModifiers(abcTags, abc, isStatic) + " "; - if (modifier.equals(" ")) { - modifier = ""; - } - String addKind = ""; - if (kindType == TRAIT_GETTER) { - addKind = "get "; - } - if (kindType == TRAIT_SETTER) { - addKind = "set "; - } - MethodBody body = abc.findBody(method_info); - - if (((classIndex == -1) || (!abc.instance_info.get(classIndex).isInterface())) && (body == null)) { - modifier = "native " + modifier; - } - - writer.appendNoHilight(modifier); - writer.hilightSpecial("function " + addKind, "traittype"); - writer.hilightSpecial(getName(abc).getName(abc.constants, fullyQualifiedNames), "traitname"); - writer.appendNoHilight("("); - abc.method_info.get(method_info).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames); - writer.appendNoHilight(") : "); - abc.method_info.get(method_info).getReturnTypeStr(writer, abc.constants, fullyQualifiedNames); - return writer; - } - - @Override - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - path = path + "." + getName(abc).getName(abc.constants, fullyQualifiedNames); - convertHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - int bodyIndex = abc.findBodyIndex(method_info); - if (!(classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1)) { - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).convert(path, exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null, true); - } - } - } - - @Override - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - path = path + "." + getName(abc).getName(abc.constants, fullyQualifiedNames); - toStringHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - int bodyIndex = abc.findBodyIndex(method_info); - if (classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1) { - writer.appendNoHilight(";"); - } else { - writer.appendNoHilight(" {").newLine(); - if (bodyIndex != -1) { - abc.bodies.get(bodyIndex).toString(path, exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null); - } - writer.appendNoHilight("}"); - } - writer.newLine(); - return writer; - } - - @Override - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { - int bodyIndex = abc.findBodyIndex(method_info); - if (bodyIndex != -1) { - return abc.bodies.get(bodyIndex).removeTraps(abc.constants, abc, this, scriptIndex, classIndex, isStatic, path); - } - return 0; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.helpers.Helper; +import java.util.List; +import java.util.Stack; + +public class TraitMethodGetterSetter extends Trait { + + public int disp_id; //compiler assigned value that helps overriding + public int method_info; + + @Override + public void delete(ABC abc, boolean d) { + abc.constants.constant_multiname.get(name_index).deleted = true; + abc.method_info.get(method_info).delete(abc, d); + } + + @Override + public String toString(ABC abc, List fullyQualifiedNames) { + return "0x" + Helper.formatAddress(fileOffset) + " " + Helper.byteArrToString(bytes) + " MethodGetterSetter " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " disp_id=" + disp_id + " method_info=" + method_info + " metadata=" + Helper.intArrToString(metadata); + } + + @Override + public void convertHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) { + } + + @Override + public GraphTextWriter toStringHeader(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) { + String modifier = getModifiers(abcTags, abc, isStatic) + " "; + if (modifier.equals(" ")) { + modifier = ""; + } + String addKind = ""; + if (kindType == TRAIT_GETTER) { + addKind = "get "; + } + if (kindType == TRAIT_SETTER) { + addKind = "set "; + } + MethodBody body = abc.findBody(method_info); + + if (((classIndex == -1) || (!abc.instance_info.get(classIndex).isInterface())) && (body == null)) { + modifier = "native " + modifier; + } + + writer.appendNoHilight(modifier); + writer.hilightSpecial("function " + addKind, "traittype"); + writer.hilightSpecial(getName(abc).getName(abc.constants, fullyQualifiedNames), "traitname"); + writer.appendNoHilight("("); + abc.method_info.get(method_info).getParamStr(writer, abc.constants, body, abc, fullyQualifiedNames); + writer.appendNoHilight(") : "); + abc.method_info.get(method_info).getReturnTypeStr(writer, abc.constants, fullyQualifiedNames); + return writer; + } + + @Override + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + path = path + "." + getName(abc).getName(abc.constants, fullyQualifiedNames); + convertHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + int bodyIndex = abc.findBodyIndex(method_info); + if (!(classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1)) { + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).convert(path, exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null, true); + } + } + } + + @Override + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + path = path + "." + getName(abc).getName(abc.constants, fullyQualifiedNames); + toStringHeader(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + int bodyIndex = abc.findBodyIndex(method_info); + if (classIndex != -1 && abc.instance_info.get(classIndex).isInterface() || bodyIndex == -1) { + writer.appendNoHilight(";"); + } else { + writer.appendNoHilight(" {").newLine(); + if (bodyIndex != -1) { + abc.bodies.get(bodyIndex).toString(path, exportMode, isStatic, scriptIndex, classIndex, abc, this, abc.constants, abc.method_info, new Stack(), false, writer, fullyQualifiedNames, null); + } + writer.appendNoHilight("}"); + } + writer.newLine(); + return writer; + } + + @Override + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { + int bodyIndex = abc.findBodyIndex(method_info); + if (bodyIndex != -1) { + return abc.bodies.get(bodyIndex).removeTraps(abc.constants, abc, this, scriptIndex, classIndex, isStatic, path); + } + return 0; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java index 11203db3d..fe6635231 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java @@ -1,177 +1,177 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Helper; -import java.util.HashMap; -import java.util.List; - -public class TraitSlotConst extends Trait implements TraitWithSlot { - - public int slot_id; - public int type_index; - public int value_index; - public int value_kind; - public GraphTargetItem assignedValue; - - @Override - public void delete(ABC abc,boolean d) { - abc.constants.constant_multiname.get(name_index).deleted = d; - } - - @Override - public int getSlotIndex() { - return slot_id; - } - - @Override - public String toString(ABC abc, List fullyQualifiedNames) { - String typeStr = "*"; - if (type_index > 0) { - typeStr = abc.constants.getMultiname(type_index).toString(abc.constants, fullyQualifiedNames); - } - return "0x" + Helper.formatAddress(fileOffset) + " " + Helper.byteArrToString(bytes) + " SlotConst " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " type=" + typeStr + " value=" + (new ValueKind(value_index, value_kind)).toString(abc.constants) + " metadata=" + Helper.intArrToString(metadata); - } - - public String getType(ConstantPool constants, List fullyQualifiedNames) { - String typeStr = "*"; - if (type_index > 0) { - typeStr = constants.getMultiname(type_index).getName(constants, fullyQualifiedNames); - } - return typeStr; - } - - public GraphTextWriter getNameStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames) { - String typeStr = getType(abc.constants, fullyQualifiedNames); - if (typeStr.equals("*")) { - typeStr = ""; - } else { - typeStr = ":" + typeStr; - } - ValueKind val = null; - if (value_kind != 0) { - val = new ValueKind(value_index, value_kind); - } - - String slotconst = "var"; - if (kindType == TRAIT_CONST) { - slotconst = "const"; - } - if (val != null && val.isNamespace()) { - slotconst = "namespace"; - } - writer.hilightSpecial(slotconst + " ", "traittype"); - writer.hilightSpecial(getName(abc).getName(abc.constants, fullyQualifiedNames), "traitname"); - writer.hilightSpecial(typeStr, "traittypename"); - return writer; - } - - public void getValueStr(Trait parent, GraphTextWriter writer, ABC abc, List fullyQualifiedNames) throws InterruptedException { - if (assignedValue != null) { - if (parent instanceof TraitClass) { - TraitClass tc = (TraitClass) parent; - int traitInitId = abc.class_info.get(tc.class_info).static_traits.traits.size() - + abc.instance_info.get(tc.class_info).instance_traits.traits.size() + 1; - int initMethod = abc.class_info.get(tc.class_info).cinit_index; - writer.startTrait(traitInitId); - writer.startMethod(initMethod); - } - assignedValue.toString(writer, LocalData.create(abc.constants, new HashMap(), fullyQualifiedNames)); - if (parent instanceof TraitClass) { - writer.endMethod(); - writer.endTrait(); - } - return; - } - - if (value_kind != 0) { - ValueKind val = new ValueKind(value_index, value_kind); - writer.hilightSpecial(val.toString(abc.constants), "traitvalue"); - } - } - - public boolean isNamespace() { - if (value_kind != 0) { - ValueKind val = new ValueKind(value_index, value_kind); - return val.isNamespace(); - } - return false; - } - - @Override - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - String modifier = getModifiers(abcTags, abc, isStatic) + " "; - if (modifier.equals(" ")) { - modifier = ""; - } - Multiname n = getName(abc); - boolean showModifier = true; - if ((classIndex == -1) && (n != null)) { - Namespace ns = n.getNamespace(abc.constants); - if (ns == null) { - showModifier = false; - } else { - if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { - showModifier = false; - } - } - } - if (!showModifier) { - modifier = ""; - } - writer.appendNoHilight(modifier); - getNameStr(writer, abc, fullyQualifiedNames); - if (assignedValue != null || value_kind != 0) { - writer.appendNoHilight(" = "); - getValueStr(parent, writer, abc, fullyQualifiedNames); - } - return writer.appendNoHilight(";").newLine(); - } - - @Override - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - getNameStr(writer, abc, fullyQualifiedNames); - if (assignedValue != null || value_kind != 0) { - getValueStr(parent, writer, abc, fullyQualifiedNames); - } - } - - public boolean isConst() { - return kindType == TRAIT_CONST; - } - - public boolean isVar() { - return kindType == TRAIT_SLOT; - } - - @Override - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) { - //do nothing - return 0; - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; +import java.util.HashMap; +import java.util.List; + +public class TraitSlotConst extends Trait implements TraitWithSlot { + + public int slot_id; + public int type_index; + public int value_index; + public int value_kind; + public GraphTargetItem assignedValue; + + @Override + public void delete(ABC abc, boolean d) { + abc.constants.constant_multiname.get(name_index).deleted = d; + } + + @Override + public int getSlotIndex() { + return slot_id; + } + + @Override + public String toString(ABC abc, List fullyQualifiedNames) { + String typeStr = "*"; + if (type_index > 0) { + typeStr = abc.constants.getMultiname(type_index).toString(abc.constants, fullyQualifiedNames); + } + return "0x" + Helper.formatAddress(fileOffset) + " " + Helper.byteArrToString(bytes) + " SlotConst " + abc.constants.getMultiname(name_index).toString(abc.constants, fullyQualifiedNames) + " slot=" + slot_id + " type=" + typeStr + " value=" + (new ValueKind(value_index, value_kind)).toString(abc.constants) + " metadata=" + Helper.intArrToString(metadata); + } + + public String getType(ConstantPool constants, List fullyQualifiedNames) { + String typeStr = "*"; + if (type_index > 0) { + typeStr = constants.getMultiname(type_index).getName(constants, fullyQualifiedNames); + } + return typeStr; + } + + public GraphTextWriter getNameStr(GraphTextWriter writer, ABC abc, List fullyQualifiedNames) { + String typeStr = getType(abc.constants, fullyQualifiedNames); + if (typeStr.equals("*")) { + typeStr = ""; + } else { + typeStr = ":" + typeStr; + } + ValueKind val = null; + if (value_kind != 0) { + val = new ValueKind(value_index, value_kind); + } + + String slotconst = "var"; + if (kindType == TRAIT_CONST) { + slotconst = "const"; + } + if (val != null && val.isNamespace()) { + slotconst = "namespace"; + } + writer.hilightSpecial(slotconst + " ", "traittype"); + writer.hilightSpecial(getName(abc).getName(abc.constants, fullyQualifiedNames), "traitname"); + writer.hilightSpecial(typeStr, "traittypename"); + return writer; + } + + public void getValueStr(Trait parent, GraphTextWriter writer, ABC abc, List fullyQualifiedNames) throws InterruptedException { + if (assignedValue != null) { + if (parent instanceof TraitClass) { + TraitClass tc = (TraitClass) parent; + int traitInitId = abc.class_info.get(tc.class_info).static_traits.traits.size() + + abc.instance_info.get(tc.class_info).instance_traits.traits.size() + 1; + int initMethod = abc.class_info.get(tc.class_info).cinit_index; + writer.startTrait(traitInitId); + writer.startMethod(initMethod); + } + assignedValue.toString(writer, LocalData.create(abc.constants, new HashMap(), fullyQualifiedNames)); + if (parent instanceof TraitClass) { + writer.endMethod(); + writer.endTrait(); + } + return; + } + + if (value_kind != 0) { + ValueKind val = new ValueKind(value_index, value_kind); + writer.hilightSpecial(val.toString(abc.constants), "traitvalue"); + } + } + + public boolean isNamespace() { + if (value_kind != 0) { + ValueKind val = new ValueKind(value_index, value_kind); + return val.isNamespace(); + } + return false; + } + + @Override + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + String modifier = getModifiers(abcTags, abc, isStatic) + " "; + if (modifier.equals(" ")) { + modifier = ""; + } + Multiname n = getName(abc); + boolean showModifier = true; + if ((classIndex == -1) && (n != null)) { + Namespace ns = n.getNamespace(abc.constants); + if (ns == null) { + showModifier = false; + } else { + if ((ns.kind != Namespace.KIND_PACKAGE) && (ns.kind != Namespace.KIND_PACKAGE_INTERNAL)) { + showModifier = false; + } + } + } + if (!showModifier) { + modifier = ""; + } + writer.appendNoHilight(modifier); + getNameStr(writer, abc, fullyQualifiedNames); + if (assignedValue != null || value_kind != 0) { + writer.appendNoHilight(" = "); + getValueStr(parent, writer, abc, fullyQualifiedNames); + } + return writer.appendNoHilight(";").newLine(); + } + + @Override + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + getNameStr(writer, abc, fullyQualifiedNames); + if (assignedValue != null || value_kind != 0) { + getValueStr(parent, writer, abc, fullyQualifiedNames); + } + } + + public boolean isConst() { + return kindType == TRAIT_CONST; + } + + public boolean isVar() { + return kindType == TRAIT_SLOT; + } + + @Override + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) { + //do nothing + return 0; + } +} diff --git a/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java b/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java index 086938f83..cdca3c7d1 100644 --- a/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java +++ b/src/com/jpexs/decompiler/flash/abc/types/traits/Traits.java @@ -1,188 +1,187 @@ -/* - * Copyright (C) 2010-2014 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.abc.types.traits; - -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class Traits implements Serializable { - - public List traits = new ArrayList<>(); - - - public void delete(ABC abc,boolean d){ - for(Trait t:traits){ - t.delete(abc,d); - } - } - - public int addTrait(Trait t) { - traits.add(t); - return traits.size() - 1; - } - - public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { - int ret = 0; - for (Trait t : traits) { - ret += t.removeTraps(scriptIndex, classIndex, isStatic, abc, path); - } - return ret; - } - - @Override - public String toString() { - String s = ""; - for (int t = 0; t < traits.size(); t++) { - if (t > 0) { - s += "\r\n"; - } - s += traits.get(t).toString(); - } - return s; - } - - public String toString(ABC abc, List fullyQualifiedNames) { - String s = ""; - for (int t = 0; t < traits.size(); t++) { - if (t > 0) { - s += "\r\n"; - } - s += traits.get(t).toString(abc, fullyQualifiedNames); - } - return s; - } - - private class TraitConvertTask implements Callable { - - Trait trait; - boolean makePackages; - String path; - List abcTags; - ABC abc; - boolean isStatic; - ScriptExportMode exportMode; - int scriptIndex; - int classIndex; - NulWriter writer; - List fullyQualifiedNames; - int traitIndex; - boolean parallel; - Trait parent; - - public TraitConvertTask(Trait trait, Trait parent, boolean makePackages, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, int traitIndex, boolean parallel) { - this.trait = trait; - this.parent = parent; - this.makePackages = makePackages; - this.path = path; - this.abcTags = abcTags; - this.abc = abc; - this.isStatic = isStatic; - this.exportMode = exportMode; - this.scriptIndex = scriptIndex; - this.classIndex = classIndex; - this.writer = writer; - this.fullyQualifiedNames = fullyQualifiedNames; - this.traitIndex = traitIndex; - this.parallel = parallel; - } - - @Override - public Void call() throws InterruptedException { - if (makePackages) { - trait.convertPackaged(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } else { - trait.convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - return null; - } - } - - public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - for (int t = 0; t < traits.size(); t++) { - writer.newLine(); - Trait trait = traits.get(t); - int h = t; - if (classIndex != -1) { - if (!isStatic) { - h += abc.class_info.get(classIndex).static_traits.traits.size(); - } - } - if (trait instanceof TraitClass) { - writer.startClass(((TraitClass) trait).class_info); - } else { - writer.startTrait(h); - } - if (makePackages) { - trait.toStringPackaged(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } else { - trait.toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); - } - if (trait instanceof TraitClass) { - writer.endClass(); - } else { - writer.endTrait(); - } - } - return writer; - } - - public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { - if (!parallel || traits.size() < 2) { - for (int t = 0; t < traits.size(); t++) { - TraitConvertTask task = new TraitConvertTask(traits.get(t), parent, makePackages, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, t, parallel); - task.call(); - } - } else { - ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); - List> futureResults = null; - - futureResults = new ArrayList<>(); - for (int t = 0; t < traits.size(); t++) { - // each convert task needs a separate NulWriter, because they are executed parallel - TraitConvertTask task = new TraitConvertTask(traits.get(t), parent, makePackages, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, new NulWriter(), fullyQualifiedNames, t, parallel); - Future future = executor.submit(task); - futureResults.add(future); - } - - for (int f = 0; f < futureResults.size(); f++) { - try { - futureResults.get(f).get(); - } catch (InterruptedException ex) { - executor.shutdownNow(); - throw ex; - } catch (ExecutionException ex) { - Logger.getLogger(Traits.class.getName()).log(Level.SEVERE, "Error during traits converting", ex); - } - } - executor.shutdown(); - } - } -} +/* + * Copyright (C) 2010-2014 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.abc.types.traits; + +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Traits implements Serializable { + + public List traits = new ArrayList<>(); + + public void delete(ABC abc, boolean d) { + for (Trait t : traits) { + t.delete(abc, d); + } + } + + public int addTrait(Trait t) { + traits.add(t); + return traits.size() - 1; + } + + public int removeTraps(int scriptIndex, int classIndex, boolean isStatic, ABC abc, String path) throws InterruptedException { + int ret = 0; + for (Trait t : traits) { + ret += t.removeTraps(scriptIndex, classIndex, isStatic, abc, path); + } + return ret; + } + + @Override + public String toString() { + String s = ""; + for (int t = 0; t < traits.size(); t++) { + if (t > 0) { + s += "\r\n"; + } + s += traits.get(t).toString(); + } + return s; + } + + public String toString(ABC abc, List fullyQualifiedNames) { + String s = ""; + for (int t = 0; t < traits.size(); t++) { + if (t > 0) { + s += "\r\n"; + } + s += traits.get(t).toString(abc, fullyQualifiedNames); + } + return s; + } + + private class TraitConvertTask implements Callable { + + Trait trait; + boolean makePackages; + String path; + List abcTags; + ABC abc; + boolean isStatic; + ScriptExportMode exportMode; + int scriptIndex; + int classIndex; + NulWriter writer; + List fullyQualifiedNames; + int traitIndex; + boolean parallel; + Trait parent; + + public TraitConvertTask(Trait trait, Trait parent, boolean makePackages, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, int traitIndex, boolean parallel) { + this.trait = trait; + this.parent = parent; + this.makePackages = makePackages; + this.path = path; + this.abcTags = abcTags; + this.abc = abc; + this.isStatic = isStatic; + this.exportMode = exportMode; + this.scriptIndex = scriptIndex; + this.classIndex = classIndex; + this.writer = writer; + this.fullyQualifiedNames = fullyQualifiedNames; + this.traitIndex = traitIndex; + this.parallel = parallel; + } + + @Override + public Void call() throws InterruptedException { + if (makePackages) { + trait.convertPackaged(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } else { + trait.convert(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + return null; + } + } + + public GraphTextWriter toString(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, GraphTextWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + for (int t = 0; t < traits.size(); t++) { + writer.newLine(); + Trait trait = traits.get(t); + int h = t; + if (classIndex != -1) { + if (!isStatic) { + h += abc.class_info.get(classIndex).static_traits.traits.size(); + } + } + if (trait instanceof TraitClass) { + writer.startClass(((TraitClass) trait).class_info); + } else { + writer.startTrait(h); + } + if (makePackages) { + trait.toStringPackaged(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } else { + trait.toString(parent, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, parallel); + } + if (trait instanceof TraitClass) { + writer.endClass(); + } else { + writer.endTrait(); + } + } + return writer; + } + + public void convert(Trait parent, String path, List abcTags, ABC abc, boolean isStatic, ScriptExportMode exportMode, boolean makePackages, int scriptIndex, int classIndex, NulWriter writer, List fullyQualifiedNames, boolean parallel) throws InterruptedException { + if (!parallel || traits.size() < 2) { + for (int t = 0; t < traits.size(); t++) { + TraitConvertTask task = new TraitConvertTask(traits.get(t), parent, makePackages, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, writer, fullyQualifiedNames, t, parallel); + task.call(); + } + } else { + ExecutorService executor = Executors.newFixedThreadPool(Configuration.parallelThreadCount.get()); + List> futureResults = null; + + futureResults = new ArrayList<>(); + for (int t = 0; t < traits.size(); t++) { + // each convert task needs a separate NulWriter, because they are executed parallel + TraitConvertTask task = new TraitConvertTask(traits.get(t), parent, makePackages, path, abcTags, abc, isStatic, exportMode, scriptIndex, classIndex, new NulWriter(), fullyQualifiedNames, t, parallel); + Future future = executor.submit(task); + futureResults.add(future); + } + + for (int f = 0; f < futureResults.size(); f++) { + try { + futureResults.get(f).get(); + } catch (InterruptedException ex) { + executor.shutdownNow(); + throw ex; + } catch (ExecutionException ex) { + Logger.getLogger(Traits.class.getName()).log(Level.SEVERE, "Error during traits converting", ex); + } + } + executor.shutdown(); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java index 166736ffc..bea9842fb 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java +++ b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java @@ -1,239 +1,239 @@ -/* - * Copyright (C) 2010-2014 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.action; - -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.regex.Pattern; - -/** - * - * @author JPEXS - */ -public class ActionDeobfuscation { - - private final Random rnd = new Random(); - private final int DEFAULT_FOO_SIZE = 10; - public HashSet allVariableNamesStr = new HashSet<>(); - private final HashMap typeCounts = new HashMap<>(); - - public static final String VALID_FIRST_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; - public static final String VALID_NEXT_CHARACTERS = VALID_FIRST_CHARACTERS + "0123456789"; - public static final String FOO_CHARACTERS = "bcdfghjklmnpqrstvwz"; - public static final String FOO_JOIN_CHARACTERS = "aeiouy"; - - private String fooString(HashMap deobfuscated, String orig, boolean firstUppercase, int rndSize) { - boolean exists; - String ret; - loopfoo: - do { - exists = false; - int len = 3 + rnd.nextInt(rndSize - 3); - ret = ""; - for (int i = 0; i < len; i++) { - String c = ""; - if ((i % 2) == 0) { - c = "" + FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length())); - } else { - c = "" + FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length())); - } - if (i == 0 && firstUppercase) { - c = c.toUpperCase(Locale.ENGLISH); - } - ret += c; - } - if (allVariableNamesStr.contains(ret)) { - exists = true; - rndSize += 1; - continue loopfoo; - } - if (Action.isReservedWord(ret)) { - exists = true; - rndSize += 1; - continue; - } - if (deobfuscated.containsValue(ret)) { - exists = true; - rndSize += 1; - continue; - } - } while (exists); - return ret; - } - - public void deobfuscateInstanceNames(HashMap namesMap, RenameType renameType, List tags, Map selected) { - for (Tag t : tags) { - if (t instanceof DefineSpriteTag) { - deobfuscateInstanceNames(namesMap, renameType, ((DefineSpriteTag) t).subTags, selected); - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - String name = po.getInstanceName(); - if (name != null) { - String changedName = deobfuscateName(name, false, "instance", namesMap, renameType, selected); - if (changedName != null) { - po.setInstanceName(changedName); - ((Tag)po).setModified(true); - } - } - String className = po.getClassName(); - if (className != null) { - String changedClassName = deobfuscateNameWithPackage(className, namesMap, renameType, selected); - if (changedClassName != null) { - po.setClassName(changedClassName); - ((Tag)po).setModified(true); - } - } - } - } - } - - public String deobfuscatePackage(String pkg, HashMap namesMap, RenameType renameType, Map selected) { - if (namesMap.containsKey(pkg)) { - return namesMap.get(pkg); - } - String[] parts = null; - if (pkg.contains(".")) { - parts = pkg.split("\\."); - } else { - parts = new String[]{pkg}; - } - String ret = ""; - boolean isChanged = false; - for (int p = 0; p < parts.length; p++) { - if (p > 0) { - ret += "."; - } - String partChanged = deobfuscateName(parts[p], false, "package", namesMap, renameType, selected); - if (partChanged != null) { - ret += partChanged; - isChanged = true; - } else { - ret += parts[p]; - } - } - if (isChanged) { - namesMap.put(pkg, ret); - return ret; - } - return null; - } - - public String deobfuscateNameWithPackage(String n, HashMap namesMap, RenameType renameType, Map selected) { - String pkg = null; - String name = ""; - if (n.contains(".")) { - pkg = n.substring(0, n.lastIndexOf('.')); - name = n.substring(n.lastIndexOf('.') + 1); - } else { - name = n; - } - boolean changed = false; - if ((pkg != null) && (!pkg.isEmpty())) { - String changedPkg = deobfuscatePackage(pkg, namesMap, renameType, selected); - if (changedPkg != null) { - changed = true; - pkg = changedPkg; - } - } - String changedName = deobfuscateName(name, true, "class", namesMap, renameType, selected); - if (changedName != null) { - changed = true; - name = changedName; - } - if (changed) { - String newClassName = ""; - if (pkg == null) { - newClassName = name; - } else { - newClassName = pkg + "." + name; - } - return newClassName; - } - return null; - } - - public String deobfuscateName(String s, boolean firstUppercase, String usageType, HashMap namesMap, RenameType renameType, Map selected) { - boolean isValid = true; - if (usageType == null) { - usageType = "name"; - } - - if (selected != null) { - if (selected.containsKey(s)) { - return selected.get(s); - } - } - - if (Action.isReservedWord(s)) { - isValid = false; - } - - if (isValid) { - for (int i = 0; i < s.length(); i++) { - if (s.charAt(i) > 127) { - isValid = false; - break; - } - } - } - - if (isValid) { - Pattern pat = Pattern.compile("^[" + Pattern.quote(VALID_FIRST_CHARACTERS) + "]" + "[" + Pattern.quote(VALID_FIRST_CHARACTERS + VALID_NEXT_CHARACTERS) + "]*$"); - if (!pat.matcher(s).matches()) { - isValid = false; - } - } - if (!isValid) { - if (namesMap.containsKey(s)) { - return namesMap.get(s); - } else { - Integer cnt = typeCounts.get(usageType); - if (cnt == null) { - cnt = 0; - } - - String ret = null; - if (renameType == RenameType.TYPENUMBER) { - - boolean found; - do { - found = false; - cnt++; - ret = usageType + "_" + cnt; - found = allVariableNamesStr.contains(ret); - } while (found); - typeCounts.put(usageType, cnt); - } else if (renameType == RenameType.RANDOMWORD) { - ret = fooString(namesMap, s, firstUppercase, DEFAULT_FOO_SIZE); - } - namesMap.put(s, ret); - return ret; - } - } - return null; - } - -} +/* + * Copyright (C) 2010-2014 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.action; + +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.regex.Pattern; + +/** + * + * @author JPEXS + */ +public class ActionDeobfuscation { + + private final Random rnd = new Random(); + private final int DEFAULT_FOO_SIZE = 10; + public HashSet allVariableNamesStr = new HashSet<>(); + private final HashMap typeCounts = new HashMap<>(); + + public static final String VALID_FIRST_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; + public static final String VALID_NEXT_CHARACTERS = VALID_FIRST_CHARACTERS + "0123456789"; + public static final String FOO_CHARACTERS = "bcdfghjklmnpqrstvwz"; + public static final String FOO_JOIN_CHARACTERS = "aeiouy"; + + private String fooString(HashMap deobfuscated, String orig, boolean firstUppercase, int rndSize) { + boolean exists; + String ret; + loopfoo: + do { + exists = false; + int len = 3 + rnd.nextInt(rndSize - 3); + ret = ""; + for (int i = 0; i < len; i++) { + String c = ""; + if ((i % 2) == 0) { + c = "" + FOO_CHARACTERS.charAt(rnd.nextInt(FOO_CHARACTERS.length())); + } else { + c = "" + FOO_JOIN_CHARACTERS.charAt(rnd.nextInt(FOO_JOIN_CHARACTERS.length())); + } + if (i == 0 && firstUppercase) { + c = c.toUpperCase(Locale.ENGLISH); + } + ret += c; + } + if (allVariableNamesStr.contains(ret)) { + exists = true; + rndSize += 1; + continue loopfoo; + } + if (Action.isReservedWord(ret)) { + exists = true; + rndSize += 1; + continue; + } + if (deobfuscated.containsValue(ret)) { + exists = true; + rndSize += 1; + continue; + } + } while (exists); + return ret; + } + + public void deobfuscateInstanceNames(HashMap namesMap, RenameType renameType, List tags, Map selected) { + for (Tag t : tags) { + if (t instanceof DefineSpriteTag) { + deobfuscateInstanceNames(namesMap, renameType, ((DefineSpriteTag) t).subTags, selected); + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + String name = po.getInstanceName(); + if (name != null) { + String changedName = deobfuscateName(name, false, "instance", namesMap, renameType, selected); + if (changedName != null) { + po.setInstanceName(changedName); + ((Tag) po).setModified(true); + } + } + String className = po.getClassName(); + if (className != null) { + String changedClassName = deobfuscateNameWithPackage(className, namesMap, renameType, selected); + if (changedClassName != null) { + po.setClassName(changedClassName); + ((Tag) po).setModified(true); + } + } + } + } + } + + public String deobfuscatePackage(String pkg, HashMap namesMap, RenameType renameType, Map selected) { + if (namesMap.containsKey(pkg)) { + return namesMap.get(pkg); + } + String[] parts = null; + if (pkg.contains(".")) { + parts = pkg.split("\\."); + } else { + parts = new String[]{pkg}; + } + String ret = ""; + boolean isChanged = false; + for (int p = 0; p < parts.length; p++) { + if (p > 0) { + ret += "."; + } + String partChanged = deobfuscateName(parts[p], false, "package", namesMap, renameType, selected); + if (partChanged != null) { + ret += partChanged; + isChanged = true; + } else { + ret += parts[p]; + } + } + if (isChanged) { + namesMap.put(pkg, ret); + return ret; + } + return null; + } + + public String deobfuscateNameWithPackage(String n, HashMap namesMap, RenameType renameType, Map selected) { + String pkg = null; + String name = ""; + if (n.contains(".")) { + pkg = n.substring(0, n.lastIndexOf('.')); + name = n.substring(n.lastIndexOf('.') + 1); + } else { + name = n; + } + boolean changed = false; + if ((pkg != null) && (!pkg.isEmpty())) { + String changedPkg = deobfuscatePackage(pkg, namesMap, renameType, selected); + if (changedPkg != null) { + changed = true; + pkg = changedPkg; + } + } + String changedName = deobfuscateName(name, true, "class", namesMap, renameType, selected); + if (changedName != null) { + changed = true; + name = changedName; + } + if (changed) { + String newClassName = ""; + if (pkg == null) { + newClassName = name; + } else { + newClassName = pkg + "." + name; + } + return newClassName; + } + return null; + } + + public String deobfuscateName(String s, boolean firstUppercase, String usageType, HashMap namesMap, RenameType renameType, Map selected) { + boolean isValid = true; + if (usageType == null) { + usageType = "name"; + } + + if (selected != null) { + if (selected.containsKey(s)) { + return selected.get(s); + } + } + + if (Action.isReservedWord(s)) { + isValid = false; + } + + if (isValid) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 127) { + isValid = false; + break; + } + } + } + + if (isValid) { + Pattern pat = Pattern.compile("^[" + Pattern.quote(VALID_FIRST_CHARACTERS) + "]" + "[" + Pattern.quote(VALID_FIRST_CHARACTERS + VALID_NEXT_CHARACTERS) + "]*$"); + if (!pat.matcher(s).matches()) { + isValid = false; + } + } + if (!isValid) { + if (namesMap.containsKey(s)) { + return namesMap.get(s); + } else { + Integer cnt = typeCounts.get(usageType); + if (cnt == null) { + cnt = 0; + } + + String ret = null; + if (renameType == RenameType.TYPENUMBER) { + + boolean found; + do { + found = false; + cnt++; + ret = usageType + "_" + cnt; + found = allVariableNamesStr.contains(ret); + } while (found); + typeCounts.put(usageType, cnt); + } else if (renameType == RenameType.RANDOMWORD) { + ret = fooString(namesMap, s, firstUppercase, DEFAULT_FOO_SIZE); + } + namesMap.put(s, ret); + return ret; + } + } + return null; + } + +} diff --git a/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java b/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java index 025a97536..596565410 100644 --- a/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java @@ -1,257 +1,257 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.ecma.Undefined; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -public class DirectValueActionItem extends ActionItem { - - public Object value; - public List constants; - public GraphTargetItem computedRegValue; - - public DirectValueActionItem(Object o) { - this(null, 0, o, new ArrayList()); - } - - public DirectValueActionItem(GraphSourceItem instruction, int instructionPos, Object value, List constants) { - super(instruction, PRECEDENCE_PRIMARY); - this.constants = constants; - this.value = value; - this.pos = instructionPos; - } - - @Override - public boolean isVariableComputed() { - if (computedRegValue != null) { - return true; - } - return false; - } - - @Override - public Object getResult() { - if (computedRegValue != null) { - return computedRegValue.getResult(); - } - if (value instanceof Double) { - return (Double) value; - } - if (value instanceof Float) { - return (double) (Float) value; - } - if (value instanceof Long) { - return (double) (Long) value; - } - if (value instanceof Boolean) { - return value; - } - - if (value instanceof String) { - return value; - } - if (value instanceof ConstantIndex) { - return (this.constants.get(((ConstantIndex) value).index)); - } - if (value instanceof RegisterNumber) { - return new Undefined(); //has not computed value - } - return value; - } - - @Override - public String toStringNoQuotes(LocalData localData) { - if (value instanceof Double) { - if (Double.compare((double) (Double) value, 0) == 0) { - return "0"; - } - } - if (value instanceof Float) { - if (Float.compare((float) (Float) value, 0) == 0) { - return "0"; - } - } - if (value instanceof String) { - return (String) value; - } - if (value instanceof ConstantIndex) { - return this.constants.get(((ConstantIndex) value).index); - } - return value.toString(); - } - - @Override - public GraphTextWriter appendToNoQuotes(GraphTextWriter writer, LocalData localData) { - if (value instanceof Double) { - if (Double.compare((double) (Double) value, 0) == 0) { - return writer.append("0"); - } - } - if (value instanceof Float) { - if (Float.compare((float) (Float) value, 0) == 0) { - return writer.append("0"); - } - } - if (value instanceof String) { - return writer.append((String) value); - } - if (value instanceof ConstantIndex) { - return writer.append(this.constants.get(((ConstantIndex) value).index)); - } - return writer.append(value.toString()); - } - - public String toStringNoH(ConstantPool constants) { - if (value instanceof Double) { - if (Double.compare((double) (Double) value, 0) == 0) { - return ("0"); - } - } - if (value instanceof Float) { - if (Float.compare((float) (Float) value, 0) == 0) { - return ("0"); - } - } - if (value instanceof String) { - return (String) value; - } - if (value instanceof ConstantIndex) { - return (this.constants.get(((ConstantIndex) value).index)); - } - return value.toString(); - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { - if (value instanceof Double) { - if (Double.compare((double) (Double) value, 0) == 0) { - return writer.append("0"); - } - } - if (value instanceof Float) { - if (Float.compare((float) (Float) value, 0) == 0) { - return writer.append("0"); - } - } - if (value instanceof String) { - return writer.append("\"" + Helper.escapeString((String) value) + "\""); - } - if (value instanceof ConstantIndex) { - return writer.append("\"" + Helper.escapeString(this.constants.get(((ConstantIndex) value).index)) + "\""); - } - if (value instanceof RegisterNumber) { - return writer.append(((RegisterNumber) value).translate()); - } - return writer.append(value.toString()); - } - - @Override - public boolean isCompileTime(Set dependencies) { - if (computedRegValue != null) { - if (dependencies.contains(computedRegValue)) { - return false; - } - dependencies.add(computedRegValue); - } - return (value instanceof Double) || (value instanceof Float) || (value instanceof Boolean) || (value instanceof Long) || (value instanceof Null) || (computedRegValue != null && computedRegValue.isCompileTime(dependencies)) || (value instanceof String) || (value instanceof ConstantIndex); - } - - @Override - public int hashCode() { - int hash = 7; - hash = 71 * hash + Objects.hashCode(this.value); - hash = 71 * hash + System.identityHashCode(this.constants); - hash = 71 * hash + pos; - return hash; - } - - @Override - public boolean valueEquals(GraphTargetItem obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof DirectValueActionItem)) { - return false; - } - final DirectValueActionItem other = (DirectValueActionItem) obj; - if (!Objects.equals(this.value, other.value)) { - return false; - } - if (!Objects.equals(this.constants, other.constants)) { - return false; - } - return true; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DirectValueActionItem other = (DirectValueActionItem) obj; - if (!Objects.equals(this.value, other.value)) { - return false; - } - if (!Objects.equals(this.constants, other.constants)) { - return false; - } - if (other.pos != this.pos) { - return false; - } - return true; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, new ActionPush(value)); - } - - @Override - public boolean hasReturnValue() { - return true; - } - - public boolean isString(){ - return (value instanceof String)||(value instanceof ConstantIndex); - } - - public String getAsString(){ - if(!isString()){ - return null; - } - return (String)getResult(); - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class DirectValueActionItem extends ActionItem { + + public Object value; + public List constants; + public GraphTargetItem computedRegValue; + + public DirectValueActionItem(Object o) { + this(null, 0, o, new ArrayList()); + } + + public DirectValueActionItem(GraphSourceItem instruction, int instructionPos, Object value, List constants) { + super(instruction, PRECEDENCE_PRIMARY); + this.constants = constants; + this.value = value; + this.pos = instructionPos; + } + + @Override + public boolean isVariableComputed() { + if (computedRegValue != null) { + return true; + } + return false; + } + + @Override + public Object getResult() { + if (computedRegValue != null) { + return computedRegValue.getResult(); + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Float) { + return (double) (Float) value; + } + if (value instanceof Long) { + return (double) (Long) value; + } + if (value instanceof Boolean) { + return value; + } + + if (value instanceof String) { + return value; + } + if (value instanceof ConstantIndex) { + return (this.constants.get(((ConstantIndex) value).index)); + } + if (value instanceof RegisterNumber) { + return new Undefined(); //has not computed value + } + return value; + } + + @Override + public String toStringNoQuotes(LocalData localData) { + if (value instanceof Double) { + if (Double.compare((double) (Double) value, 0) == 0) { + return "0"; + } + } + if (value instanceof Float) { + if (Float.compare((float) (Float) value, 0) == 0) { + return "0"; + } + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof ConstantIndex) { + return this.constants.get(((ConstantIndex) value).index); + } + return value.toString(); + } + + @Override + public GraphTextWriter appendToNoQuotes(GraphTextWriter writer, LocalData localData) { + if (value instanceof Double) { + if (Double.compare((double) (Double) value, 0) == 0) { + return writer.append("0"); + } + } + if (value instanceof Float) { + if (Float.compare((float) (Float) value, 0) == 0) { + return writer.append("0"); + } + } + if (value instanceof String) { + return writer.append((String) value); + } + if (value instanceof ConstantIndex) { + return writer.append(this.constants.get(((ConstantIndex) value).index)); + } + return writer.append(value.toString()); + } + + public String toStringNoH(ConstantPool constants) { + if (value instanceof Double) { + if (Double.compare((double) (Double) value, 0) == 0) { + return ("0"); + } + } + if (value instanceof Float) { + if (Float.compare((float) (Float) value, 0) == 0) { + return ("0"); + } + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof ConstantIndex) { + return (this.constants.get(((ConstantIndex) value).index)); + } + return value.toString(); + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { + if (value instanceof Double) { + if (Double.compare((double) (Double) value, 0) == 0) { + return writer.append("0"); + } + } + if (value instanceof Float) { + if (Float.compare((float) (Float) value, 0) == 0) { + return writer.append("0"); + } + } + if (value instanceof String) { + return writer.append("\"" + Helper.escapeString((String) value) + "\""); + } + if (value instanceof ConstantIndex) { + return writer.append("\"" + Helper.escapeString(this.constants.get(((ConstantIndex) value).index)) + "\""); + } + if (value instanceof RegisterNumber) { + return writer.append(((RegisterNumber) value).translate()); + } + return writer.append(value.toString()); + } + + @Override + public boolean isCompileTime(Set dependencies) { + if (computedRegValue != null) { + if (dependencies.contains(computedRegValue)) { + return false; + } + dependencies.add(computedRegValue); + } + return (value instanceof Double) || (value instanceof Float) || (value instanceof Boolean) || (value instanceof Long) || (value instanceof Null) || (computedRegValue != null && computedRegValue.isCompileTime(dependencies)) || (value instanceof String) || (value instanceof ConstantIndex); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 71 * hash + Objects.hashCode(this.value); + hash = 71 * hash + System.identityHashCode(this.constants); + hash = 71 * hash + pos; + return hash; + } + + @Override + public boolean valueEquals(GraphTargetItem obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof DirectValueActionItem)) { + return false; + } + final DirectValueActionItem other = (DirectValueActionItem) obj; + if (!Objects.equals(this.value, other.value)) { + return false; + } + if (!Objects.equals(this.constants, other.constants)) { + return false; + } + return true; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final DirectValueActionItem other = (DirectValueActionItem) obj; + if (!Objects.equals(this.value, other.value)) { + return false; + } + if (!Objects.equals(this.constants, other.constants)) { + return false; + } + if (other.pos != this.pos) { + return false; + } + return true; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, new ActionPush(value)); + } + + @Override + public boolean hasReturnValue() { + return true; + } + + public boolean isString() { + return (value instanceof String) || (value instanceof ConstantIndex); + } + + public String getAsString() { + if (!isString()) { + return null; + } + return (String) getResult(); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/FSCommandActionItem.java b/src/com/jpexs/decompiler/flash/action/model/FSCommandActionItem.java index 31a14ffc4..3bab33786 100644 --- a/src/com/jpexs/decompiler/flash/action/model/FSCommandActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/FSCommandActionItem.java @@ -1,75 +1,74 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.Helper; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class FSCommandActionItem extends ActionItem { - - private final GraphTargetItem command; - - public FSCommandActionItem(GraphSourceItem instruction, GraphTargetItem command) { - super(instruction, PRECEDENCE_PRIMARY); - this.command = command; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { - writer.append("fscommand"); - writer.spaceBeforeCallParenthesies(1); - writer.append("("); - try { - command.appendTo(writer, localData); - } catch (InterruptedException ex) { - Logger.getLogger(FSCommandActionItem.class.getName()).log(Level.SEVERE, null, ex); - } - return writer.append(")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asg=(ActionSourceGenerator)generator; - if((command instanceof DirectValueActionItem)&&((DirectValueActionItem)command).isString()){ - return toSourceMerge(localData, generator, new ActionGetURL("FSCommand:" + ((DirectValueActionItem)command).getAsString(), "")); - } - return toSourceMerge(localData, generator, new AddActionItem(null, asg.pushConstTargetItem("FSCommand:"), command, true), asg.pushConstTargetItem(""), new ActionGetURL2(1/*GET*/, false, false)); - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class FSCommandActionItem extends ActionItem { + + private final GraphTargetItem command; + + public FSCommandActionItem(GraphSourceItem instruction, GraphTargetItem command) { + super(instruction, PRECEDENCE_PRIMARY); + this.command = command; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { + writer.append("fscommand"); + writer.spaceBeforeCallParenthesies(1); + writer.append("("); + try { + command.appendTo(writer, localData); + } catch (InterruptedException ex) { + Logger.getLogger(FSCommandActionItem.class.getName()).log(Level.SEVERE, null, ex); + } + return writer.append(")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asg = (ActionSourceGenerator) generator; + if ((command instanceof DirectValueActionItem) && ((DirectValueActionItem) command).isString()) { + return toSourceMerge(localData, generator, new ActionGetURL("FSCommand:" + ((DirectValueActionItem) command).getAsString(), "")); + } + return toSourceMerge(localData, generator, new AddActionItem(null, asg.pushConstTargetItem("FSCommand:"), command, true), asg.pushConstTargetItem(""), new ActionGetURL2(1/*GET*/, false, false)); + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/GotoFrame2ActionItem.java b/src/com/jpexs/decompiler/flash/action/model/GotoFrame2ActionItem.java index 5891fe28c..1c19f2fa1 100644 --- a/src/com/jpexs/decompiler/flash/action/model/GotoFrame2ActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/GotoFrame2ActionItem.java @@ -1,89 +1,89 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionPlay; -import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemPos; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -public class GotoFrame2ActionItem extends ActionItem { - - public GraphTargetItem frame; - public boolean sceneBiasFlag; - public boolean playFlag; - public int sceneBias; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(frame); - return ret; - } - - public GotoFrame2ActionItem(GraphSourceItem instruction, GraphTargetItem frame, boolean sceneBiasFlag, boolean playFlag, int sceneBias) { - super(instruction, PRECEDENCE_PRIMARY); - this.frame = frame; - this.sceneBiasFlag = sceneBiasFlag; - this.playFlag = playFlag; - this.sceneBias = sceneBias; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - String prefix = "gotoAndStop"; - if (playFlag) { - prefix = "gotoAndPlay"; - } - writer.append(prefix); - writer.spaceBeforeCallParenthesies(1); - writer.append("("); - frame.toString(writer, localData); - writer.append((sceneBiasFlag ? "," + sceneBias : "")); - return writer.append(")"); - } - - @Override - public List getNeededSources() { - List ret = super.getNeededSources(); - ret.addAll(frame.getNeededSources()); - return ret; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - if(!sceneBiasFlag && (frame instanceof DirectValueActionItem) && (((DirectValueActionItem)frame).value instanceof Long)){ - return toSourceMerge(localData, generator, frame, new ActionGotoFrame((int)((long)(Long)((DirectValueActionItem)frame).value)-1),playFlag?new ActionPlay():null); - }else{ - return toSourceMerge(localData, generator, frame, new ActionGotoFrame2(playFlag, sceneBiasFlag, sceneBias)); - } - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame; +import com.jpexs.decompiler.flash.action.swf3.ActionPlay; +import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemPos; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +public class GotoFrame2ActionItem extends ActionItem { + + public GraphTargetItem frame; + public boolean sceneBiasFlag; + public boolean playFlag; + public int sceneBias; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(frame); + return ret; + } + + public GotoFrame2ActionItem(GraphSourceItem instruction, GraphTargetItem frame, boolean sceneBiasFlag, boolean playFlag, int sceneBias) { + super(instruction, PRECEDENCE_PRIMARY); + this.frame = frame; + this.sceneBiasFlag = sceneBiasFlag; + this.playFlag = playFlag; + this.sceneBias = sceneBias; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + String prefix = "gotoAndStop"; + if (playFlag) { + prefix = "gotoAndPlay"; + } + writer.append(prefix); + writer.spaceBeforeCallParenthesies(1); + writer.append("("); + frame.toString(writer, localData); + writer.append((sceneBiasFlag ? "," + sceneBias : "")); + return writer.append(")"); + } + + @Override + public List getNeededSources() { + List ret = super.getNeededSources(); + ret.addAll(frame.getNeededSources()); + return ret; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + if (!sceneBiasFlag && (frame instanceof DirectValueActionItem) && (((DirectValueActionItem) frame).value instanceof Long)) { + return toSourceMerge(localData, generator, frame, new ActionGotoFrame((int) ((long) (Long) ((DirectValueActionItem) frame).value) - 1), playFlag ? new ActionPlay() : null); + } else { + return toSourceMerge(localData, generator, frame, new ActionGotoFrame2(playFlag, sceneBiasFlag, sceneBias)); + } + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/LoadMovieNumActionItem.java b/src/com/jpexs/decompiler/flash/action/model/LoadMovieNumActionItem.java index e1e5c1bb0..4844a1b4a 100644 --- a/src/com/jpexs/decompiler/flash/action/model/LoadMovieNumActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/LoadMovieNumActionItem.java @@ -1,91 +1,91 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class LoadMovieNumActionItem extends ActionItem { - - private final GraphTargetItem urlString; - private final GraphTargetItem num; - private final int method; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(urlString); - ret.add(num); - return ret; - } - - public LoadMovieNumActionItem(GraphSourceItem instruction, GraphTargetItem urlString, GraphTargetItem num, int method) { - super(instruction, PRECEDENCE_PRIMARY); - this.urlString = urlString; - this.num = num; - this.method = method; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - String methodStr = ""; - if (method == 1) { - methodStr = ",\"GET\""; - } - if (method == 2) { - methodStr = ",\"POST\""; - } - writer.append("loadMovieNum"); - writer.spaceBeforeCallParenthesies(2); - writer.append("("); - urlString.toString(writer, localData); - writer.append(","); - num.toString(writer, localData); - return writer.append(methodStr + ")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; - Object lev = null; - if((num instanceof DirectValueActionItem)&&(((DirectValueActionItem)num).value instanceof Long)){ - lev = asGenerator.pushConstTargetItem("_level"+((DirectValueActionItem)num).value); - }else{ - lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); - } - return toSourceMerge(localData, generator, urlString, lev, new ActionGetURL2(method, false, false)); - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class LoadMovieNumActionItem extends ActionItem { + + private final GraphTargetItem urlString; + private final GraphTargetItem num; + private final int method; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(urlString); + ret.add(num); + return ret; + } + + public LoadMovieNumActionItem(GraphSourceItem instruction, GraphTargetItem urlString, GraphTargetItem num, int method) { + super(instruction, PRECEDENCE_PRIMARY); + this.urlString = urlString; + this.num = num; + this.method = method; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + String methodStr = ""; + if (method == 1) { + methodStr = ",\"GET\""; + } + if (method == 2) { + methodStr = ",\"POST\""; + } + writer.append("loadMovieNum"); + writer.spaceBeforeCallParenthesies(2); + writer.append("("); + urlString.toString(writer, localData); + writer.append(","); + num.toString(writer, localData); + return writer.append(methodStr + ")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; + Object lev = null; + if ((num instanceof DirectValueActionItem) && (((DirectValueActionItem) num).value instanceof Long)) { + lev = asGenerator.pushConstTargetItem("_level" + ((DirectValueActionItem) num).value); + } else { + lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); + } + return toSourceMerge(localData, generator, urlString, lev, new ActionGetURL2(method, false, false)); + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/LoadVariablesNumActionItem.java b/src/com/jpexs/decompiler/flash/action/model/LoadVariablesNumActionItem.java index c5d95afc1..4d4db99bc 100644 --- a/src/com/jpexs/decompiler/flash/action/model/LoadVariablesNumActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/LoadVariablesNumActionItem.java @@ -1,91 +1,91 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class LoadVariablesNumActionItem extends ActionItem { - - private final GraphTargetItem urlString; - private final GraphTargetItem num; - private final int method; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(urlString); - ret.add(num); - return ret; - } - - public LoadVariablesNumActionItem(GraphSourceItem instruction, GraphTargetItem urlString, GraphTargetItem num, int method) { - super(instruction, PRECEDENCE_PRIMARY); - this.urlString = urlString; - this.num = num; - this.method = method; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - String methodStr = ""; - if (method == 1) { - methodStr = ",\"GET\""; - } - if (method == 2) { - methodStr = ",\"POST\""; - } - writer.append("loadVariablesNum"); - writer.spaceBeforeCallParenthesies(2); - writer.append("("); - urlString.toString(writer, localData); - writer.append(","); - num.toString(writer, localData); - return writer.append(methodStr + ")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; - Object lev = null; - if((num instanceof DirectValueActionItem)&&(((DirectValueActionItem)num).value instanceof Long)){ - lev = asGenerator.pushConstTargetItem("_level"+((DirectValueActionItem)num).value); - }else{ - lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); - } - return toSourceMerge(localData, generator, urlString, lev, new ActionGetURL2(method, true, false)); - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class LoadVariablesNumActionItem extends ActionItem { + + private final GraphTargetItem urlString; + private final GraphTargetItem num; + private final int method; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(urlString); + ret.add(num); + return ret; + } + + public LoadVariablesNumActionItem(GraphSourceItem instruction, GraphTargetItem urlString, GraphTargetItem num, int method) { + super(instruction, PRECEDENCE_PRIMARY); + this.urlString = urlString; + this.num = num; + this.method = method; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + String methodStr = ""; + if (method == 1) { + methodStr = ",\"GET\""; + } + if (method == 2) { + methodStr = ",\"POST\""; + } + writer.append("loadVariablesNum"); + writer.spaceBeforeCallParenthesies(2); + writer.append("("); + urlString.toString(writer, localData); + writer.append(","); + num.toString(writer, localData); + return writer.append(methodStr + ")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; + Object lev = null; + if ((num instanceof DirectValueActionItem) && (((DirectValueActionItem) num).value instanceof Long)) { + lev = asGenerator.pushConstTargetItem("_level" + ((DirectValueActionItem) num).value); + } else { + lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); + } + return toSourceMerge(localData, generator, urlString, lev, new ActionGetURL2(method, true, false)); + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/PrintAsBitmapNumActionItem.java b/src/com/jpexs/decompiler/flash/action/model/PrintAsBitmapNumActionItem.java index ee0064f64..7fef1bbf4 100644 --- a/src/com/jpexs/decompiler/flash/action/model/PrintAsBitmapNumActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/PrintAsBitmapNumActionItem.java @@ -1,82 +1,82 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class PrintAsBitmapNumActionItem extends ActionItem { - - private final GraphTargetItem num; - private final GraphTargetItem boundingBox; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(num); - ret.add(boundingBox); - return ret; - } - - public PrintAsBitmapNumActionItem(GraphSourceItem instruction, GraphTargetItem num, GraphTargetItem boundingBox) { - super(instruction, PRECEDENCE_PRIMARY); - this.num = num; - this.boundingBox = boundingBox; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("printAsBitmapNum"); - writer.spaceBeforeCallParenthesies(2); - writer.append("("); - num.toString(writer, localData); - writer.append(","); - boundingBox.toString(writer, localData); - return writer.append(")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; - Object lev = null; - if((num instanceof DirectValueActionItem)&&(((DirectValueActionItem)num).value instanceof Long)){ - lev = asGenerator.pushConstTargetItem("_level"+((DirectValueActionItem)num).value); - }else{ - lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); - } - return toSourceMerge(localData, generator, new AddActionItem(src, asGenerator.pushConstTargetItem("printasbitmap:#"), boundingBox, true), lev, new ActionGetURL2(0, false, false)); - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class PrintAsBitmapNumActionItem extends ActionItem { + + private final GraphTargetItem num; + private final GraphTargetItem boundingBox; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(num); + ret.add(boundingBox); + return ret; + } + + public PrintAsBitmapNumActionItem(GraphSourceItem instruction, GraphTargetItem num, GraphTargetItem boundingBox) { + super(instruction, PRECEDENCE_PRIMARY); + this.num = num; + this.boundingBox = boundingBox; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("printAsBitmapNum"); + writer.spaceBeforeCallParenthesies(2); + writer.append("("); + num.toString(writer, localData); + writer.append(","); + boundingBox.toString(writer, localData); + return writer.append(")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; + Object lev = null; + if ((num instanceof DirectValueActionItem) && (((DirectValueActionItem) num).value instanceof Long)) { + lev = asGenerator.pushConstTargetItem("_level" + ((DirectValueActionItem) num).value); + } else { + lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); + } + return toSourceMerge(localData, generator, new AddActionItem(src, asGenerator.pushConstTargetItem("printasbitmap:#"), boundingBox, true), lev, new ActionGetURL2(0, false, false)); + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/PrintNumActionItem.java b/src/com/jpexs/decompiler/flash/action/model/PrintNumActionItem.java index 4ef022070..b19ce7fed 100644 --- a/src/com/jpexs/decompiler/flash/action/model/PrintNumActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/PrintNumActionItem.java @@ -1,82 +1,82 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class PrintNumActionItem extends ActionItem { - - private final GraphTargetItem num; - private final GraphTargetItem boundingBox; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(num); - ret.add(boundingBox); - return ret; - } - - public PrintNumActionItem(GraphSourceItem instruction, GraphTargetItem num, GraphTargetItem boundingBox) { - super(instruction, PRECEDENCE_PRIMARY); - this.num = num; - this.boundingBox = boundingBox; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("printNum"); - writer.spaceBeforeCallParenthesies(2); - writer.append("("); - num.toString(writer, localData); - writer.append(","); - boundingBox.toString(writer, localData); - return writer.append(")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; - Object lev = null; - if((num instanceof DirectValueActionItem)&&(((DirectValueActionItem)num).value instanceof Long)){ - lev = asGenerator.pushConstTargetItem("_level"+((DirectValueActionItem)num).value); - }else{ - lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); - } - return toSourceMerge(localData, generator, new AddActionItem(src, asGenerator.pushConstTargetItem("print:#"), boundingBox, true), lev, new ActionGetURL2(0, false, false)); - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class PrintNumActionItem extends ActionItem { + + private final GraphTargetItem num; + private final GraphTargetItem boundingBox; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(num); + ret.add(boundingBox); + return ret; + } + + public PrintNumActionItem(GraphSourceItem instruction, GraphTargetItem num, GraphTargetItem boundingBox) { + super(instruction, PRECEDENCE_PRIMARY); + this.num = num; + this.boundingBox = boundingBox; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("printNum"); + writer.spaceBeforeCallParenthesies(2); + writer.append("("); + num.toString(writer, localData); + writer.append(","); + boundingBox.toString(writer, localData); + return writer.append(")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; + Object lev = null; + if ((num instanceof DirectValueActionItem) && (((DirectValueActionItem) num).value instanceof Long)) { + lev = asGenerator.pushConstTargetItem("_level" + ((DirectValueActionItem) num).value); + } else { + lev = new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true); + } + return toSourceMerge(localData, generator, new AddActionItem(src, asGenerator.pushConstTargetItem("print:#"), boundingBox, true), lev, new ActionGetURL2(0, false, false)); + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/UnLoadMovieNumActionItem.java b/src/com/jpexs/decompiler/flash/action/model/UnLoadMovieNumActionItem.java index 8c43a871a..8c4b76321 100644 --- a/src/com/jpexs/decompiler/flash/action/model/UnLoadMovieNumActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/UnLoadMovieNumActionItem.java @@ -1,78 +1,78 @@ -/* - * Copyright (C) 2010-2014 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.action.model; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class UnLoadMovieNumActionItem extends ActionItem { - - private final GraphTargetItem num; - - @Override - public List getAllSubItems() { - List ret = new ArrayList<>(); - ret.add(num); - return ret; - } - - public UnLoadMovieNumActionItem(GraphSourceItem instruction, GraphTargetItem num) { - super(instruction, PRECEDENCE_PRIMARY); - this.num = num; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("unloadMovieNum"); - writer.spaceBeforeCallParenthesies(1); - writer.append("("); - num.toString(writer, localData); - return writer.append(")"); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; - if((num instanceof DirectValueActionItem)&&(((DirectValueActionItem)num).value instanceof Long)){ - return toSourceMerge(localData, generator, new ActionGetURL("","_level"+((DirectValueActionItem)num).value )); - }else{ - return toSourceMerge(localData, generator, new ActionPush(""), new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true), new ActionGetURL2(0, false, true)); - } - - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; +import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class UnLoadMovieNumActionItem extends ActionItem { + + private final GraphTargetItem num; + + @Override + public List getAllSubItems() { + List ret = new ArrayList<>(); + ret.add(num); + return ret; + } + + public UnLoadMovieNumActionItem(GraphSourceItem instruction, GraphTargetItem num) { + super(instruction, PRECEDENCE_PRIMARY); + this.num = num; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("unloadMovieNum"); + writer.spaceBeforeCallParenthesies(1); + writer.append("("); + num.toString(writer, localData); + return writer.append(")"); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator asGenerator = (ActionSourceGenerator) generator; + if ((num instanceof DirectValueActionItem) && (((DirectValueActionItem) num).value instanceof Long)) { + return toSourceMerge(localData, generator, new ActionGetURL("", "_level" + ((DirectValueActionItem) num).value)); + } else { + return toSourceMerge(localData, generator, new ActionPush(""), new AddActionItem(src, asGenerator.pushConstTargetItem("_level"), num, true), new ActionGetURL2(0, false, true)); + } + + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/clauses/TellTargetActionItem.java b/src/com/jpexs/decompiler/flash/action/model/clauses/TellTargetActionItem.java index e2d4c2be6..cc0caa59c 100644 --- a/src/com/jpexs/decompiler/flash/action/model/clauses/TellTargetActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/clauses/TellTargetActionItem.java @@ -1,87 +1,87 @@ -/* - * Copyright (C) 2010-2014 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.action.model.clauses; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.ActionItem; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget; -import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemPos; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.LocalData; -import java.util.ArrayList; -import java.util.List; - -public class TellTargetActionItem extends ActionItem { - - public List commands; - public GraphTargetItem target; - - public TellTargetActionItem(GraphSourceItem instruction, GraphTargetItem target, List commands) { - super(instruction, PRECEDENCE_PRIMARY); - this.target = target; - this.commands = commands; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - writer.append("tellTarget"); - writer.spaceBeforeCallParenthesies(1); - writer.append("("); - target.toString(writer, localData); - writer.append(")").newLine(); - writer.append("{").newLine(); - writer.indent(); - for (GraphTargetItem ti : commands) { - ti.toString(writer, localData).newLine(); - } - writer.unindent(); - return writer.append("}"); - } - - @Override - public List getNeededSources() { - List ret = super.getNeededSources(); - ret.addAll(target.getNeededSources()); - return ret; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - List ret = new ArrayList<>(); - if((target instanceof DirectValueActionItem)&&((((DirectValueActionItem)target).value instanceof String) || (((DirectValueActionItem)target).value instanceof ConstantIndex))){ - ret.add(new ActionSetTarget((String)target.getResult())); - }else{ - ret.addAll(target.toSource(localData, generator)); - ret.add(new ActionSetTarget2()); - } - ret.addAll(generator.generate(localData, commands)); - ret.add(new ActionSetTarget("")); - return ret; - } - - @Override - public boolean hasReturnValue() { - return false; - } -} +/* + * Copyright (C) 2010-2014 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.action.model.clauses; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.ActionItem; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget; +import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemPos; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.LocalData; +import java.util.ArrayList; +import java.util.List; + +public class TellTargetActionItem extends ActionItem { + + public List commands; + public GraphTargetItem target; + + public TellTargetActionItem(GraphSourceItem instruction, GraphTargetItem target, List commands) { + super(instruction, PRECEDENCE_PRIMARY); + this.target = target; + this.commands = commands; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + writer.append("tellTarget"); + writer.spaceBeforeCallParenthesies(1); + writer.append("("); + target.toString(writer, localData); + writer.append(")").newLine(); + writer.append("{").newLine(); + writer.indent(); + for (GraphTargetItem ti : commands) { + ti.toString(writer, localData).newLine(); + } + writer.unindent(); + return writer.append("}"); + } + + @Override + public List getNeededSources() { + List ret = super.getNeededSources(); + ret.addAll(target.getNeededSources()); + return ret; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + List ret = new ArrayList<>(); + if ((target instanceof DirectValueActionItem) && ((((DirectValueActionItem) target).value instanceof String) || (((DirectValueActionItem) target).value instanceof ConstantIndex))) { + ret.add(new ActionSetTarget((String) target.getResult())); + } else { + ret.addAll(target.toSource(localData, generator)); + ret.add(new ActionSetTarget2()); + } + ret.addAll(generator.generate(localData, commands)); + ret.add(new ActionSetTarget("")); + return ret; + } + + @Override + public boolean hasReturnValue() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/BitXorActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/BitXorActionItem.java index 783b2c1bf..bb2bf01e9 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/BitXorActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/BitXorActionItem.java @@ -1,72 +1,70 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import java.util.List; - -public class BitXorActionItem extends BinaryOpItem { - - public BitXorActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_BITWISEXOR, leftSide, rightSide, "^"); - } - - @Override - public Object getResult() { - return ((long) (double) EcmaScript.toNumber(leftSide.getResult())) ^ ((long) (double) EcmaScript.toNumber(rightSide.getResult())); - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - if((rightSide instanceof DirectValueActionItem)&&(((DirectValueActionItem)rightSide).value.equals(4.294967295E9))){ - writer.append("~"); - if(leftSide.getPrecedence()>PRECEDENCE_UNARY){ - writer.append("("); - } - leftSide.appendTo(writer, localData); - if(leftSide.getPrecedence()>PRECEDENCE_UNARY){ - writer.append(")"); - } - return writer; - }else{ - return super.appendTo(writer, localData); - } - } - - - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionBitXor()); - } - - @Override - public GraphTargetItem returnType() { - return new UnboundedTypeItem(); - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.List; + +public class BitXorActionItem extends BinaryOpItem { + + public BitXorActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_BITWISEXOR, leftSide, rightSide, "^"); + } + + @Override + public Object getResult() { + return ((long) (double) EcmaScript.toNumber(leftSide.getResult())) ^ ((long) (double) EcmaScript.toNumber(rightSide.getResult())); + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + if ((rightSide instanceof DirectValueActionItem) && (((DirectValueActionItem) rightSide).value.equals(4.294967295E9))) { + writer.append("~"); + if (leftSide.getPrecedence() > PRECEDENCE_UNARY) { + writer.append("("); + } + leftSide.appendTo(writer, localData); + if (leftSide.getPrecedence() > PRECEDENCE_UNARY) { + writer.append(")"); + } + return writer; + } else { + return super.appendTo(writer, localData); + } + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionBitXor()); + } + + @Override + public GraphTargetItem returnType() { + return new UnboundedTypeItem(); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/GeActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/GeActionItem.java index 16a401457..df74299c0 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/GeActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/GeActionItem.java @@ -1,76 +1,76 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import com.jpexs.decompiler.graph.model.LogicalOpItem; -import java.util.List; - -public class GeActionItem extends BinaryOpItem implements LogicalOpItem, Inverted { - - boolean version2; - - public GeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide, boolean version2) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, ">="); - this.version2 = version2; - } - - @Override - public Object getResult() { - if (version2) { - Object ret = EcmaScript.compare(leftSide.getResult(), rightSide.getResult()); - if (ret == Boolean.TRUE) { - return Boolean.FALSE; - } - if (ret == Boolean.FALSE) { - return Boolean.TRUE; - } - return ret;//undefined - } else { - //For SWF 4 and older, it should return 1 or 0 - return Action.toFloatPoint(leftSide.getResult()) >= Action.toFloatPoint(rightSide.getResult()); - } - } - - @Override - public GraphTargetItem invert() { - return new LtActionItem(src, leftSide, rightSide, version2); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator g = (ActionSourceGenerator)generator; - return toSourceMerge(localData, generator, leftSide, rightSide, g.getSwfVersion()>=5?new ActionLess2():new ActionLess(), new ActionNot()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.LogicalOpItem; +import java.util.List; + +public class GeActionItem extends BinaryOpItem implements LogicalOpItem, Inverted { + + boolean version2; + + public GeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide, boolean version2) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, ">="); + this.version2 = version2; + } + + @Override + public Object getResult() { + if (version2) { + Object ret = EcmaScript.compare(leftSide.getResult(), rightSide.getResult()); + if (ret == Boolean.TRUE) { + return Boolean.FALSE; + } + if (ret == Boolean.FALSE) { + return Boolean.TRUE; + } + return ret;//undefined + } else { + //For SWF 4 and older, it should return 1 or 0 + return Action.toFloatPoint(leftSide.getResult()) >= Action.toFloatPoint(rightSide.getResult()); + } + } + + @Override + public GraphTargetItem invert() { + return new LtActionItem(src, leftSide, rightSide, version2); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator g = (ActionSourceGenerator) generator; + return toSourceMerge(localData, generator, leftSide, rightSide, g.getSwfVersion() >= 5 ? new ActionLess2() : new ActionLess(), new ActionNot()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/GtActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/GtActionItem.java index a9e9ff87d..6bd8ee069 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/GtActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/GtActionItem.java @@ -1,63 +1,63 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.action.swf6.ActionGreater; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import com.jpexs.decompiler.graph.model.LogicalOpItem; -import java.util.List; - -public class GtActionItem extends BinaryOpItem implements LogicalOpItem { - - public GtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, ">"); - } - - @Override - public Object getResult() { - return EcmaScript.compare(rightSide.getResult(), leftSide.getResult()); - } - - @Override - public GraphTargetItem invert() { - return new LeActionItem(src, leftSide, rightSide); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator g = (ActionSourceGenerator)generator; - if(g.getSwfVersion()>=6){ - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionGreater()); - } - return toSourceMerge(localData, generator, rightSide, leftSide, g.getSwfVersion()>=5?new ActionLess2():new ActionLess()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.LogicalOpItem; +import java.util.List; + +public class GtActionItem extends BinaryOpItem implements LogicalOpItem { + + public GtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, ">"); + } + + @Override + public Object getResult() { + return EcmaScript.compare(rightSide.getResult(), leftSide.getResult()); + } + + @Override + public GraphTargetItem invert() { + return new LeActionItem(src, leftSide, rightSide); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator g = (ActionSourceGenerator) generator; + if (g.getSwfVersion() >= 6) { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionGreater()); + } + return toSourceMerge(localData, generator, rightSide, leftSide, g.getSwfVersion() >= 5 ? new ActionLess2() : new ActionLess()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/LeActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/LeActionItem.java index 3f3ba9418..695bdf09f 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/LeActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/LeActionItem.java @@ -1,72 +1,72 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.action.swf6.ActionGreater; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import com.jpexs.decompiler.graph.model.LogicalOpItem; -import java.util.List; - -public class LeActionItem extends BinaryOpItem implements LogicalOpItem, Inverted { - - public LeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "<="); - } - - @Override - public Object getResult() { - Object ret = EcmaScript.compare(rightSide.getResult(), leftSide.getResult()); - if (ret == Boolean.TRUE) { - return Boolean.FALSE; - } - if (ret == Boolean.FALSE) { - return Boolean.TRUE; - } - return ret;//undefined - } - - @Override - public GraphTargetItem invert() { - return new GtActionItem(src, leftSide, rightSide); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator g = (ActionSourceGenerator)generator; - if(g.getSwfVersion()>=6){ - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionGreater(), new ActionNot()); - } - return toSourceMerge(localData, generator, rightSide, leftSide, g.getSwfVersion()>=5?new ActionLess2():new ActionLess(), new ActionNot()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.action.swf6.ActionGreater; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.LogicalOpItem; +import java.util.List; + +public class LeActionItem extends BinaryOpItem implements LogicalOpItem, Inverted { + + public LeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "<="); + } + + @Override + public Object getResult() { + Object ret = EcmaScript.compare(rightSide.getResult(), leftSide.getResult()); + if (ret == Boolean.TRUE) { + return Boolean.FALSE; + } + if (ret == Boolean.FALSE) { + return Boolean.TRUE; + } + return ret;//undefined + } + + @Override + public GraphTargetItem invert() { + return new GtActionItem(src, leftSide, rightSide); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator g = (ActionSourceGenerator) generator; + if (g.getSwfVersion() >= 6) { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionGreater(), new ActionNot()); + } + return toSourceMerge(localData, generator, rightSide, leftSide, g.getSwfVersion() >= 5 ? new ActionLess2() : new ActionLess(), new ActionNot()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/LtActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/LtActionItem.java index 8f3e0e54d..ee6723755 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/LtActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/LtActionItem.java @@ -1,68 +1,68 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.ecma.EcmaScript; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import com.jpexs.decompiler.graph.model.LogicalOpItem; -import java.util.List; - -public class LtActionItem extends BinaryOpItem implements LogicalOpItem { - - boolean version2; - - public LtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide, boolean version2) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "<"); - this.version2 = version2; - } - - @Override - public Object getResult() { - if (version2) { - return EcmaScript.compare(leftSide.getResult(), rightSide.getResult()); - } else { - //For SWF 4 and older, it should return 1 or 0 - return Action.toFloatPoint(leftSide.getResult()) < Action.toFloatPoint(rightSide.getResult()); - } - } - - @Override - public GraphTargetItem invert() { - return new GeActionItem(src, leftSide, rightSide, version2); - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator g = (ActionSourceGenerator)generator; - return toSourceMerge(localData, generator, leftSide, rightSide, g.getSwfVersion()>=5?new ActionLess2():new ActionLess()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionLess; +import com.jpexs.decompiler.flash.action.swf5.ActionLess2; +import com.jpexs.decompiler.flash.ecma.EcmaScript; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import com.jpexs.decompiler.graph.model.LogicalOpItem; +import java.util.List; + +public class LtActionItem extends BinaryOpItem implements LogicalOpItem { + + boolean version2; + + public LtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide, boolean version2) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "<"); + this.version2 = version2; + } + + @Override + public Object getResult() { + if (version2) { + return EcmaScript.compare(leftSide.getResult(), rightSide.getResult()); + } else { + //For SWF 4 and older, it should return 1 or 0 + return Action.toFloatPoint(leftSide.getResult()) < Action.toFloatPoint(rightSide.getResult()); + } + } + + @Override + public GraphTargetItem invert() { + return new GeActionItem(src, leftSide, rightSide, version2); + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator g = (ActionSourceGenerator) generator; + return toSourceMerge(localData, generator, leftSide, rightSide, g.getSwfVersion() >= 5 ? new ActionLess2() : new ActionLess()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/StringGeActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/StringGeActionItem.java index 2a6df99b3..6c5271f58 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/StringGeActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/StringGeActionItem.java @@ -1,57 +1,56 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.special.ActionNop; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import java.util.List; -import java.util.Set; - -public class StringGeActionItem extends BinaryOpItem implements Inverted{ - - public StringGeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "ge"); - } - - @Override - public boolean isCompileTime(Set dependencies) { - return false; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringLess(), new ActionNot()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } - - @Override - public GraphTargetItem invert() { - return new StringLtActionItem(src, leftSide, rightSide); - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import java.util.List; +import java.util.Set; + +public class StringGeActionItem extends BinaryOpItem implements Inverted { + + public StringGeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "ge"); + } + + @Override + public boolean isCompileTime(Set dependencies) { + return false; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringLess(), new ActionNot()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } + + @Override + public GraphTargetItem invert() { + return new StringLtActionItem(src, leftSide, rightSide); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/StringGtActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/StringGtActionItem.java index 03b7a7cb0..008d3b926 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/StringGtActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/StringGtActionItem.java @@ -1,61 +1,61 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import java.util.List; -import java.util.Set; - -public class StringGtActionItem extends BinaryOpItem implements Inverted { - - public StringGtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "gt"); - } - - @Override - public boolean isCompileTime(Set dependencies) { - return false; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - ActionSourceGenerator g = (ActionSourceGenerator)generator; - if(g.getSwfVersion()>=6){ - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringGreater()); - } - return toSourceMerge(localData, generator, rightSide, leftSide, new ActionStringLess()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } - - @Override - public GraphTargetItem invert() { - return new StringLeActionItem(src, leftSide, rightSide); - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import java.util.List; +import java.util.Set; + +public class StringGtActionItem extends BinaryOpItem implements Inverted { + + public StringGtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "gt"); + } + + @Override + public boolean isCompileTime(Set dependencies) { + return false; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + ActionSourceGenerator g = (ActionSourceGenerator) generator; + if (g.getSwfVersion() >= 6) { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringGreater()); + } + return toSourceMerge(localData, generator, rightSide, leftSide, new ActionStringLess()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } + + @Override + public GraphTargetItem invert() { + return new StringLeActionItem(src, leftSide, rightSide); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/StringLeActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/StringLeActionItem.java index de0d224f2..daf586822 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/StringLeActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/StringLeActionItem.java @@ -1,65 +1,65 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import java.util.List; -import java.util.Set; - -public class StringLeActionItem extends BinaryOpItem implements Inverted{ - - public StringLeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "le"); - } - - @Override - public boolean isCompileTime(Set dependencies) { - return false; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - - ActionSourceGenerator g = (ActionSourceGenerator)generator; - if(g.getSwfVersion()>=6){ - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringGreater(), new ActionNot()); - } - - return toSourceMerge(localData, generator, rightSide, leftSide, new ActionStringLess(), new ActionNot()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } - - @Override - public GraphTargetItem invert() { - return new StringGtActionItem(src, leftSide, rightSide); - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.parser.script.ActionSourceGenerator; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import static com.jpexs.decompiler.graph.GraphTargetItem.toSourceMerge; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import java.util.List; +import java.util.Set; + +public class StringLeActionItem extends BinaryOpItem implements Inverted { + + public StringLeActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "le"); + } + + @Override + public boolean isCompileTime(Set dependencies) { + return false; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + + ActionSourceGenerator g = (ActionSourceGenerator) generator; + if (g.getSwfVersion() >= 6) { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringGreater(), new ActionNot()); + } + + return toSourceMerge(localData, generator, rightSide, leftSide, new ActionStringLess(), new ActionNot()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } + + @Override + public GraphTargetItem invert() { + return new StringGtActionItem(src, leftSide, rightSide); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/model/operations/StringLtActionItem.java b/src/com/jpexs/decompiler/flash/action/model/operations/StringLtActionItem.java index 920735470..0499fdefd 100644 --- a/src/com/jpexs/decompiler/flash/action/model/operations/StringLtActionItem.java +++ b/src/com/jpexs/decompiler/flash/action/model/operations/StringLtActionItem.java @@ -1,55 +1,55 @@ -/* - * Copyright (C) 2010-2014 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.action.model.operations; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.BinaryOpItem; -import java.util.List; -import java.util.Set; - -public class StringLtActionItem extends BinaryOpItem implements Inverted{ - - public StringLtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { - super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "lt"); - } - - @Override - public boolean isCompileTime(Set dependencies) { - return false; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringLess()); - } - - @Override - public GraphTargetItem returnType() { - return TypeItem.BOOLEAN; - } - - @Override - public GraphTargetItem invert() { - return new StringGeActionItem(src, leftSide, rightSide); - } -} +/* + * Copyright (C) 2010-2014 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.action.model.operations; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.BinaryOpItem; +import java.util.List; +import java.util.Set; + +public class StringLtActionItem extends BinaryOpItem implements Inverted { + + public StringLtActionItem(GraphSourceItem instruction, GraphTargetItem leftSide, GraphTargetItem rightSide) { + super(instruction, PRECEDENCE_RELATIONAL, leftSide, rightSide, "lt"); + } + + @Override + public boolean isCompileTime(Set dependencies) { + return false; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return toSourceMerge(localData, generator, leftSide, rightSide, new ActionStringLess()); + } + + @Override + public GraphTargetItem returnType() { + return TypeItem.BOOLEAN; + } + + @Override + public GraphTargetItem invert() { + return new StringGeActionItem(src, leftSide, rightSide); + } +} diff --git a/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java b/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java index ede8b8859..a1ef4635b 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java +++ b/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptLexer.java @@ -1,2270 +1,2496 @@ -/* The following code was generated by JFlex 1.5.0-SNAPSHOT */ - -/* - * Copyright (C) 2010-2014 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.action.parser.script; -import com.jpexs.decompiler.flash.action.parser.ParseException; -import java.util.Stack; -import java.util.List; -import java.util.ArrayList; - - -/** - * This class is a scanner generated by - * JFlex 1.5.0-SNAPSHOT - * from the specification file D:/Dropbox/Programovani/JavaSE/FFDec/src/com/jpexs/decompiler/flash/action/parser/script/actionscript.flex - */ -public final class ActionScriptLexer { - - /** This character denotes the end of file */ - public static final int YYEOF = -1; - - /** initial size of the lookahead buffer */ - private static final int ZZ_BUFFERSIZE = 16384; - - /** lexical states */ - public static final int YYINITIAL = 0; - public static final int STRING = 2; - public static final int CHARLITERAL = 4; - public static final int XMLSTARTTAG = 6; - public static final int XML = 8; - - /** - * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l - * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l - * at the beginning of a line - * l is of the form l = 2*k, k a non negative integer - */ - private static final int ZZ_LEXSTATE[] = { - 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 - }; - - /** - * Translates characters to character classes - */ - private static final String ZZ_CMAP_PACKED = - "\11\7\1\3\1\2\1\120\1\121\1\1\16\7\4\0\1\14\1\107"+ - "\1\16\1\0\1\6\1\116\1\112\1\30\1\77\1\100\1\5\1\114"+ - "\1\106\1\26\1\11\1\4\1\17\3\23\4\24\2\20\1\10\1\105"+ - "\1\12\1\15\1\13\1\111\1\117\1\61\1\22\1\71\1\72\1\25"+ - "\1\63\1\6\1\65\1\76\2\6\1\67\1\70\1\75\1\6\1\74"+ - "\1\66\1\6\1\62\1\64\1\60\1\73\1\6\1\21\2\6\1\103"+ - "\1\27\1\104\1\115\1\6\1\0\1\34\1\31\1\36\1\45\1\33"+ - "\1\46\1\57\1\51\1\43\1\6\1\35\1\47\1\54\1\41\1\40"+ - "\1\52\1\6\1\32\1\37\1\42\1\44\1\55\1\50\1\56\1\53"+ - "\1\6\1\101\1\113\1\102\1\110\6\7\1\122\32\7\2\0\4\6"+ - "\4\0\1\6\2\0\1\7\7\0\1\6\4\0\1\6\5\0\27\6"+ - "\1\0\37\6\1\0\u01ca\6\4\0\14\6\16\0\5\6\7\0\1\6"+ - "\1\0\1\6\21\0\160\7\5\6\1\0\2\6\2\0\4\6\10\0"+ - "\1\6\1\0\3\6\1\0\1\6\1\0\24\6\1\0\123\6\1\0"+ - "\213\6\1\0\5\7\2\0\236\6\11\0\46\6\2\0\1\6\7\0"+ - "\47\6\11\0\55\7\1\0\1\7\1\0\2\7\1\0\2\7\1\0"+ - "\1\7\10\0\33\6\5\0\3\6\15\0\4\7\7\0\1\6\4\0"+ - "\13\7\5\0\53\6\37\7\4\0\2\6\1\7\143\6\1\0\1\6"+ - "\10\7\1\0\6\7\2\6\2\7\1\0\4\7\2\6\12\7\3\6"+ - "\2\0\1\6\17\0\1\7\1\6\1\7\36\6\33\7\2\0\131\6"+ - "\13\7\1\6\16\0\12\7\41\6\11\7\2\6\4\0\1\6\5\0"+ - "\26\6\4\7\1\6\11\7\1\6\3\7\1\6\5\7\22\0\31\6"+ - "\3\7\244\0\4\7\66\6\3\7\1\6\22\7\1\6\7\7\12\6"+ - "\2\7\2\0\12\7\1\0\7\6\1\0\7\6\1\0\3\7\1\0"+ - "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\1\6\3\0"+ - "\4\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\1\6"+ - "\10\0\1\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\4\6"+ - "\7\0\1\6\5\0\3\7\1\0\6\6\4\0\2\6\2\0\26\6"+ - "\1\0\7\6\1\0\2\6\1\0\2\6\1\0\2\6\2\0\1\7"+ - "\1\0\5\7\4\0\2\7\2\0\3\7\3\0\1\7\7\0\4\6"+ - "\1\0\1\6\7\0\14\7\3\6\1\7\13\0\3\7\1\0\11\6"+ - "\1\0\3\6\1\0\26\6\1\0\7\6\1\0\2\6\1\0\5\6"+ - "\2\0\1\7\1\6\10\7\1\0\3\7\1\0\3\7\2\0\1\6"+ - "\17\0\2\6\2\7\2\0\12\7\1\0\1\6\17\0\3\7\1\0"+ - "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\2\6\1\0"+ - "\5\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\10\0"+ - "\2\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\1\0\1\6"+ - "\20\0\1\7\1\6\1\0\6\6\3\0\3\6\1\0\4\6\3\0"+ - "\2\6\1\0\1\6\1\0\2\6\3\0\2\6\3\0\3\6\3\0"+ - "\14\6\4\0\5\7\3\0\3\7\1\0\4\7\2\0\1\6\6\0"+ - "\1\7\16\0\12\7\11\0\1\6\7\0\3\7\1\0\10\6\1\0"+ - "\3\6\1\0\27\6\1\0\12\6\1\0\5\6\3\0\1\6\7\7"+ - "\1\0\3\7\1\0\4\7\7\0\2\7\1\0\2\6\6\0\2\6"+ - "\2\7\2\0\12\7\22\0\2\7\1\0\10\6\1\0\3\6\1\0"+ - "\27\6\1\0\12\6\1\0\5\6\2\0\1\7\1\6\7\7\1\0"+ - "\3\7\1\0\4\7\7\0\2\7\7\0\1\6\1\0\2\6\2\7"+ - "\2\0\12\7\1\0\2\6\17\0\2\7\1\0\10\6\1\0\3\6"+ - "\1\0\51\6\2\0\1\6\7\7\1\0\3\7\1\0\4\7\1\6"+ - "\10\0\1\7\10\0\2\6\2\7\2\0\12\7\12\0\6\6\2\0"+ - "\2\7\1\0\22\6\3\0\30\6\1\0\11\6\1\0\1\6\2\0"+ - "\7\6\3\0\1\7\4\0\6\7\1\0\1\7\1\0\10\7\22\0"+ - "\2\7\15\0\60\6\1\7\2\6\7\7\4\0\10\6\10\7\1\0"+ - "\12\7\47\0\2\6\1\0\1\6\2\0\2\6\1\0\1\6\2\0"+ - "\1\6\6\0\4\6\1\0\7\6\1\0\3\6\1\0\1\6\1\0"+ - "\1\6\2\0\2\6\1\0\4\6\1\7\2\6\6\7\1\0\2\7"+ - "\1\6\2\0\5\6\1\0\1\6\1\0\6\7\2\0\12\7\2\0"+ - "\2\6\42\0\1\6\27\0\2\7\6\0\12\7\13\0\1\7\1\0"+ - "\1\7\1\0\1\7\4\0\2\7\10\6\1\0\44\6\4\0\24\7"+ - "\1\0\2\7\5\6\13\7\1\0\44\7\11\0\1\7\71\0\53\6"+ - "\24\7\1\6\12\7\6\0\6\6\4\7\4\6\3\7\1\6\3\7"+ - "\2\6\7\7\3\6\4\7\15\6\14\7\1\6\17\7\2\0\46\6"+ - "\12\0\53\6\1\0\1\6\3\0\u0149\6\1\0\4\6\2\0\7\6"+ - "\1\0\1\6\1\0\4\6\2\0\51\6\1\0\4\6\2\0\41\6"+ - "\1\0\4\6\2\0\7\6\1\0\1\6\1\0\4\6\2\0\17\6"+ - "\1\0\71\6\1\0\4\6\2\0\103\6\2\0\3\7\40\0\20\6"+ - "\20\0\125\6\14\0\u026c\6\2\0\21\6\1\0\32\6\5\0\113\6"+ - "\3\0\3\6\17\0\15\6\1\0\4\6\3\7\13\0\22\6\3\7"+ - "\13\0\22\6\2\7\14\0\15\6\1\0\3\6\1\0\2\7\14\0"+ - "\64\6\40\7\3\0\1\6\3\0\2\6\1\7\2\0\12\7\41\0"+ - "\3\7\2\0\12\7\6\0\130\6\10\0\51\6\1\7\1\6\5\0"+ - "\106\6\12\0\35\6\3\0\14\7\4\0\14\7\12\0\12\7\36\6"+ - "\2\0\5\6\13\0\54\6\4\0\21\7\7\6\2\7\6\0\12\7"+ - "\46\0\27\6\5\7\4\0\65\6\12\7\1\0\35\7\2\0\13\7"+ - "\6\0\12\7\15\0\1\6\130\0\5\7\57\6\21\7\7\6\4\0"+ - "\12\7\21\0\11\7\14\0\3\7\36\6\12\7\3\0\2\6\12\7"+ - "\6\0\46\6\16\7\14\0\44\6\24\7\10\0\12\7\3\0\3\6"+ - "\12\7\44\6\122\0\3\7\1\0\25\7\4\6\1\7\4\6\1\7"+ - "\15\0\300\6\47\7\25\0\4\7\u0116\6\2\0\6\6\2\0\46\6"+ - "\2\0\6\6\2\0\10\6\1\0\1\6\1\0\1\6\1\0\1\6"+ - "\1\0\37\6\2\0\65\6\1\0\7\6\1\0\1\6\3\0\3\6"+ - "\1\0\7\6\3\0\4\6\2\0\6\6\4\0\15\6\5\0\3\6"+ - "\1\0\7\6\16\0\5\7\30\0\1\120\1\120\5\7\20\0\2\6"+ - "\23\0\1\6\13\0\5\7\5\0\6\7\1\0\1\6\15\0\1\6"+ - "\20\0\15\6\3\0\32\6\26\0\15\7\4\0\1\7\3\0\14\7"+ - "\21\0\1\6\4\0\1\6\2\0\12\6\1\0\1\6\3\0\5\6"+ - "\6\0\1\6\1\0\1\6\1\0\1\6\1\0\4\6\1\0\13\6"+ - "\2\0\4\6\5\0\5\6\4\0\1\6\21\0\51\6\u0a77\0\57\6"+ - "\1\0\57\6\1\0\205\6\6\0\4\6\3\7\16\0\46\6\12\0"+ - "\66\6\11\0\1\6\17\0\1\7\27\6\11\0\7\6\1\0\7\6"+ - "\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6"+ - "\1\0\7\6\1\0\40\7\57\0\1\6\u01d5\0\3\6\31\0\11\6"+ - "\6\7\1\0\5\6\2\0\5\6\4\0\126\6\2\0\2\7\2\0"+ - "\3\6\1\0\132\6\1\0\4\6\5\0\51\6\3\0\136\6\21\0"+ - "\33\6\65\0\20\6\u0200\0\u19b6\6\112\0\u51cc\6\64\0\u048d\6\103\0"+ - "\56\6\2\0\u010d\6\3\0\20\6\12\7\2\6\24\0\57\6\1\7"+ - "\14\0\2\7\1\0\31\6\10\0\120\6\2\7\45\0\11\6\2\0"+ - "\147\6\2\0\4\6\1\0\2\6\16\0\12\6\120\0\10\6\1\7"+ - "\3\6\1\7\4\6\1\7\27\6\5\7\20\0\1\6\7\0\64\6"+ - "\14\0\2\7\62\6\21\7\13\0\12\7\6\0\22\7\6\6\3\0"+ - "\1\6\4\0\12\7\34\6\10\7\2\0\27\6\15\7\14\0\35\6"+ - "\3\0\4\7\57\6\16\7\16\0\1\6\12\7\46\0\51\6\16\7"+ - "\11\0\3\6\1\7\10\6\2\7\2\0\12\7\6\0\27\6\3\0"+ - "\1\6\1\7\4\0\60\6\1\7\1\6\3\7\2\6\2\7\5\6"+ - "\2\7\1\6\1\7\1\6\30\0\3\6\43\0\6\6\2\0\6\6"+ - "\2\0\6\6\11\0\7\6\1\0\7\6\221\0\43\6\10\7\1\0"+ - "\2\7\2\0\12\7\6\0\u2ba4\6\14\0\27\6\4\0\61\6\u2104\0"+ - "\u012e\6\2\0\76\6\2\0\152\6\46\0\7\6\14\0\5\6\5\0"+ - "\1\6\1\7\12\6\1\0\15\6\1\0\5\6\1\0\1\6\1\0"+ - "\2\6\1\0\2\6\1\0\154\6\41\0\u016b\6\22\0\100\6\2\0"+ - "\66\6\50\0\15\6\3\0\20\7\20\0\7\7\14\0\2\6\30\0"+ - "\3\6\31\0\1\6\6\0\5\6\1\0\207\6\2\0\1\7\4\0"+ - "\1\6\13\0\12\7\7\0\32\6\4\0\1\6\1\0\32\6\13\0"+ - "\131\6\3\0\6\6\2\0\6\6\2\0\6\6\2\0\3\6\3\0"+ - "\2\6\3\0\2\6\22\0\3\7\4\0"; - - /** - * Translates characters to character classes - */ - private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); - - /** - * Translates DFA states to action switch labels. - */ - private static final int [] ZZ_ACTION = zzUnpackAction(); - - private static final String ZZ_ACTION_PACKED_0 = - "\5\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7"+ - "\1\10\1\11\1\12\1\13\1\14\2\15\1\16\1\17"+ - "\26\6\1\20\1\21\1\22\1\23\1\24\1\25\1\26"+ - "\1\27\1\30\1\31\1\32\1\33\1\34\1\35\1\36"+ - "\1\37\1\40\1\41\2\42\1\43\1\1\1\41\2\44"+ - "\1\41\1\1\1\45\3\41\1\3\1\0\1\46\1\47"+ - "\1\50\2\0\1\51\1\0\1\52\1\53\1\54\1\55"+ - "\1\56\1\57\1\60\1\51\1\0\2\60\1\0\1\61"+ - "\1\62\7\6\1\63\11\6\1\64\12\6\1\65\1\66"+ - "\1\67\4\6\1\70\30\6\1\53\1\71\1\72\1\73"+ - "\1\74\1\75\1\76\1\77\1\100\1\101\1\102\2\103"+ - "\1\104\1\105\1\106\1\107\1\110\1\111\1\112\6\0"+ - "\2\3\2\0\1\113\3\0\1\114\1\0\1\115\1\116"+ - "\1\117\1\120\2\121\1\60\1\51\1\0\10\6\1\122"+ - "\5\6\1\123\1\124\5\6\1\125\1\6\1\126\5\6"+ - "\1\127\7\6\1\130\2\6\1\131\10\6\1\132\20\6"+ - "\1\133\1\6\1\134\2\6\1\135\2\6\1\136\1\103"+ - "\7\0\1\137\5\0\1\140\1\121\1\60\4\6\1\141"+ - "\1\142\1\143\1\6\1\144\1\6\1\145\5\6\1\146"+ - "\7\6\1\147\1\6\1\150\4\6\1\151\22\6\1\152"+ - "\7\6\1\153\4\6\1\154\7\6\1\41\1\0\1\155"+ - "\12\0\1\121\1\60\1\156\4\6\1\157\1\160\1\6"+ - "\1\161\5\6\1\162\5\6\1\163\3\6\1\164\14\6"+ - "\1\165\6\6\1\166\2\6\1\167\3\6\1\170\1\6"+ - "\1\171\10\6\10\0\1\121\1\60\1\172\1\6\1\173"+ - "\3\6\1\174\2\6\1\175\1\176\7\6\1\177\4\6"+ - "\1\200\4\6\1\201\5\6\1\202\10\6\1\203\2\6"+ - "\1\204\3\6\1\205\1\206\1\6\2\0\1\114\1\121"+ - "\1\60\1\6\1\207\5\6\1\210\14\6\1\211\1\6"+ - "\1\212\1\6\1\213\7\6\1\214\1\215\6\6\1\41"+ - "\1\121\1\60\1\6\1\216\2\6\1\217\1\220\6\6"+ - "\1\221\7\6\1\222\5\6\1\223\1\6\1\224\1\225"+ - "\3\6\1\226\1\121\1\60\1\6\1\227\1\6\1\230"+ - "\1\231\4\6\1\232\2\6\1\233\2\6\1\234\1\235"+ - "\1\6\1\236\1\237\5\6\1\121\1\60\2\6\1\240"+ - "\1\241\1\6\1\242\1\6\1\243\6\6\1\244\2\6"+ - "\1\60\4\6\1\245\4\6\1\246\1\247\1\250\1\60"+ - "\6\6\1\251\2\6\1\60\1\6\1\252\1\6\1\253"+ - "\2\6\1\254\1\255\1\60\2\6\1\256\3\6\1\60"+ - "\1\257\4\6\1\60\2\6\1\260\1\261\1\262\1\6"+ - "\1\263"; - - private static int [] zzUnpackAction() { - int [] result = new int[687]; - int offset = 0; - offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAction(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - - /** - * Translates a state to a row index in the transition table - */ - private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); - - private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\123\0\246\0\371\0\u014c\0\u019f\0\u01f2\0\u019f"+ - "\0\u0245\0\u0298\0\u02eb\0\u033e\0\u0391\0\u03e4\0\u0437\0\u048a"+ - "\0\u04dd\0\u019f\0\u0530\0\u0583\0\u05d6\0\u019f\0\u0629\0\u067c"+ - "\0\u06cf\0\u0722\0\u0775\0\u07c8\0\u081b\0\u086e\0\u08c1\0\u0914"+ - "\0\u0967\0\u09ba\0\u0a0d\0\u0a60\0\u0ab3\0\u0b06\0\u0b59\0\u0bac"+ - "\0\u0bff\0\u0c52\0\u0ca5\0\u0cf8\0\u019f\0\u019f\0\u019f\0\u019f"+ - "\0\u019f\0\u019f\0\u019f\0\u019f\0\u0d4b\0\u019f\0\u019f\0\u0d9e"+ - "\0\u0df1\0\u0e44\0\u0e97\0\u0eea\0\u019f\0\u0f3d\0\u0f90\0\u019f"+ - "\0\u019f\0\u0fe3\0\u1036\0\u1089\0\u019f\0\u10dc\0\u112f\0\u019f"+ - "\0\u1182\0\u019f\0\u11d5\0\u1228\0\u127b\0\u019f\0\u019f\0\u019f"+ - "\0\u12ce\0\u1321\0\u1374\0\u13c7\0\u141a\0\u019f\0\u019f\0\u146d"+ - "\0\u019f\0\u14c0\0\u1513\0\u1566\0\u15b9\0\u160c\0\u165f\0\u16b2"+ - "\0\u019f\0\u019f\0\u1705\0\u1758\0\u17ab\0\u17fe\0\u1851\0\u18a4"+ - "\0\u18f7\0\u033e\0\u194a\0\u199d\0\u19f0\0\u1a43\0\u1a96\0\u1ae9"+ - "\0\u1b3c\0\u1b8f\0\u1be2\0\u1c35\0\u1c88\0\u1cdb\0\u1d2e\0\u1d81"+ - "\0\u1dd4\0\u1e27\0\u1e7a\0\u1ecd\0\u1f20\0\u1f73\0\u033e\0\u1fc6"+ - "\0\u2019\0\u206c\0\u20bf\0\u2112\0\u2165\0\u033e\0\u21b8\0\u220b"+ - "\0\u225e\0\u22b1\0\u2304\0\u2357\0\u23aa\0\u23fd\0\u2450\0\u24a3"+ - "\0\u24f6\0\u2549\0\u259c\0\u25ef\0\u2642\0\u2695\0\u26e8\0\u273b"+ - "\0\u278e\0\u27e1\0\u2834\0\u2887\0\u28da\0\u292d\0\u2980\0\u019f"+ - "\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f"+ - "\0\u019f\0\u29d3\0\u2a26\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f"+ - "\0\u019f\0\u019f\0\u112f\0\u2a79\0\u2acc\0\u2b1f\0\u2b72\0\u2bc5"+ - "\0\u2c18\0\u019f\0\u2c6b\0\u2cbe\0\u019f\0\u2d11\0\u2d64\0\u2db7"+ - "\0\u019f\0\u2e0a\0\u019f\0\u2e5d\0\u019f\0\u019f\0\u15b9\0\u2eb0"+ - "\0\u2f03\0\u2f56\0\u2f56\0\u2fa9\0\u2ffc\0\u304f\0\u30a2\0\u30f5"+ - "\0\u3148\0\u319b\0\u31ee\0\u033e\0\u3241\0\u3294\0\u32e7\0\u333a"+ - "\0\u338d\0\u033e\0\u033e\0\u33e0\0\u3433\0\u3486\0\u34d9\0\u352c"+ - "\0\u033e\0\u357f\0\u35d2\0\u3625\0\u3678\0\u36cb\0\u371e\0\u3771"+ - "\0\u033e\0\u37c4\0\u3817\0\u386a\0\u38bd\0\u3910\0\u3963\0\u39b6"+ - "\0\u3a09\0\u3a5c\0\u3aaf\0\u033e\0\u3b02\0\u3b55\0\u3ba8\0\u3bfb"+ - "\0\u3c4e\0\u3ca1\0\u3cf4\0\u3d47\0\u033e\0\u3d9a\0\u3ded\0\u3e40"+ - "\0\u3e93\0\u3ee6\0\u3f39\0\u3f8c\0\u3fdf\0\u4032\0\u4085\0\u40d8"+ - "\0\u412b\0\u417e\0\u41d1\0\u4224\0\u4277\0\u033e\0\u42ca\0\u431d"+ - "\0\u4370\0\u43c3\0\u033e\0\u4416\0\u4469\0\u019f\0\u019f\0\u44bc"+ - "\0\u450f\0\u4562\0\u45b5\0\u4608\0\u465b\0\u46ae\0\u019f\0\u4701"+ - "\0\u4754\0\u47a7\0\u47fa\0\u484d\0\u019f\0\u48a0\0\u48f3\0\u4946"+ - "\0\u4999\0\u49ec\0\u4a3f\0\u033e\0\u033e\0\u033e\0\u4a92\0\u033e"+ - "\0\u4ae5\0\u033e\0\u4b38\0\u4b8b\0\u4bde\0\u4c31\0\u4c84\0\u4cd7"+ - "\0\u4d2a\0\u4d7d\0\u4dd0\0\u4e23\0\u4e76\0\u4ec9\0\u4f1c\0\u033e"+ - "\0\u4f6f\0\u033e\0\u4fc2\0\u5015\0\u5068\0\u50bb\0\u033e\0\u510e"+ - "\0\u5161\0\u51b4\0\u5207\0\u525a\0\u52ad\0\u5300\0\u5353\0\u53a6"+ - "\0\u53f9\0\u544c\0\u549f\0\u54f2\0\u5545\0\u5598\0\u55eb\0\u563e"+ - "\0\u5691\0\u033e\0\u56e4\0\u5737\0\u578a\0\u57dd\0\u5830\0\u5883"+ - "\0\u58d6\0\u033e\0\u5929\0\u597c\0\u59cf\0\u5a22\0\u033e\0\u5a75"+ - "\0\u5ac8\0\u5b1b\0\u5b6e\0\u5bc1\0\u5c14\0\u5c67\0\u5cba\0\u5d0d"+ - "\0\u019f\0\u5d60\0\u5db3\0\u5e06\0\u5e59\0\u5eac\0\u5eff\0\u5f52"+ - "\0\u5fa5\0\u5ff8\0\u604b\0\u609e\0\u60f1\0\u033e\0\u6144\0\u6197"+ - "\0\u61ea\0\u623d\0\u033e\0\u033e\0\u6290\0\u033e\0\u62e3\0\u6336"+ - "\0\u6389\0\u63dc\0\u642f\0\u033e\0\u6482\0\u64d5\0\u6528\0\u657b"+ - "\0\u65ce\0\u033e\0\u6621\0\u6674\0\u66c7\0\u033e\0\u671a\0\u676d"+ - "\0\u67c0\0\u6813\0\u6866\0\u68b9\0\u690c\0\u695f\0\u69b2\0\u6a05"+ - "\0\u6a58\0\u6aab\0\u033e\0\u6afe\0\u6b51\0\u6ba4\0\u6bf7\0\u6c4a"+ - "\0\u6c9d\0\u033e\0\u6cf0\0\u6d43\0\u6d96\0\u6de9\0\u6e3c\0\u6e8f"+ - "\0\u033e\0\u6ee2\0\u033e\0\u6f35\0\u6f88\0\u6fdb\0\u702e\0\u7081"+ - "\0\u70d4\0\u7127\0\u717a\0\u71cd\0\u7220\0\u7273\0\u72c6\0\u7319"+ - "\0\u736c\0\u73bf\0\u7412\0\u7465\0\u74b8\0\u033e\0\u750b\0\u033e"+ - "\0\u755e\0\u75b1\0\u7604\0\u033e\0\u7657\0\u76aa\0\u033e\0\u033e"+ - "\0\u76fd\0\u7750\0\u77a3\0\u77f6\0\u7849\0\u789c\0\u78ef\0\u033e"+ - "\0\u7942\0\u7995\0\u79e8\0\u7a3b\0\u033e\0\u7a8e\0\u7ae1\0\u7b34"+ - "\0\u7b87\0\u033e\0\u7bda\0\u7c2d\0\u7c80\0\u7cd3\0\u7d26\0\u033e"+ - "\0\u7d79\0\u7dcc\0\u7e1f\0\u7e72\0\u7ec5\0\u7f18\0\u7f6b\0\u7fbe"+ - "\0\u033e\0\u8011\0\u8064\0\u033e\0\u80b7\0\u810a\0\u815d\0\u033e"+ - "\0\u033e\0\u81b0\0\u8203\0\u8256\0\u5f52\0\u82a9\0\u82fc\0\u834f"+ - "\0\u033e\0\u83a2\0\u83f5\0\u8448\0\u849b\0\u84ee\0\u033e\0\u8541"+ - "\0\u8594\0\u85e7\0\u863a\0\u868d\0\u86e0\0\u8733\0\u8786\0\u87d9"+ - "\0\u882c\0\u887f\0\u88d2\0\u033e\0\u8925\0\u033e\0\u8978\0\u033e"+ - "\0\u89cb\0\u8a1e\0\u8a71\0\u8ac4\0\u8b17\0\u8b6a\0\u8bbd\0\u033e"+ - "\0\u033e\0\u8c10\0\u8c63\0\u8cb6\0\u8d09\0\u8d5c\0\u8daf\0\u7273"+ - "\0\u8e02\0\u8e55\0\u8ea8\0\u033e\0\u8efb\0\u8f4e\0\u033e\0\u033e"+ - "\0\u8fa1\0\u8ff4\0\u9047\0\u909a\0\u90ed\0\u9140\0\u033e\0\u9193"+ - "\0\u91e6\0\u9239\0\u928c\0\u92df\0\u9332\0\u9385\0\u033e\0\u93d8"+ - "\0\u942b\0\u947e\0\u94d1\0\u9524\0\u033e\0\u9577\0\u033e\0\u033e"+ - "\0\u95ca\0\u961d\0\u9670\0\u033e\0\u96c3\0\u9716\0\u9769\0\u033e"+ - "\0\u97bc\0\u033e\0\u033e\0\u980f\0\u9862\0\u98b5\0\u9908\0\u033e"+ - "\0\u995b\0\u99ae\0\u033e\0\u9a01\0\u9a54\0\u033e\0\u9aa7\0\u9afa"+ - "\0\u033e\0\u033e\0\u9b4d\0\u9ba0\0\u9bf3\0\u9c46\0\u9c99\0\u019f"+ - "\0\u9cec\0\u9d3f\0\u9d92\0\u033e\0\u033e\0\u9de5\0\u033e\0\u9e38"+ - "\0\u033e\0\u9e8b\0\u9ede\0\u9f31\0\u9f84\0\u9fd7\0\ua02a\0\u033e"+ - "\0\ua07d\0\ua0d0\0\ua123\0\ua176\0\ua1c9\0\ua21c\0\ua26f\0\ua2c2"+ - "\0\ua315\0\ua368\0\ua3bb\0\ua40e\0\u033e\0\u033e\0\u033e\0\ua461"+ - "\0\ua4b4\0\ua507\0\ua55a\0\ua5ad\0\ua600\0\ua653\0\u033e\0\ua6a6"+ - "\0\ua6f9\0\ua74c\0\ua79f\0\u033e\0\ua7f2\0\u033e\0\ua845\0\ua898"+ - "\0\ua8eb\0\ua93e\0\ua991\0\ua9e4\0\uaa37\0\u033e\0\uaa8a\0\uaadd"+ - "\0\uab30\0\uab83\0\u033e\0\uabd6\0\uac29\0\uac7c\0\uaccf\0\u1566"+ - "\0\uad22\0\uad75\0\u033e\0\u033e\0\u033e\0\uadc8\0\u033e"; - - private static int [] zzUnpackRowMap() { - int [] result = new int[687]; - int offset = 0; - offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackRowMap(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int high = packed.charAt(i++) << 16; - result[j++] = high | packed.charAt(i++); - } - return j; - } - - /** - * The transition table of the DFA - */ - private static final int [] ZZ_TRANS = zzUnpackTrans(); - - private static final String ZZ_TRANS_PACKED_0 = - "\1\6\1\7\1\10\1\11\1\12\1\13\1\14\1\6"+ - "\1\15\1\16\1\17\1\20\1\11\1\21\1\22\1\23"+ - "\1\24\2\14\2\24\1\14\1\25\1\6\1\26\1\27"+ - "\1\30\1\31\1\32\1\14\1\33\1\34\1\35\1\36"+ - "\1\37\1\40\1\41\1\42\1\43\1\44\1\45\1\14"+ - "\1\46\1\14\1\47\1\50\1\14\1\51\2\14\1\52"+ - "\12\14\1\53\1\54\1\55\1\56\1\57\1\60\1\61"+ - "\1\62\1\63\1\64\1\65\1\66\1\67\1\70\1\71"+ - "\1\72\1\73\1\74\1\75\1\0\1\11\1\0\1\76"+ - "\1\77\1\100\13\76\1\101\10\76\1\102\73\76\1\103"+ - "\1\77\1\100\24\103\1\102\1\101\72\103\1\6\1\104"+ - "\1\105\1\106\2\6\1\107\4\6\1\110\1\111\4\6"+ - "\2\107\2\6\1\107\3\6\46\107\21\6\1\0\1\106"+ - "\1\0\1\112\1\104\1\105\7\112\1\113\105\112\130\0"+ - "\1\10\123\0\1\11\10\0\1\11\104\0\1\11\5\0"+ - "\1\114\1\115\7\0\1\116\122\0\1\117\113\0\2\14"+ - "\7\0\7\14\3\0\46\14\23\0\1\14\10\0\1\120"+ - "\123\0\1\121\1\122\4\0\2\123\2\0\2\123\104\0"+ - "\1\124\3\0\1\125\1\126\1\0\1\127\3\0\2\124"+ - "\2\0\1\124\3\0\46\124\37\0\1\130\1\0\1\131"+ - "\122\0\1\132\116\0\1\123\5\0\1\133\1\134\1\135"+ - "\1\0\1\136\1\137\1\140\5\0\1\140\22\0\1\135"+ - "\55\0\1\123\5\0\2\24\2\0\2\24\1\140\5\0"+ - "\1\140\104\0\1\141\10\0\1\142\102\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\143\44\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\144\1\145\42\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14"+ - "\1\146\12\14\1\147\5\14\1\150\1\151\20\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\152"+ - "\1\14\1\153\35\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\154\3\14\1\155\6\14\1\156"+ - "\1\14\1\157\25\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\160\6\14\1\161\1\14\1\162"+ - "\3\14\1\163\26\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\164\22\14\1\165\21\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\166"+ - "\1\167\7\14\1\170\32\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\1\14\1\171\1\172\1\173\3\14"+ - "\1\174\10\14\1\175\1\14\1\176\23\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\6\14\1\177\1\14"+ - "\1\200\4\14\1\201\5\14\1\202\22\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\6\14\1\203\1\14"+ - "\1\204\35\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\2\14\1\205\4\14\1\206\3\14\1\207\6\14"+ - "\1\210\23\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\3\14\1\211\2\14\1\212\1\213\2\14\1\214"+ - "\1\215\32\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\2\14\1\216\4\14\1\217\36\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\220\5\14"+ - "\1\221\25\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\1\14\1\222\1\14\1\223\7\14\1\224\2\14"+ - "\1\225\27\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\1\226\45\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\227\3\14\1\230\36\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\231"+ - "\4\14\1\232\36\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\233\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\234\7\14\1\235"+ - "\32\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\10\14\1\236\35\14\23\0\1\14\15\0\1\237\122\0"+ - "\1\240\74\0\1\241\25\0\1\242\75\0\1\243\24\0"+ - "\1\244\76\0\1\245\23\0\1\246\122\0\1\247\105\0"+ - "\1\76\2\0\13\76\1\0\10\76\1\0\73\76\2\0"+ - "\1\100\120\0\1\250\2\0\13\250\1\251\1\252\3\250"+ - "\1\252\1\253\2\250\1\254\1\255\1\256\1\257\6\250"+ - "\1\260\1\261\3\250\1\262\51\250\3\0\1\103\2\0"+ - "\24\103\2\0\72\103\2\0\1\105\123\0\1\106\10\0"+ - "\1\106\104\0\1\106\7\0\2\263\1\264\3\0\1\265"+ - "\1\266\1\0\7\263\3\0\46\263\23\0\1\263\3\0"+ - "\1\106\2\0\1\263\5\0\1\111\4\0\2\263\2\0"+ - "\1\263\3\0\46\263\22\0\1\106\5\0\1\267\1\0"+ - "\1\270\12\0\2\270\2\0\1\270\3\0\46\270\24\0"+ - "\1\114\1\271\1\272\120\114\5\273\1\274\115\273\11\0"+ - "\1\275\117\0\1\276\12\0\2\276\2\0\1\276\3\0"+ - "\46\276\43\0\2\123\2\0\2\123\1\140\5\0\1\140"+ - "\75\0\1\277\1\124\1\300\2\0\1\301\1\302\2\0"+ - "\2\124\2\277\2\124\1\277\3\0\46\277\23\0\1\124"+ - "\15\0\1\303\120\0\1\304\1\0\1\305\122\0\1\306"+ - "\116\0\1\123\5\0\1\133\1\134\2\0\1\136\1\137"+ - "\1\140\5\0\1\140\100\0\1\123\5\0\2\134\2\0"+ - "\2\134\1\140\5\0\1\140\106\0\1\307\1\310\1\0"+ - "\4\310\3\0\1\310\1\0\2\310\1\0\1\310\6\0"+ - "\2\310\12\0\1\310\1\0\1\310\5\0\2\310\41\0"+ - "\1\123\5\0\1\137\1\134\2\0\2\137\1\140\5\0"+ - "\1\140\100\0\1\123\5\0\1\311\1\134\2\0\2\311"+ - "\1\140\5\0\1\140\106\0\2\312\2\0\2\312\1\0"+ - "\1\313\65\0\1\313\14\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\314\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\315\11\14\1\316\22\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\317"+ - "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\5\14\1\320\40\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\6\14\1\321\37\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\322\42\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\323"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\14\14\1\324\31\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\6\14\1\325\2\14\1\326\4\14\1\327"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\10\14\1\330\35\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\331\42\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\1\14\1\332\44\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\333"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\334\3\14\1\335\36\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\1\336\20\14\1\337\24\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\340\33\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\14\14\1\341\31\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\342\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\17\14\1\343\5\14"+ - "\1\344\20\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\23\14\1\345\22\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\16\14\1\346\27\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\3\14\1\347\7\14"+ - "\1\350\6\14\1\351\23\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\16\14\1\352\27\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\1\14\1\353\44\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14"+ - "\1\354\17\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\1\14\1\355\10\14\1\356\33\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\21\14\1\357\24\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\6\14"+ - "\1\360\2\14\1\361\34\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\32\14\1\362\13\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\21\14\1\363\24\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14"+ - "\1\364\43\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\14\14\1\365\1\14\1\366\27\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\15\14\1\367\1\370"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\21\14\1\371\24\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\10\14\1\372\35\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\16\14\1\373\27\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\374"+ - "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\1\14\1\375\44\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\10\14\1\376\35\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\10\14\1\377\35\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u0100"+ - "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\u0101\42\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u0102\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u0103\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0104"+ - "\4\14\1\u0105\2\14\1\u0106\33\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\5\14\1\u0107\40\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\1\u0108\45\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14"+ - "\1\u0109\42\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\5\14\1\u010a\1\u010b\1\u010c\6\14\1\u010d\27\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\14"+ - "\1\u010e\44\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\12\14\1\u010f\33\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\11\14\1\u0110\34\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\11\14\1\u0111\34\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\14"+ - "\1\u0112\44\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\44\14\1\u0113\1\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\23\14\1\u0114\22\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\15\14\1\u0115\30\14"+ - "\23\0\1\14\15\0\1\u0116\124\0\1\253\3\0\2\253"+ - "\115\0\1\u0117\3\0\2\u0117\104\0\1\u0118\12\0\2\u0118"+ - "\2\0\1\u0118\3\0\46\u0118\40\0\1\265\1\266\121\0"+ - "\1\266\1\0\1\u0119\112\0\1\u011a\12\0\2\u011a\2\0"+ - "\1\u011a\3\0\46\u011a\32\0\1\u011b\1\270\1\u011c\2\0"+ - "\1\112\1\u011d\2\0\2\270\2\u011b\2\270\1\u011b\3\0"+ - "\46\u011b\23\0\1\270\2\0\1\272\120\0\5\273\1\u011e"+ - "\115\273\4\0\1\272\1\274\123\0\2\276\3\0\1\u011f"+ - "\3\0\7\276\3\0\46\276\23\0\1\276\6\0\2\277"+ - "\1\u0120\2\0\1\301\1\u0121\1\u0122\1\0\7\277\3\0"+ - "\46\277\23\0\1\277\6\0\1\u0123\12\0\2\u0123\2\0"+ - "\1\u0123\3\0\46\u0123\32\0\1\u0124\5\0\1\302\4\0"+ - "\2\u0124\2\0\1\u0124\3\0\46\u0124\41\0\1\u0125\124\0"+ - "\2\u0126\1\0\4\u0126\3\0\1\u0126\1\0\2\u0126\1\0"+ - "\1\u0126\6\0\2\u0126\12\0\1\u0126\1\0\1\u0126\5\0"+ - "\2\u0126\41\0\1\123\5\0\1\u0127\1\134\2\0\2\u0127"+ - "\1\140\5\0\1\140\106\0\2\312\2\0\2\312\104\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\u0128\42\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\13\14\1\u0129"+ - "\32\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\7\14\1\u012a\36\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\14\14\1\u012b\31\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\20\14\1\u012c\25\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u012d"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\16\14\1\u012e\27\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u012f\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0130\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u0131"+ - "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\16\14\1\u0132\27\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\6\14\1\u0133\2\14\1\u0134\34\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0135"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\1\14\1\u0136\7\14\1\u0137\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\21\14\1\u0138\24\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0139"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u013a\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u013b\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\1\14\1\u013c\44\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u013d"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u013e\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u013f\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\16\14\1\u0140\27\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u0141"+ - "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u0142\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\16\14\1\u0143\27\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\26\14\1\u0144\17\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\26\14\1\u0145"+ - "\17\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\7\14\1\u0146\36\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\6\14\1\u0147\37\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0148\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u0149"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u014a\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\u014b\44\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\7\14\1\u014c\6\14\1\u014d"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u014e\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\7\14\1\u014f\36\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\u0150\42\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0151"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\16\14\1\u0152\27\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\u0153\42\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\6\14\1\u0154\37\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0155"+ - "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\u0156\42\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\5\14\1\u0157\40\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\26\14\1\u0158\17\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\14\14\1\u0159"+ - "\31\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\20\14\1\u015a\25\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\16\14\1\u015b\27\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\24\14\1\u015c\21\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u015d"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\10\14\1\u015e\13\14\1\u015f\21\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\4\14\1\u0160\41\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u0161"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\22\14\1\u0162\23\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\20\14\1\u0163\25\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\13\14\1\u0164\32\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\1\14\1\u0165"+ - "\44\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u0166\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\14\14\1\u0167\31\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\27\14\1\u0168\3\14\1\u0169"+ - "\6\14\1\u016a\3\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\7\14\1\u016b\36\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u016c\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\1\u016d\45\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\u016e\33\14\23\0\1\14\6\0\2\u0118\4\0\1\265"+ - "\1\266\1\0\7\u0118\3\0\46\u0118\23\0\1\u0118\1\u0119"+ - "\2\0\13\u0119\1\u016f\104\u0119\6\0\2\u011a\1\u0170\2\0"+ - "\1\u0171\3\0\7\u011a\3\0\46\u011a\23\0\1\u011a\6\0"+ - "\2\u011b\1\u0172\2\0\1\112\1\u0173\1\u0174\1\0\7\u011b"+ - "\3\0\46\u011b\23\0\1\u011b\6\0\1\u0175\12\0\2\u0175"+ - "\2\0\1\u0175\3\0\46\u0175\32\0\1\u0176\5\0\1\u011d"+ - "\4\0\2\u0176\2\0\1\u0176\3\0\46\u0176\24\0\4\273"+ - "\1\272\1\u011e\115\273\6\0\1\u0177\12\0\2\u0177\2\0"+ - "\1\u0177\3\0\46\u0177\32\0\1\u0124\5\0\1\u0121\1\u0122"+ - "\3\0\2\u0124\2\0\1\u0124\3\0\46\u0124\40\0\1\u0122"+ - "\1\0\1\u0178\112\0\1\u0179\1\u0123\3\0\1\301\1\302"+ - "\2\0\2\u0123\2\u0179\2\u0123\1\u0179\3\0\46\u0179\23\0"+ - "\1\u0123\6\0\2\u0124\1\u017a\3\0\1\u017b\1\u0122\1\0"+ - "\7\u0124\3\0\46\u0124\23\0\1\u0124\17\0\2\u017c\1\0"+ - "\4\u017c\3\0\1\u017c\1\0\2\u017c\1\0\1\u017c\6\0"+ - "\2\u017c\12\0\1\u017c\1\0\1\u017c\5\0\2\u017c\41\0"+ - "\1\123\5\0\1\u017d\1\134\2\0\2\u017d\1\140\5\0"+ - "\1\140\75\0\2\14\7\0\7\14\3\0\4\14\1\u017e"+ - "\41\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\1\14\1\u017f\44\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\24\14\1\u0180\21\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\7\14\1\u0181\36\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u0182"+ - "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\20\14\1\u0183\25\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u0184\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u0185\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0186"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u0187\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\12\14\1\u0188\33\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\30\14\1\u0189\10\14\1\u018a"+ - "\4\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u018b\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\u018c\44\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\5\14\1\u018d\40\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\1\14\1\u018e"+ - "\44\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\12\14\1\u018f\33\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\32\14\1\u0190\13\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\6\14\1\u0191\37\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0192"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\33\14\1\u0193\12\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u0194\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\16\14\1\u0195\27\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\17\14\1\u0196"+ - "\26\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\7\14\1\u0197\36\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\u0198\42\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\1\14\1\u0199\44\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u019a"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\1\14\1\u019b\44\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u019c\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\15\14\1\u019d\30\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u019e"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\13\14\1\u019f\32\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u01a0\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u01a1\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\23\14\1\u01a2"+ - "\22\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\2\14\1\u01a3\43\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\23\14\1\u01a4\22\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\16\14\1\u01a5\27\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u01a6"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u01a7\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\37\14\1\u01a8\2\14\1\u01a9\3\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u01aa"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\32\14\1\u01ab\13\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u01ac\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\11\14\1\u01ad\34\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u01ae"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\u01af\42\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\12\14\1\u01b0\33\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\1\14\1\u01b1\44\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\1\u01b2\45\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\14\14"+ - "\1\u01b3\31\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\10\14\1\u01b4\35\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\1\14\1\u01b5\44\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01b6\33\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14"+ - "\1\u01b7\43\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\30\14\1\u01b8\15\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u01b9\35\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01ba\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\10\14"+ - "\1\u01bb\35\14\23\0\1\14\1\u0119\2\0\11\u0119\1\u016f"+ - "\1\u0119\1\u016f\104\u0119\6\0\1\u01bc\12\0\2\u01bc\2\0"+ - "\1\u01bc\3\0\46\u01bc\32\0\1\u01bd\12\0\2\u01bd\2\0"+ - "\1\u01bd\3\0\46\u01bd\32\0\1\u0176\5\0\1\u0173\1\u0174"+ - "\3\0\2\u0176\2\0\1\u0176\3\0\46\u0176\40\0\1\u0174"+ - "\1\0\1\u01be\112\0\1\u01bf\1\u0175\3\0\1\112\1\u011d"+ - "\2\0\2\u0175\2\u01bf\2\u0175\1\u01bf\3\0\46\u01bf\23\0"+ - "\1\u0175\6\0\2\u0176\1\u01c0\3\0\1\u01c1\1\u0174\1\0"+ - "\7\u0176\3\0\46\u0176\23\0\1\u0176\6\0\1\u0179\1\u0177"+ - "\3\0\1\301\1\u0121\1\u0122\1\0\2\u0177\2\u0179\2\u0177"+ - "\1\u0179\3\0\46\u0179\23\0\1\u0177\1\u0178\2\0\13\u0178"+ - "\1\u01c2\104\u0178\6\0\2\u0179\1\u017a\2\0\1\301\1\u0121"+ - "\1\u0122\1\0\7\u0179\3\0\46\u0179\23\0\1\u0179\6\0"+ - "\1\u01c3\12\0\2\u01c3\2\0\1\u01c3\3\0\46\u01c3\40\0"+ - "\1\u017b\1\u0122\124\0\2\u01c4\1\0\4\u01c4\3\0\1\u01c4"+ - "\1\0\2\u01c4\1\0\1\u01c4\6\0\2\u01c4\12\0\1\u01c4"+ - "\1\0\1\u01c4\5\0\2\u01c4\41\0\1\123\5\0\1\u01c5"+ - "\1\134\2\0\2\u01c5\1\140\5\0\1\140\75\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u01c6\35\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01c7\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14"+ - "\1\u01c8\22\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\14\14\1\u01c9\31\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u01ca\35\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\41\14\1\u01cb\4\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\5\14"+ - "\1\u01cc\40\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\16\14\1\u01cd\27\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\1\14\1\u01ce\44\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01cf\44\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14"+ - "\1\u01d0\25\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\12\14\1\u01d1\33\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u01d2\35\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01d3\44\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\21\14"+ - "\1\u01d4\24\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\3\14\1\u01d5\42\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\11\14\1\u01d6\34\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01d7\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\15\14"+ - "\1\u01d8\30\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\10\14\1\u01d9\35\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u01da\4\14\1\u01db\30\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14"+ - "\1\u01dc\22\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\11\14\1\u01dd\34\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\23\14\1\u01de\22\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01df\33\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\14\14"+ - "\1\u01e0\31\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\16\14\1\u01e1\27\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\u01e2\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\5\14\1\u01e3\40\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\u01e4\33\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\23\14\1\u01e5\22\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\16\14\1\u01e6\27\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01e7\33\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14"+ - "\1\u01e8\25\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\7\14\1\u01e9\36\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\3\14\1\u01ea\42\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01eb\44\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\5\14"+ - "\1\u01ec\40\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\30\14\1\u01ed\13\14\1\u01ee\1\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\11\14\1\u01ef\34\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14"+ - "\1\u01f0\17\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\5\14\1\u01f1\40\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\6\14\1\u01f2\37\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\26\14\1\u01f3\17\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\16\14"+ - "\1\u01f4\27\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\23\14\1\u01f5\22\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\1\14\1\u01f6\44\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\10\14\1\u01f7\35\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14"+ - "\1\u01f8\17\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\1\14\1\u01f9\44\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\12\14\1\u01fa\33\14\23\0\1\14"+ - "\6\0\2\u01bc\3\0\1\u0171\3\0\7\u01bc\3\0\46\u01bc"+ - "\23\0\1\u01bc\6\0\1\u01bf\1\u01bd\3\0\1\112\1\u0173"+ - "\1\u0174\1\0\2\u01bd\2\u01bf\2\u01bd\1\u01bf\3\0\46\u01bf"+ - "\23\0\1\u01bd\1\u01be\2\0\13\u01be\1\u01fb\104\u01be\6\0"+ - "\2\u01bf\1\u01c0\2\0\1\112\1\u0173\1\u0174\1\0\7\u01bf"+ - "\3\0\46\u01bf\23\0\1\u01bf\6\0\1\u01fc\12\0\2\u01fc"+ - "\2\0\1\u01fc\3\0\46\u01fc\40\0\1\u01c1\1\u0174\105\0"+ - "\1\u0178\2\0\10\u0178\1\u01fd\1\u01c2\1\u0178\1\u01c2\104\u0178"+ - "\6\0\2\u01c3\4\0\1\u017b\1\u0122\1\0\7\u01c3\3\0"+ - "\46\u01c3\23\0\1\u01c3\17\0\2\u01fe\1\0\4\u01fe\3\0"+ - "\1\u01fe\1\0\2\u01fe\1\0\1\u01fe\6\0\2\u01fe\12\0"+ - "\1\u01fe\1\0\1\u01fe\5\0\2\u01fe\41\0\1\123\5\0"+ - "\1\u01ff\1\134\2\0\2\u01ff\1\140\5\0\1\140\75\0"+ - "\2\14\7\0\7\14\3\0\37\14\1\u0200\6\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0201"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\13\14\1\u0202\32\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\u0203\44\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\16\14\1\u0204\27\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0205"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\14\14\1\u0206\31\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u0207\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\u0208\42\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0209"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\1\14\1\u020a\44\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\43\14\1\u020b\2\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\34\14\1\u020c\11\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u020d"+ - "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\u020e\42\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\u020f\42\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0210\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0211"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\10\14\1\u0212\35\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\37\14\1\u0213\6\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\11\14\1\u0214\34\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0215"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\5\14\1\u0216\40\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\3\14\1\u0217\42\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\22\14\1\u0218\23\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0219"+ - "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\24\14\1\u021a\21\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\1\14\1\u021b\44\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\3\14\1\u021c\42\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u021d"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\6\14\1\u021e\37\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\13\14\1\u021f\32\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0220\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0221"+ - "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u0222\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u0223\34\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0224\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0225"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\14\14\1\u0226\31\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\11\14\1\u0227\34\14\23\0\1\14\1\u01be"+ - "\2\0\10\u01be\1\u0228\1\u01fb\1\u01be\1\u01fb\104\u01be\6\0"+ - "\2\u01fc\4\0\1\u01c1\1\u0174\1\0\7\u01fc\3\0\46\u01fc"+ - "\23\0\1\u01fc\17\0\2\u0229\1\0\4\u0229\3\0\1\u0229"+ - "\1\0\2\u0229\1\0\1\u0229\6\0\2\u0229\12\0\1\u0229"+ - "\1\0\1\u0229\5\0\2\u0229\41\0\1\123\5\0\1\u022a"+ - "\1\134\2\0\2\u022a\1\140\5\0\1\140\75\0\2\14"+ - "\7\0\7\14\3\0\7\14\1\u022b\36\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u022c\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14"+ - "\1\u022d\42\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\31\14\1\u022e\14\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\26\14\1\u022f\17\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0230\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14"+ - "\1\u0231\22\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\5\14\1\u0232\40\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\26\14\1\u0233\17\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0234\42\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\u0235\33\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\2\14\1\u0236\43\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\16\14\1\u0237\27\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\5\14\1\u0238\40\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\36\14"+ - "\1\u0239\7\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\10\14\1\u023a\35\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\u023b\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\7\14\1\u023c\36\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\11\14"+ - "\1\u023d\34\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\10\14\1\u023e\35\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\10\14\1\u023f\35\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\u0240\33\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\u0241\33\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\23\14\1\u0242\22\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\u0243\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\3\14\1\u0244\3\14\3\0\46\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14"+ - "\1\u0245\22\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\1\14\1\u0246\44\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\20\14\1\u0247\25\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\1\14\1\u0248\44\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14"+ - "\1\u0249\33\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\31\14\1\u024a\11\14\1\u024b\2\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\22\14\1\u024c\23\14"+ - "\23\0\1\14\17\0\2\u024d\1\0\4\u024d\3\0\1\u024d"+ - "\1\0\2\u024d\1\0\1\u024d\6\0\2\u024d\12\0\1\u024d"+ - "\1\0\1\u024d\5\0\2\u024d\41\0\1\123\5\0\1\u024e"+ - "\1\134\2\0\2\u024e\1\140\5\0\1\140\75\0\2\14"+ - "\7\0\7\14\3\0\24\14\1\u024f\21\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\26\14\1\u0250\17\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14"+ - "\1\u0251\36\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\2\14\1\u0252\43\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\u0253\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0254\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\11\14"+ - "\1\u0255\34\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\26\14\1\u0256\17\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\7\14\1\u0257\36\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0258\43\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14"+ - "\1\u0259\36\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\11\14\1\u025a\34\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\14\14\1\u025b\31\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\24\14\1\u025c\21\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14"+ - "\1\u025d\43\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\14\14\1\u025e\31\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\2\14\1\u025f\43\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0260\42\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14"+ - "\1\u0261\43\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\14\14\1\u0262\31\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\12\14\1\u0263\33\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\12\14\1\u0264\33\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14"+ - "\1\u0265\36\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\11\14\1\u0266\34\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\16\14\1\u0267\27\14\23\0\1\14"+ - "\17\0\2\u0268\1\0\4\u0268\3\0\1\u0268\1\0\2\u0268"+ - "\1\0\1\u0268\6\0\2\u0268\12\0\1\u0268\1\0\1\u0268"+ - "\5\0\2\u0268\41\0\1\123\5\0\1\u0269\1\134\2\0"+ - "\2\u0269\1\140\5\0\1\140\75\0\2\14\7\0\7\14"+ - "\3\0\12\14\1\u026a\33\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\13\14\1\u026b\32\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\11\14\1\u026c\34\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14"+ - "\1\u026d\25\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\20\14\1\u026e\25\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\15\14\1\u026f\30\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0270\42\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\6\14"+ - "\1\u0271\37\14\23\0\1\14\6\0\2\14\7\0\7\14"+ - "\3\0\12\14\1\u0272\33\14\23\0\1\14\6\0\2\14"+ - "\7\0\7\14\3\0\37\14\1\u0273\6\14\23\0\1\14"+ - "\6\0\2\14\7\0\7\14\3\0\44\14\1\u0274\1\14"+ - "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\u0275"+ - "\45\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\11\14\1\u0276\34\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\10\14\1\u0277\35\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\10\14\1\u0278\35\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0279"+ - "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\3\14\1\u027a\42\14\23\0\1\14\11\0\1\123\5\0"+ - "\1\u027b\1\134\2\0\2\u027b\1\140\5\0\1\140\75\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u027c\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u027d"+ - "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\35\14\1\u027e\10\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\14\14\1\u027f\31\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0280\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0281"+ - "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\13\14\1\u0282\32\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\16\14\1\u0283\27\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\23\14\1\u0284\22\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\26\14\1\u0285"+ - "\17\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\21\14\1\u0286\24\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\22\14\1\u0287\23\14\23\0\1\14\11\0"+ - "\1\123\5\0\1\u0288\1\134\2\0\2\u0288\1\140\5\0"+ - "\1\140\75\0\2\14\7\0\7\14\3\0\40\14\1\u0289"+ - "\5\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\14\14\1\u028a\31\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\13\14\1\u028b\32\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u028c\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\44\14\1\u028d"+ - "\1\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\24\14\1\u028e\21\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\23\14\1\u028f\22\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\2\14\1\u0290\43\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0291"+ - "\42\14\23\0\1\14\11\0\1\123\5\0\1\u0292\1\134"+ - "\2\0\2\u0292\1\140\5\0\1\140\75\0\2\14\7\0"+ - "\7\14\3\0\16\14\1\u0293\27\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\6\14\1\u0294\37\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0295"+ - "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\14\14\1\u0296\31\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\13\14\1\u0297\32\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u0298\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0299"+ - "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\21\14\1\u029a\24\14\23\0\1\14\11\0\1\123\5\0"+ - "\1\u029b\1\134\2\0\2\u029b\1\140\5\0\1\140\75\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u029c\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u029d"+ - "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\23\14\1\u029e\22\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\2\14\1\u029f\43\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\44\14\1\u02a0\1\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\44\14\1\u02a1"+ - "\1\14\23\0\1\14\11\0\1\123\5\0\1\u02a2\1\134"+ - "\2\0\2\u02a2\1\140\5\0\1\140\75\0\2\14\7\0"+ - "\7\14\3\0\21\14\1\u02a3\24\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\12\14\1\u02a4\33\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\40\14\1\u02a5"+ - "\5\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\13\14\1\u02a6\32\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\13\14\1\u02a7\32\14\23\0\1\14\11\0"+ - "\1\123\5\0\1\u02a8\1\134\2\0\2\u02a8\1\140\5\0"+ - "\1\140\75\0\2\14\7\0\7\14\3\0\11\14\1\u02a9"+ - "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\16\14\1\u02aa\27\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\23\14\1\u02ab\22\14\23\0\1\14\6\0"+ - "\2\14\7\0\7\14\3\0\23\14\1\u02ac\22\14\23\0"+ - "\1\14\6\0\2\14\7\0\7\14\3\0\22\14\1\u02ad"+ - "\23\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0"+ - "\12\14\1\u02ae\33\14\23\0\1\14\6\0\2\14\7\0"+ - "\7\14\3\0\21\14\1\u02af\24\14\23\0\1\14"; - - private static int [] zzUnpackTrans() { - int [] result = new int[44571]; - int offset = 0; - offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackTrans(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - value--; - do result[j++] = value; while (--count > 0); - } - return j; - } - - - /* error codes */ - private static final int ZZ_UNKNOWN_ERROR = 0; - private static final int ZZ_NO_MATCH = 1; - private static final int ZZ_PUSHBACK_2BIG = 2; - - /* error messages for the codes above */ - private static final String ZZ_ERROR_MSG[] = { - "Unkown internal scanner error", - "Error: could not match input", - "Error: pushback value was too large" - }; - - /** - * ZZ_ATTRIBUTE[aState] contains the attributes of state aState - */ - private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); - - private static final String ZZ_ATTRIBUTE_PACKED_0 = - "\5\0\1\11\1\1\1\11\11\1\1\11\3\1\1\11"+ - "\26\1\10\11\1\1\2\11\5\1\1\11\2\1\2\11"+ - "\3\1\1\11\2\1\1\11\1\1\1\11\2\1\1\0"+ - "\3\11\2\0\1\1\1\0\1\1\2\11\1\1\1\11"+ - "\3\1\1\0\2\1\1\0\2\11\75\1\12\11\2\1"+ - "\7\11\6\0\1\1\1\11\2\0\1\11\3\0\1\11"+ - "\1\0\1\11\1\1\2\11\4\1\1\0\112\1\2\11"+ - "\7\0\1\11\5\0\1\11\112\1\1\0\1\11\12\0"+ - "\100\1\10\0\67\1\2\0\153\1\1\11\107\1"; - - private static int [] zzUnpackAttribute() { - int [] result = new int[687]; - int offset = 0; - offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); - return result; - } - - private static int zzUnpackAttribute(String packed, int offset, int [] result) { - int i = 0; /* index in packed string */ - int j = offset; /* index in unpacked array */ - int l = packed.length(); - while (i < l) { - int count = packed.charAt(i++); - int value = packed.charAt(i++); - do result[j++] = value; while (--count > 0); - } - return j; - } - - /** the input device */ - private java.io.Reader zzReader; - - /** the current state of the DFA */ - private int zzState; - - /** the current lexical state */ - private int zzLexicalState = YYINITIAL; - - /** this buffer contains the current text to be matched and is - the source of the yytext() string */ - private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; - - /** the textposition at the last accepting state */ - private int zzMarkedPos; - - /** the current text position in the buffer */ - private int zzCurrentPos; - - /** startRead marks the beginning of the yytext() string in the buffer */ - private int zzStartRead; - - /** endRead marks the last character in the buffer, that has been read - from input */ - private int zzEndRead; - - /** number of newlines encountered up to the start of the matched text */ - private int yyline; - - /** the number of characters up to the start of the matched text */ - private int yychar; - - /** - * the number of characters from the last newline up to the start of the - * matched text - */ - private int yycolumn; - - /** - * zzAtBOL == true <=> the scanner is currently at the beginning of a line - */ - private boolean zzAtBOL = true; - - /** zzAtEOF == true <=> the scanner is at the EOF */ - private boolean zzAtEOF; - - /** denotes if the user-EOF-code has already been executed */ - private boolean zzEOFDone; - - /* user code: */ - - StringBuffer string = new StringBuffer(); - - private static String xmlTagName=""; - - public int yychar() { - return yychar; - } - - private Stack pushedBack=new Stack(); - - public int yyline() { - return yyline+1; - } - private List listeners=new ArrayList<>(); - - public void addListener(LexListener listener){ - listeners.add(listener); - } - - public void removeListener(LexListener listener){ - listeners.remove(listener); - } - - public void informListenersLex(ParsedSymbol s){ - for(LexListener l:listeners){ - l.onLex(s); - } - } - - public void informListenersPushBack(ParsedSymbol s){ - for(LexListener l:listeners){ - l.onPushBack(s); - } - } - - public void pushback(ParsedSymbol symb) { - pushedBack.push(symb); - last = null; - informListenersPushBack(symb); - } - ParsedSymbol last; - public ParsedSymbol lex() throws java.io.IOException, ParseException{ - ParsedSymbol ret=null; - if(!pushedBack.isEmpty()){ - ret = last = pushedBack.pop(); - }else{ - ret = last = yylex(); - } - informListenersLex(ret); - return ret; - } - - - - /** - * Creates a new scanner - * There is also a java.io.InputStream version of this constructor. - * - * @param in the java.io.Reader to read input from. - */ - public ActionScriptLexer(java.io.Reader in) { - this.zzReader = in; - } - - /** - * Creates a new scanner. - * There is also java.io.Reader version of this constructor. - * - * @param in the java.io.Inputstream to read input from. - */ - public ActionScriptLexer(java.io.InputStream in) { - this(new java.io.InputStreamReader - (in, java.nio.charset.Charset.forName("UTF-8"))); - } - - /** - * Unpacks the compressed character translation table. - * - * @param packed the packed character translation table - * @return the unpacked character translation table - */ - private static char [] zzUnpackCMap(String packed) { - char [] map = new char[0x10000]; - int i = 0; /* index in packed string */ - int j = 0; /* index in unpacked array */ - while (i < 2272) { - int count = packed.charAt(i++); - char value = packed.charAt(i++); - do map[j++] = value; while (--count > 0); - } - return map; - } - - - /** - * Refills the input buffer. - * - * @return false, iff there was new input. - * - * @exception java.io.IOException if any I/O-Error occurs - */ - private boolean zzRefill() throws java.io.IOException { - - /* first: make room (if you can) */ - if (zzStartRead > 0) { - System.arraycopy(zzBuffer, zzStartRead, - zzBuffer, 0, - zzEndRead-zzStartRead); - - /* translate stored positions */ - zzEndRead-= zzStartRead; - zzCurrentPos-= zzStartRead; - zzMarkedPos-= zzStartRead; - zzStartRead = 0; - } - - /* is the buffer big enough? */ - if (zzCurrentPos >= zzBuffer.length) { - /* if not: blow it up */ - char newBuffer[] = new char[zzCurrentPos*2]; - System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); - zzBuffer = newBuffer; - } - - /* finally: fill the buffer with new input */ - int numRead = zzReader.read(zzBuffer, zzEndRead, - zzBuffer.length-zzEndRead); - - if (numRead > 0) { - zzEndRead+= numRead; - return false; - } - // unlikely but not impossible: read 0 characters, but not at end of stream - if (numRead == 0) { - int c = zzReader.read(); - if (c == -1) { - return true; - } else { - zzBuffer[zzEndRead++] = (char) c; - return false; - } - } - - // numRead < 0 - return true; - } - - - /** - * Closes the input stream. - */ - public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ - - if (zzReader != null) - zzReader.close(); - } - - - /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. - * - * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to ZZ_INITIAL. - * - * Internal scan buffer is resized down to its initial length, if it has grown. - * - * @param reader the new input stream - */ - public final void yyreset(java.io.Reader reader) { - zzReader = reader; - zzAtBOL = true; - zzAtEOF = false; - zzEOFDone = false; - zzEndRead = zzStartRead = 0; - zzCurrentPos = zzMarkedPos = 0; - yyline = yychar = yycolumn = 0; - zzLexicalState = YYINITIAL; - if (zzBuffer.length > ZZ_BUFFERSIZE) - zzBuffer = new char[ZZ_BUFFERSIZE]; - } - - - /** - * Returns the current lexical state. - */ - public final int yystate() { - return zzLexicalState; - } - - - /** - * Enters a new lexical state - * - * @param newState the new lexical state - */ - public final void yybegin(int newState) { - zzLexicalState = newState; - } - - - /** - * Returns the text matched by the current regular expression. - */ - public final String yytext() { - return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); - } - - - /** - * Returns the character at position pos from the - * matched text. - * - * It is equivalent to yytext().charAt(pos), but faster - * - * @param pos the position of the character to fetch. - * A value from 0 to yylength()-1. - * - * @return the character at position pos - */ - public final char yycharat(int pos) { - return zzBuffer[zzStartRead+pos]; - } - - - /** - * Returns the length of the matched text region. - */ - public final int yylength() { - return zzMarkedPos-zzStartRead; - } - - - /** - * Reports an error that occured while scanning. - * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method - * will only be called with things that "Can't Possibly Happen". - * If this method is called, something is seriously wrong - * (e.g. a JFlex bug producing a faulty scanner etc.). - * - * Usual syntax/scanner level error handling should be done - * in error fallback rules. - * - * @param errorCode the code of the errormessage to display - */ - private void zzScanError(int errorCode) { - String message; - try { - message = ZZ_ERROR_MSG[errorCode]; - } - catch (ArrayIndexOutOfBoundsException e) { - message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; - } - - throw new Error(message); - } - - - /** - * Pushes the specified amount of characters back into the input stream. - * - * They will be read again by then next call of the scanning method - * - * @param number the number of characters to be read again. - * This number must not be greater than yylength()! - */ - public void yypushback(int number) { - if ( number > yylength() ) - zzScanError(ZZ_PUSHBACK_2BIG); - - zzMarkedPos -= number; - } - - - /** - * Resumes scanning until the next regular expression is matched, - * the end of input is encountered or an I/O-Error occurs. - * - * @return the next token - * @exception java.io.IOException if any I/O-Error occurs - */ - public ParsedSymbol yylex() throws java.io.IOException, ParseException { - int zzInput; - int zzAction; - - // cached fields: - int zzCurrentPosL; - int zzMarkedPosL; - int zzEndReadL = zzEndRead; - char [] zzBufferL = zzBuffer; - char [] zzCMapL = ZZ_CMAP; - - int [] zzTransL = ZZ_TRANS; - int [] zzRowMapL = ZZ_ROWMAP; - int [] zzAttrL = ZZ_ATTRIBUTE; - - while (true) { - zzMarkedPosL = zzMarkedPos; - - yychar+= zzMarkedPosL-zzStartRead; - - zzAction = -1; - - zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; - - zzState = ZZ_LEXSTATE[zzLexicalState]; - - // set up zzAction for empty match case: - int zzAttributes = zzAttrL[zzState]; - if ( (zzAttributes & 1) == 1 ) { - zzAction = zzState; - } - - - zzForAction: { - while (true) { - - if (zzCurrentPosL < zzEndReadL) - zzInput = zzBufferL[zzCurrentPosL++]; - else if (zzAtEOF) { - zzInput = YYEOF; - break zzForAction; - } - else { - // store back cached positions - zzCurrentPos = zzCurrentPosL; - zzMarkedPos = zzMarkedPosL; - boolean eof = zzRefill(); - // get translated positions and possibly new buffer - zzCurrentPosL = zzCurrentPos; - zzMarkedPosL = zzMarkedPos; - zzBufferL = zzBuffer; - zzEndReadL = zzEndRead; - if (eof) { - zzInput = YYEOF; - break zzForAction; - } - else { - zzInput = zzBufferL[zzCurrentPosL++]; - } - } - int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; - if (zzNext == -1) break zzForAction; - zzState = zzNext; - - zzAttributes = zzAttrL[zzState]; - if ( (zzAttributes & 1) == 1 ) { - zzAction = zzState; - zzMarkedPosL = zzCurrentPosL; - if ( (zzAttributes & 8) == 8 ) break zzForAction; - } - - } - } - - // store back cached position - zzMarkedPos = zzMarkedPosL; - - switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { - case 1: - { - } - case 180: break; - case 2: - { yyline++; - } - case 181: break; - case 3: - { /*ignore*/ - } - case 182: break; - case 4: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.DIVIDE,yytext()); - } - case 183: break; - case 5: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.MULTIPLY,yytext()); - } - case 184: break; - case 6: - { return new ParsedSymbol(SymbolGroup.IDENTIFIER,SymbolType.IDENTIFIER, yytext()); - } - case 185: break; - case 7: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.COLON,yytext()); - } - case 186: break; - case 8: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.DOT,yytext()); - } - case 187: break; - case 9: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.LOWER_THAN,yytext()); - } - case 188: break; - case 10: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.GREATER_THAN,yytext()); - } - case 189: break; - case 11: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN,yytext()); - } - case 190: break; - case 12: - { string.setLength(0); - yybegin(STRING); - } - case 191: break; - case 13: - { return new ParsedSymbol(SymbolGroup.INTEGER,SymbolType.INTEGER,new Long(Long.parseLong((yytext())))); - } - case 192: break; - case 14: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.MINUS,yytext()); - } - case 193: break; - case 15: - { string.setLength(0); - yybegin(CHARLITERAL); - } - case 194: break; - case 16: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.PARENT_OPEN,yytext()); - } - case 195: break; - case 17: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.PARENT_CLOSE,yytext()); - } - case 196: break; - case 18: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.CURLY_OPEN,yytext()); - } - case 197: break; - case 19: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.CURLY_CLOSE,yytext()); - } - case 198: break; - case 20: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.BRACKET_OPEN,yytext()); - } - case 199: break; - case 21: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.BRACKET_CLOSE,yytext()); - } - case 200: break; - case 22: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.SEMICOLON,yytext()); - } - case 201: break; - case 23: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.COMMA,yytext()); - } - case 202: break; - case 24: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.NOT,yytext()); - } - case 203: break; - case 25: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.NEGATE,yytext()); - } - case 204: break; - case 26: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.TERNAR,yytext()); - } - case 205: break; - case 27: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.BITAND,yytext()); - } - case 206: break; - case 28: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.BITOR,yytext()); - } - case 207: break; - case 29: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.PLUS,yytext()); - } - case 208: break; - case 30: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.XOR,yytext()); - } - case 209: break; - case 31: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.MODULO,yytext()); - } - case 210: break; - case 32: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ATTRIBUTE,yytext()); - } - case 211: break; - case 33: - { string.append( yytext() ); - } - case 212: break; - case 34: - { yybegin(YYINITIAL); yyline++; - } - case 213: break; - case 35: - { yybegin(YYINITIAL); - // length also includes the trailing quote - return new ParsedSymbol(SymbolGroup.STRING,SymbolType.STRING,string.toString()); - } - case 214: break; - case 36: - { string.append( yytext() ); yyline++; - } - case 215: break; - case 37: - { yybegin(XML); string.append( yytext() ); - } - case 216: break; - case 38: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_DIVIDE,yytext()); - } - case 217: break; - case 39: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_MULTIPLY,yytext()); - } - case 218: break; - case 40: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.NAMESPACE_OP,yytext()); - } - case 219: break; - case 41: - { return new ParsedSymbol(SymbolGroup.DOUBLE,SymbolType.DOUBLE,new Double(Double.parseDouble((yytext())))); - } - case 220: break; - case 42: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.SHIFT_LEFT,yytext()); - } - case 221: break; - case 43: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.NOT_EQUAL,yytext()); - } - case 222: break; - case 44: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.LOWER_EQUAL,yytext()); - } - case 223: break; - case 45: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.SHIFT_RIGHT,yytext()); - } - case 224: break; - case 46: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.GREATER_EQUAL,yytext()); - } - case 225: break; - case 47: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.EQUALS,yytext()); - } - case 226: break; - case 48: - { return new ParsedSymbol(SymbolGroup.INTEGER,SymbolType.INTEGER,new Long(Long.parseLong(yytext(),8))); - } - case 227: break; - case 49: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_MINUS,yytext()); - } - case 228: break; - case 50: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.DECREMENT,yytext()); - } - case 229: break; - case 51: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.AS,yytext()); - } - case 230: break; - case 52: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.FULLOR,yytext()); - } - case 231: break; - case 53: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.IS,yytext()); - } - case 232: break; - case 54: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.IN,yytext()); - } - case 233: break; - case 55: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.IF,yytext()); - } - case 234: break; - case 56: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.DO,yytext()); - } - case 235: break; - case 57: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_BITAND,yytext()); - } - case 236: break; - case 58: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.AND,yytext()); - } - case 237: break; - case 59: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_BITOR,yytext()); - } - case 238: break; - case 60: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.OR,yytext()); - } - case 239: break; - case 61: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_PLUS,yytext()); - } - case 240: break; - case 62: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.INCREMENT,yytext()); - } - case 241: break; - case 63: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_XOR,yytext()); - } - case 242: break; - case 64: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_MODULO,yytext()); - } - case 243: break; - case 65: - { throw new ParseException("Illegal escape sequence \""+yytext()+"\"",yyline+1); - } - case 244: break; - case 66: - { string.append( '\"' ); - } - case 245: break; - case 67: - { char val = (char) Integer.parseInt(yytext().substring(1),8); - string.append( val ); - } - case 246: break; - case 68: - { string.append( '\\' ); - } - case 247: break; - case 69: - { string.append( '\'' ); - } - case 248: break; - case 70: - { string.append( '\b' ); - } - case 249: break; - case 71: - { string.append( '\r' ); - } - case 250: break; - case 72: - { string.append( '\n' ); - } - case 251: break; - case 73: - { string.append( '\t' ); - } - case 252: break; - case 74: - { string.append( '\f' ); - } - case 253: break; - case 75: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.REST,yytext()); - } - case 254: break; - case 76: - { string.setLength(0); - yybegin(XML); - String s=yytext(); - s=s.substring(1,s.length()-1); - if(s.contains(" ")){ - s=s.substring(0,s.indexOf(" ")); - } - xmlTagName = s; - string.append(yytext()); - } - case 255: break; - case 77: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_SHIFT_LEFT,yytext()); - } - case 256: break; - case 78: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.USHIFT_RIGHT,yytext()); - } - case 257: break; - case 79: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_SHIFT_RIGHT,yytext()); - } - case 258: break; - case 80: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.STRICT_EQUALS,yytext()); - } - case 259: break; - case 81: - { return new ParsedSymbol(SymbolGroup.INTEGER,SymbolType.INTEGER,new Long(Long.parseLong(yytext().substring(2),16))); - } - case 260: break; - case 82: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.FULLAND,yytext()); - } - case 261: break; - case 83: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.CHR,yytext()); - } - case 262: break; - case 84: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.SET,yytext()); - } - case 263: break; - case 85: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.ORD,yytext()); - } - case 264: break; - case 86: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.NEW,yytext()); - } - case 265: break; - case 87: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.TRY,yytext()); - } - case 266: break; - case 88: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.INT,yytext()); - } - case 267: break; - case 89: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.USE,yytext()); - } - case 268: break; - case 90: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.FOR,yytext()); - } - case 269: break; - case 91: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.VAR,yytext()); - } - case 270: break; - case 92: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.GET,yytext()); - } - case 271: break; - case 93: - { return new ParsedSymbol(SymbolGroup.GLOBALCONST,SymbolType.NAN,yytext()); - } - case 272: break; - case 94: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.STRICT_NOT_EQUAL,yytext()); - } - case 273: break; - case 95: - { String t=yytext(); return new ParsedSymbol(SymbolGroup.TYPENAME,SymbolType.TYPENAME,t.substring(2,t.length()-1)); - } - case 274: break; - case 96: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.ASSIGN_USHIFT_RIGHT,yytext()); - } - case 275: break; - case 97: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.EACH,yytext()); - } - case 276: break; - case 98: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.ELSE,yytext()); - } - case 277: break; - case 99: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.EVAL,yytext()); - } - case 278: break; - case 100: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.CASE,yytext()); - } - case 279: break; - case 101: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.CALL,yytext()); - } - case 280: break; - case 102: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.STOP,yytext()); - } - case 281: break; - case 103: - { return new ParsedSymbol(SymbolGroup.GLOBALCONST,SymbolType.NULL,yytext()); - } - case 282: break; - case 104: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.TRUE,yytext()); - } - case 283: break; - case 105: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.THIS,yytext()); - } - case 284: break; - case 106: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.WITH,yytext()); - } - case 285: break; - case 107: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PLAY,yytext()); - } - case 286: break; - case 108: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.VOID,yytext()); - } - case 287: break; - case 109: - { string.append( yytext() ); - String endtagname=yytext(); - endtagname=endtagname.substring(2,endtagname.length()-1); - if(endtagname.equals(xmlTagName)){ - yybegin(YYINITIAL); - return new ParsedSymbol(SymbolGroup.XML,SymbolType.XML, string.toString()); - } - } - case 288: break; - case 110: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.BREAK,yytext()); - } - case 289: break; - case 111: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.CATCH,yytext()); - } - case 290: break; - case 112: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.CONST,yytext()); - } - case 291: break; - case 113: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.CLASS,yytext()); - } - case 292: break; - case 114: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.SUPER,yytext()); - } - case 293: break; - case 115: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.TRACE,yytext()); - } - case 294: break; - case 116: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.THROW,yytext()); - } - case 295: break; - case 117: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.FALSE,yytext()); - } - case 296: break; - case 118: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.WHILE,yytext()); - } - case 297: break; - case 119: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PRINT,yytext()); - } - case 298: break; - case 120: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.MBCHR,yytext()); - } - case 299: break; - case 121: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.MBORD,yytext()); - } - case 300: break; - case 122: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.RETURN,yytext()); - } - case 301: break; - case 123: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.RANDOM,yytext()); - } - case 302: break; - case 124: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.STATIC,yytext()); - } - case 303: break; - case 125: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.SUBSTR,yytext()); - } - case 304: break; - case 126: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.SWITCH,yytext()); - } - case 305: break; - case 127: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.TYPEOF,yytext()); - } - case 306: break; - case 128: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.IMPORT,yytext()); - } - case 307: break; - case 129: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.DELETE,yytext()); - } - case 308: break; - case 130: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.LENGTH,yytext()); - } - case 309: break; - case 131: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.PUBLIC,yytext()); - } - case 310: break; - case 132: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.GETURL,yytext()); - } - case 311: break; - case 133: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.STRING_OP,yytext()); - } - case 312: break; - case 134: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.NUMBER_OP,yytext()); - } - case 313: break; - case 135: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.EXTENDS,yytext()); - } - case 314: break; - case 136: - { return new ParsedSymbol(SymbolGroup.GLOBALCONST,SymbolType.NEWLINE,yytext()); - } - case 315: break; - case 137: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.DEFAULT,yytext()); - } - case 316: break; - case 138: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.DYNAMIC,yytext()); - } - case 317: break; - case 139: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.FINALLY,yytext()); - } - case 318: break; - case 140: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.PRIVATE,yytext()); - } - case 319: break; - case 141: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.PACKAGE,yytext()); - } - case 320: break; - case 142: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.CONTINUE,yytext()); - } - case 321: break; - case 143: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.STOPDRAG,yytext()); - } - case 322: break; - case 144: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.OVERRIDE,yytext()); - } - case 323: break; - case 145: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.INTERNAL,yytext()); - } - case 324: break; - case 146: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.FUNCTION,yytext()); - } - case 325: break; - case 147: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PRINTNUM,yytext()); - } - case 326: break; - case 148: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.MBLENGTH,yytext()); - } - case 327: break; - case 149: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.GETTIMER,yytext()); - } - case 328: break; - case 150: - { return new ParsedSymbol(SymbolGroup.GLOBALCONST,SymbolType.INFINITY,yytext()); - } - case 329: break; - case 151: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.STARTDRAG,yytext()); - } - case 330: break; - case 152: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.NEXTFRAME,yytext()); - } - case 331: break; - case 153: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.NAMESPACE,yytext()); - } - case 332: break; - case 154: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.INTERFACE,yytext()); - } - case 333: break; - case 155: - { return new ParsedSymbol(SymbolGroup.GLOBALCONST,SymbolType.UNDEFINED,yytext()); - } - case 334: break; - case 156: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.FSCOMMAND,yytext()); - } - case 335: break; - case 157: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.LOADMOVIE,yytext()); - } - case 336: break; - case 158: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PREVFRAME,yytext()); - } - case 337: break; - case 159: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.PROTECTED,yytext()); - } - case 338: break; - case 160: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.TELLTARGET,yytext()); - } - case 339: break; - case 161: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.TARGETPATH,yytext()); - } - case 340: break; - case 162: - { return new ParsedSymbol(SymbolGroup.OPERATOR,SymbolType.INSTANCEOF,yytext()); - } - case 341: break; - case 163: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.IMPLEMENTS,yytext()); - } - case 342: break; - case 164: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.GETVERSION,yytext()); - } - case 343: break; - case 165: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.UNLOADMOVIE,yytext()); - } - case 344: break; - case 166: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.MBSUBSTRING,yytext()); - } - case 345: break; - case 167: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.GOTOANDSTOP,yytext()); - } - case 346: break; - case 168: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.GOTOANDPLAY,yytext()); - } - case 347: break; - case 169: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.LOADMOVIENUM,yytext()); - } - case 348: break; - case 170: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.STOPALLSOUNDS,yytext()); - } - case 349: break; - case 171: - { return new ParsedSymbol(SymbolGroup.KEYWORD,SymbolType.IFFRAMELOADED,yytext()); - } - case 350: break; - case 172: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.LOADVARIABLES,yytext()); - } - case 351: break; - case 173: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PRINTASBITMAP,yytext()); - } - case 352: break; - case 174: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.UNLOADMOVIENUM,yytext()); - } - case 353: break; - case 175: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.REMOVEMOVIECLIP,yytext()); - } - case 354: break; - case 176: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.LOADVARIABLESNUM,yytext()); - } - case 355: break; - case 177: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.PRINTASBITMAPNUM,yytext()); - } - case 356: break; - case 178: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.TOGGLEHIGHQUALITY,yytext()); - } - case 357: break; - case 179: - { return new ParsedSymbol(SymbolGroup.GLOBALFUNC,SymbolType.DUPLICATEMOVIECLIP,yytext()); - } - case 358: break; - default: - if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { - zzAtEOF = true; - { - return new ParsedSymbol(SymbolGroup.EOF,SymbolType.EOF,null); - } - } - else { - zzScanError(ZZ_NO_MATCH); - } - } - } - } - - -} +/* The following code was generated by JFlex 1.5.0-SNAPSHOT */ + +/* + * Copyright (C) 2010-2014 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.action.parser.script; + +import com.jpexs.decompiler.flash.action.parser.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * This class is a scanner generated by + * JFlex 1.5.0-SNAPSHOT from the + * specification file + * D:/Dropbox/Programovani/JavaSE/FFDec/src/com/jpexs/decompiler/flash/action/parser/script/actionscript.flex + */ +public final class ActionScriptLexer { + + /** + * This character denotes the end of file + */ + public static final int YYEOF = -1; + + /** + * initial size of the lookahead buffer + */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** + * lexical states + */ + public static final int YYINITIAL = 0; + public static final int STRING = 2; + public static final int CHARLITERAL = 4; + public static final int XMLSTARTTAG = 6; + public static final int XML = 8; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED + = "\11\7\1\3\1\2\1\120\1\121\1\1\16\7\4\0\1\14\1\107" + + "\1\16\1\0\1\6\1\116\1\112\1\30\1\77\1\100\1\5\1\114" + + "\1\106\1\26\1\11\1\4\1\17\3\23\4\24\2\20\1\10\1\105" + + "\1\12\1\15\1\13\1\111\1\117\1\61\1\22\1\71\1\72\1\25" + + "\1\63\1\6\1\65\1\76\2\6\1\67\1\70\1\75\1\6\1\74" + + "\1\66\1\6\1\62\1\64\1\60\1\73\1\6\1\21\2\6\1\103" + + "\1\27\1\104\1\115\1\6\1\0\1\34\1\31\1\36\1\45\1\33" + + "\1\46\1\57\1\51\1\43\1\6\1\35\1\47\1\54\1\41\1\40" + + "\1\52\1\6\1\32\1\37\1\42\1\44\1\55\1\50\1\56\1\53" + + "\1\6\1\101\1\113\1\102\1\110\6\7\1\122\32\7\2\0\4\6" + + "\4\0\1\6\2\0\1\7\7\0\1\6\4\0\1\6\5\0\27\6" + + "\1\0\37\6\1\0\u01ca\6\4\0\14\6\16\0\5\6\7\0\1\6" + + "\1\0\1\6\21\0\160\7\5\6\1\0\2\6\2\0\4\6\10\0" + + "\1\6\1\0\3\6\1\0\1\6\1\0\24\6\1\0\123\6\1\0" + + "\213\6\1\0\5\7\2\0\236\6\11\0\46\6\2\0\1\6\7\0" + + "\47\6\11\0\55\7\1\0\1\7\1\0\2\7\1\0\2\7\1\0" + + "\1\7\10\0\33\6\5\0\3\6\15\0\4\7\7\0\1\6\4\0" + + "\13\7\5\0\53\6\37\7\4\0\2\6\1\7\143\6\1\0\1\6" + + "\10\7\1\0\6\7\2\6\2\7\1\0\4\7\2\6\12\7\3\6" + + "\2\0\1\6\17\0\1\7\1\6\1\7\36\6\33\7\2\0\131\6" + + "\13\7\1\6\16\0\12\7\41\6\11\7\2\6\4\0\1\6\5\0" + + "\26\6\4\7\1\6\11\7\1\6\3\7\1\6\5\7\22\0\31\6" + + "\3\7\244\0\4\7\66\6\3\7\1\6\22\7\1\6\7\7\12\6" + + "\2\7\2\0\12\7\1\0\7\6\1\0\7\6\1\0\3\7\1\0" + + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\1\6\3\0" + + "\4\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\1\6" + + "\10\0\1\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\4\6" + + "\7\0\1\6\5\0\3\7\1\0\6\6\4\0\2\6\2\0\26\6" + + "\1\0\7\6\1\0\2\6\1\0\2\6\1\0\2\6\2\0\1\7" + + "\1\0\5\7\4\0\2\7\2\0\3\7\3\0\1\7\7\0\4\6" + + "\1\0\1\6\7\0\14\7\3\6\1\7\13\0\3\7\1\0\11\6" + + "\1\0\3\6\1\0\26\6\1\0\7\6\1\0\2\6\1\0\5\6" + + "\2\0\1\7\1\6\10\7\1\0\3\7\1\0\3\7\2\0\1\6" + + "\17\0\2\6\2\7\2\0\12\7\1\0\1\6\17\0\3\7\1\0" + + "\10\6\2\0\2\6\2\0\26\6\1\0\7\6\1\0\2\6\1\0" + + "\5\6\2\0\1\7\1\6\7\7\2\0\2\7\2\0\3\7\10\0" + + "\2\7\4\0\2\6\1\0\3\6\2\7\2\0\12\7\1\0\1\6" + + "\20\0\1\7\1\6\1\0\6\6\3\0\3\6\1\0\4\6\3\0" + + "\2\6\1\0\1\6\1\0\2\6\3\0\2\6\3\0\3\6\3\0" + + "\14\6\4\0\5\7\3\0\3\7\1\0\4\7\2\0\1\6\6\0" + + "\1\7\16\0\12\7\11\0\1\6\7\0\3\7\1\0\10\6\1\0" + + "\3\6\1\0\27\6\1\0\12\6\1\0\5\6\3\0\1\6\7\7" + + "\1\0\3\7\1\0\4\7\7\0\2\7\1\0\2\6\6\0\2\6" + + "\2\7\2\0\12\7\22\0\2\7\1\0\10\6\1\0\3\6\1\0" + + "\27\6\1\0\12\6\1\0\5\6\2\0\1\7\1\6\7\7\1\0" + + "\3\7\1\0\4\7\7\0\2\7\7\0\1\6\1\0\2\6\2\7" + + "\2\0\12\7\1\0\2\6\17\0\2\7\1\0\10\6\1\0\3\6" + + "\1\0\51\6\2\0\1\6\7\7\1\0\3\7\1\0\4\7\1\6" + + "\10\0\1\7\10\0\2\6\2\7\2\0\12\7\12\0\6\6\2\0" + + "\2\7\1\0\22\6\3\0\30\6\1\0\11\6\1\0\1\6\2\0" + + "\7\6\3\0\1\7\4\0\6\7\1\0\1\7\1\0\10\7\22\0" + + "\2\7\15\0\60\6\1\7\2\6\7\7\4\0\10\6\10\7\1\0" + + "\12\7\47\0\2\6\1\0\1\6\2\0\2\6\1\0\1\6\2\0" + + "\1\6\6\0\4\6\1\0\7\6\1\0\3\6\1\0\1\6\1\0" + + "\1\6\2\0\2\6\1\0\4\6\1\7\2\6\6\7\1\0\2\7" + + "\1\6\2\0\5\6\1\0\1\6\1\0\6\7\2\0\12\7\2\0" + + "\2\6\42\0\1\6\27\0\2\7\6\0\12\7\13\0\1\7\1\0" + + "\1\7\1\0\1\7\4\0\2\7\10\6\1\0\44\6\4\0\24\7" + + "\1\0\2\7\5\6\13\7\1\0\44\7\11\0\1\7\71\0\53\6" + + "\24\7\1\6\12\7\6\0\6\6\4\7\4\6\3\7\1\6\3\7" + + "\2\6\7\7\3\6\4\7\15\6\14\7\1\6\17\7\2\0\46\6" + + "\12\0\53\6\1\0\1\6\3\0\u0149\6\1\0\4\6\2\0\7\6" + + "\1\0\1\6\1\0\4\6\2\0\51\6\1\0\4\6\2\0\41\6" + + "\1\0\4\6\2\0\7\6\1\0\1\6\1\0\4\6\2\0\17\6" + + "\1\0\71\6\1\0\4\6\2\0\103\6\2\0\3\7\40\0\20\6" + + "\20\0\125\6\14\0\u026c\6\2\0\21\6\1\0\32\6\5\0\113\6" + + "\3\0\3\6\17\0\15\6\1\0\4\6\3\7\13\0\22\6\3\7" + + "\13\0\22\6\2\7\14\0\15\6\1\0\3\6\1\0\2\7\14\0" + + "\64\6\40\7\3\0\1\6\3\0\2\6\1\7\2\0\12\7\41\0" + + "\3\7\2\0\12\7\6\0\130\6\10\0\51\6\1\7\1\6\5\0" + + "\106\6\12\0\35\6\3\0\14\7\4\0\14\7\12\0\12\7\36\6" + + "\2\0\5\6\13\0\54\6\4\0\21\7\7\6\2\7\6\0\12\7" + + "\46\0\27\6\5\7\4\0\65\6\12\7\1\0\35\7\2\0\13\7" + + "\6\0\12\7\15\0\1\6\130\0\5\7\57\6\21\7\7\6\4\0" + + "\12\7\21\0\11\7\14\0\3\7\36\6\12\7\3\0\2\6\12\7" + + "\6\0\46\6\16\7\14\0\44\6\24\7\10\0\12\7\3\0\3\6" + + "\12\7\44\6\122\0\3\7\1\0\25\7\4\6\1\7\4\6\1\7" + + "\15\0\300\6\47\7\25\0\4\7\u0116\6\2\0\6\6\2\0\46\6" + + "\2\0\6\6\2\0\10\6\1\0\1\6\1\0\1\6\1\0\1\6" + + "\1\0\37\6\2\0\65\6\1\0\7\6\1\0\1\6\3\0\3\6" + + "\1\0\7\6\3\0\4\6\2\0\6\6\4\0\15\6\5\0\3\6" + + "\1\0\7\6\16\0\5\7\30\0\1\120\1\120\5\7\20\0\2\6" + + "\23\0\1\6\13\0\5\7\5\0\6\7\1\0\1\6\15\0\1\6" + + "\20\0\15\6\3\0\32\6\26\0\15\7\4\0\1\7\3\0\14\7" + + "\21\0\1\6\4\0\1\6\2\0\12\6\1\0\1\6\3\0\5\6" + + "\6\0\1\6\1\0\1\6\1\0\1\6\1\0\4\6\1\0\13\6" + + "\2\0\4\6\5\0\5\6\4\0\1\6\21\0\51\6\u0a77\0\57\6" + + "\1\0\57\6\1\0\205\6\6\0\4\6\3\7\16\0\46\6\12\0" + + "\66\6\11\0\1\6\17\0\1\7\27\6\11\0\7\6\1\0\7\6" + + "\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6\1\0\7\6" + + "\1\0\7\6\1\0\40\7\57\0\1\6\u01d5\0\3\6\31\0\11\6" + + "\6\7\1\0\5\6\2\0\5\6\4\0\126\6\2\0\2\7\2\0" + + "\3\6\1\0\132\6\1\0\4\6\5\0\51\6\3\0\136\6\21\0" + + "\33\6\65\0\20\6\u0200\0\u19b6\6\112\0\u51cc\6\64\0\u048d\6\103\0" + + "\56\6\2\0\u010d\6\3\0\20\6\12\7\2\6\24\0\57\6\1\7" + + "\14\0\2\7\1\0\31\6\10\0\120\6\2\7\45\0\11\6\2\0" + + "\147\6\2\0\4\6\1\0\2\6\16\0\12\6\120\0\10\6\1\7" + + "\3\6\1\7\4\6\1\7\27\6\5\7\20\0\1\6\7\0\64\6" + + "\14\0\2\7\62\6\21\7\13\0\12\7\6\0\22\7\6\6\3\0" + + "\1\6\4\0\12\7\34\6\10\7\2\0\27\6\15\7\14\0\35\6" + + "\3\0\4\7\57\6\16\7\16\0\1\6\12\7\46\0\51\6\16\7" + + "\11\0\3\6\1\7\10\6\2\7\2\0\12\7\6\0\27\6\3\0" + + "\1\6\1\7\4\0\60\6\1\7\1\6\3\7\2\6\2\7\5\6" + + "\2\7\1\6\1\7\1\6\30\0\3\6\43\0\6\6\2\0\6\6" + + "\2\0\6\6\11\0\7\6\1\0\7\6\221\0\43\6\10\7\1\0" + + "\2\7\2\0\12\7\6\0\u2ba4\6\14\0\27\6\4\0\61\6\u2104\0" + + "\u012e\6\2\0\76\6\2\0\152\6\46\0\7\6\14\0\5\6\5\0" + + "\1\6\1\7\12\6\1\0\15\6\1\0\5\6\1\0\1\6\1\0" + + "\2\6\1\0\2\6\1\0\154\6\41\0\u016b\6\22\0\100\6\2\0" + + "\66\6\50\0\15\6\3\0\20\7\20\0\7\7\14\0\2\6\30\0" + + "\3\6\31\0\1\6\6\0\5\6\1\0\207\6\2\0\1\7\4\0" + + "\1\6\13\0\12\7\7\0\32\6\4\0\1\6\1\0\32\6\13\0" + + "\131\6\3\0\6\6\2\0\6\6\2\0\6\6\2\0\3\6\3\0" + + "\2\6\3\0\2\6\22\0\3\7\4\0"; + + /** + * Translates characters to character classes + */ + private static final char[] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int[] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 + = "\5\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7" + + "\1\10\1\11\1\12\1\13\1\14\2\15\1\16\1\17" + + "\26\6\1\20\1\21\1\22\1\23\1\24\1\25\1\26" + + "\1\27\1\30\1\31\1\32\1\33\1\34\1\35\1\36" + + "\1\37\1\40\1\41\2\42\1\43\1\1\1\41\2\44" + + "\1\41\1\1\1\45\3\41\1\3\1\0\1\46\1\47" + + "\1\50\2\0\1\51\1\0\1\52\1\53\1\54\1\55" + + "\1\56\1\57\1\60\1\51\1\0\2\60\1\0\1\61" + + "\1\62\7\6\1\63\11\6\1\64\12\6\1\65\1\66" + + "\1\67\4\6\1\70\30\6\1\53\1\71\1\72\1\73" + + "\1\74\1\75\1\76\1\77\1\100\1\101\1\102\2\103" + + "\1\104\1\105\1\106\1\107\1\110\1\111\1\112\6\0" + + "\2\3\2\0\1\113\3\0\1\114\1\0\1\115\1\116" + + "\1\117\1\120\2\121\1\60\1\51\1\0\10\6\1\122" + + "\5\6\1\123\1\124\5\6\1\125\1\6\1\126\5\6" + + "\1\127\7\6\1\130\2\6\1\131\10\6\1\132\20\6" + + "\1\133\1\6\1\134\2\6\1\135\2\6\1\136\1\103" + + "\7\0\1\137\5\0\1\140\1\121\1\60\4\6\1\141" + + "\1\142\1\143\1\6\1\144\1\6\1\145\5\6\1\146" + + "\7\6\1\147\1\6\1\150\4\6\1\151\22\6\1\152" + + "\7\6\1\153\4\6\1\154\7\6\1\41\1\0\1\155" + + "\12\0\1\121\1\60\1\156\4\6\1\157\1\160\1\6" + + "\1\161\5\6\1\162\5\6\1\163\3\6\1\164\14\6" + + "\1\165\6\6\1\166\2\6\1\167\3\6\1\170\1\6" + + "\1\171\10\6\10\0\1\121\1\60\1\172\1\6\1\173" + + "\3\6\1\174\2\6\1\175\1\176\7\6\1\177\4\6" + + "\1\200\4\6\1\201\5\6\1\202\10\6\1\203\2\6" + + "\1\204\3\6\1\205\1\206\1\6\2\0\1\114\1\121" + + "\1\60\1\6\1\207\5\6\1\210\14\6\1\211\1\6" + + "\1\212\1\6\1\213\7\6\1\214\1\215\6\6\1\41" + + "\1\121\1\60\1\6\1\216\2\6\1\217\1\220\6\6" + + "\1\221\7\6\1\222\5\6\1\223\1\6\1\224\1\225" + + "\3\6\1\226\1\121\1\60\1\6\1\227\1\6\1\230" + + "\1\231\4\6\1\232\2\6\1\233\2\6\1\234\1\235" + + "\1\6\1\236\1\237\5\6\1\121\1\60\2\6\1\240" + + "\1\241\1\6\1\242\1\6\1\243\6\6\1\244\2\6" + + "\1\60\4\6\1\245\4\6\1\246\1\247\1\250\1\60" + + "\6\6\1\251\2\6\1\60\1\6\1\252\1\6\1\253" + + "\2\6\1\254\1\255\1\60\2\6\1\256\3\6\1\60" + + "\1\257\4\6\1\60\2\6\1\260\1\261\1\262\1\6" + + "\1\263"; + + private static int[] zzUnpackAction() { + int[] result = new int[687]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + + int j = offset; /* index in unpacked array */ + + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do { + result[j++] = value; + } while (--count > 0); + } + return j; + } + + /** + * Translates a state to a row index in the transition table + */ + private static final int[] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 + = "\0\0\0\123\0\246\0\371\0\u014c\0\u019f\0\u01f2\0\u019f" + + "\0\u0245\0\u0298\0\u02eb\0\u033e\0\u0391\0\u03e4\0\u0437\0\u048a" + + "\0\u04dd\0\u019f\0\u0530\0\u0583\0\u05d6\0\u019f\0\u0629\0\u067c" + + "\0\u06cf\0\u0722\0\u0775\0\u07c8\0\u081b\0\u086e\0\u08c1\0\u0914" + + "\0\u0967\0\u09ba\0\u0a0d\0\u0a60\0\u0ab3\0\u0b06\0\u0b59\0\u0bac" + + "\0\u0bff\0\u0c52\0\u0ca5\0\u0cf8\0\u019f\0\u019f\0\u019f\0\u019f" + + "\0\u019f\0\u019f\0\u019f\0\u019f\0\u0d4b\0\u019f\0\u019f\0\u0d9e" + + "\0\u0df1\0\u0e44\0\u0e97\0\u0eea\0\u019f\0\u0f3d\0\u0f90\0\u019f" + + "\0\u019f\0\u0fe3\0\u1036\0\u1089\0\u019f\0\u10dc\0\u112f\0\u019f" + + "\0\u1182\0\u019f\0\u11d5\0\u1228\0\u127b\0\u019f\0\u019f\0\u019f" + + "\0\u12ce\0\u1321\0\u1374\0\u13c7\0\u141a\0\u019f\0\u019f\0\u146d" + + "\0\u019f\0\u14c0\0\u1513\0\u1566\0\u15b9\0\u160c\0\u165f\0\u16b2" + + "\0\u019f\0\u019f\0\u1705\0\u1758\0\u17ab\0\u17fe\0\u1851\0\u18a4" + + "\0\u18f7\0\u033e\0\u194a\0\u199d\0\u19f0\0\u1a43\0\u1a96\0\u1ae9" + + "\0\u1b3c\0\u1b8f\0\u1be2\0\u1c35\0\u1c88\0\u1cdb\0\u1d2e\0\u1d81" + + "\0\u1dd4\0\u1e27\0\u1e7a\0\u1ecd\0\u1f20\0\u1f73\0\u033e\0\u1fc6" + + "\0\u2019\0\u206c\0\u20bf\0\u2112\0\u2165\0\u033e\0\u21b8\0\u220b" + + "\0\u225e\0\u22b1\0\u2304\0\u2357\0\u23aa\0\u23fd\0\u2450\0\u24a3" + + "\0\u24f6\0\u2549\0\u259c\0\u25ef\0\u2642\0\u2695\0\u26e8\0\u273b" + + "\0\u278e\0\u27e1\0\u2834\0\u2887\0\u28da\0\u292d\0\u2980\0\u019f" + + "\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f" + + "\0\u019f\0\u29d3\0\u2a26\0\u019f\0\u019f\0\u019f\0\u019f\0\u019f" + + "\0\u019f\0\u019f\0\u112f\0\u2a79\0\u2acc\0\u2b1f\0\u2b72\0\u2bc5" + + "\0\u2c18\0\u019f\0\u2c6b\0\u2cbe\0\u019f\0\u2d11\0\u2d64\0\u2db7" + + "\0\u019f\0\u2e0a\0\u019f\0\u2e5d\0\u019f\0\u019f\0\u15b9\0\u2eb0" + + "\0\u2f03\0\u2f56\0\u2f56\0\u2fa9\0\u2ffc\0\u304f\0\u30a2\0\u30f5" + + "\0\u3148\0\u319b\0\u31ee\0\u033e\0\u3241\0\u3294\0\u32e7\0\u333a" + + "\0\u338d\0\u033e\0\u033e\0\u33e0\0\u3433\0\u3486\0\u34d9\0\u352c" + + "\0\u033e\0\u357f\0\u35d2\0\u3625\0\u3678\0\u36cb\0\u371e\0\u3771" + + "\0\u033e\0\u37c4\0\u3817\0\u386a\0\u38bd\0\u3910\0\u3963\0\u39b6" + + "\0\u3a09\0\u3a5c\0\u3aaf\0\u033e\0\u3b02\0\u3b55\0\u3ba8\0\u3bfb" + + "\0\u3c4e\0\u3ca1\0\u3cf4\0\u3d47\0\u033e\0\u3d9a\0\u3ded\0\u3e40" + + "\0\u3e93\0\u3ee6\0\u3f39\0\u3f8c\0\u3fdf\0\u4032\0\u4085\0\u40d8" + + "\0\u412b\0\u417e\0\u41d1\0\u4224\0\u4277\0\u033e\0\u42ca\0\u431d" + + "\0\u4370\0\u43c3\0\u033e\0\u4416\0\u4469\0\u019f\0\u019f\0\u44bc" + + "\0\u450f\0\u4562\0\u45b5\0\u4608\0\u465b\0\u46ae\0\u019f\0\u4701" + + "\0\u4754\0\u47a7\0\u47fa\0\u484d\0\u019f\0\u48a0\0\u48f3\0\u4946" + + "\0\u4999\0\u49ec\0\u4a3f\0\u033e\0\u033e\0\u033e\0\u4a92\0\u033e" + + "\0\u4ae5\0\u033e\0\u4b38\0\u4b8b\0\u4bde\0\u4c31\0\u4c84\0\u4cd7" + + "\0\u4d2a\0\u4d7d\0\u4dd0\0\u4e23\0\u4e76\0\u4ec9\0\u4f1c\0\u033e" + + "\0\u4f6f\0\u033e\0\u4fc2\0\u5015\0\u5068\0\u50bb\0\u033e\0\u510e" + + "\0\u5161\0\u51b4\0\u5207\0\u525a\0\u52ad\0\u5300\0\u5353\0\u53a6" + + "\0\u53f9\0\u544c\0\u549f\0\u54f2\0\u5545\0\u5598\0\u55eb\0\u563e" + + "\0\u5691\0\u033e\0\u56e4\0\u5737\0\u578a\0\u57dd\0\u5830\0\u5883" + + "\0\u58d6\0\u033e\0\u5929\0\u597c\0\u59cf\0\u5a22\0\u033e\0\u5a75" + + "\0\u5ac8\0\u5b1b\0\u5b6e\0\u5bc1\0\u5c14\0\u5c67\0\u5cba\0\u5d0d" + + "\0\u019f\0\u5d60\0\u5db3\0\u5e06\0\u5e59\0\u5eac\0\u5eff\0\u5f52" + + "\0\u5fa5\0\u5ff8\0\u604b\0\u609e\0\u60f1\0\u033e\0\u6144\0\u6197" + + "\0\u61ea\0\u623d\0\u033e\0\u033e\0\u6290\0\u033e\0\u62e3\0\u6336" + + "\0\u6389\0\u63dc\0\u642f\0\u033e\0\u6482\0\u64d5\0\u6528\0\u657b" + + "\0\u65ce\0\u033e\0\u6621\0\u6674\0\u66c7\0\u033e\0\u671a\0\u676d" + + "\0\u67c0\0\u6813\0\u6866\0\u68b9\0\u690c\0\u695f\0\u69b2\0\u6a05" + + "\0\u6a58\0\u6aab\0\u033e\0\u6afe\0\u6b51\0\u6ba4\0\u6bf7\0\u6c4a" + + "\0\u6c9d\0\u033e\0\u6cf0\0\u6d43\0\u6d96\0\u6de9\0\u6e3c\0\u6e8f" + + "\0\u033e\0\u6ee2\0\u033e\0\u6f35\0\u6f88\0\u6fdb\0\u702e\0\u7081" + + "\0\u70d4\0\u7127\0\u717a\0\u71cd\0\u7220\0\u7273\0\u72c6\0\u7319" + + "\0\u736c\0\u73bf\0\u7412\0\u7465\0\u74b8\0\u033e\0\u750b\0\u033e" + + "\0\u755e\0\u75b1\0\u7604\0\u033e\0\u7657\0\u76aa\0\u033e\0\u033e" + + "\0\u76fd\0\u7750\0\u77a3\0\u77f6\0\u7849\0\u789c\0\u78ef\0\u033e" + + "\0\u7942\0\u7995\0\u79e8\0\u7a3b\0\u033e\0\u7a8e\0\u7ae1\0\u7b34" + + "\0\u7b87\0\u033e\0\u7bda\0\u7c2d\0\u7c80\0\u7cd3\0\u7d26\0\u033e" + + "\0\u7d79\0\u7dcc\0\u7e1f\0\u7e72\0\u7ec5\0\u7f18\0\u7f6b\0\u7fbe" + + "\0\u033e\0\u8011\0\u8064\0\u033e\0\u80b7\0\u810a\0\u815d\0\u033e" + + "\0\u033e\0\u81b0\0\u8203\0\u8256\0\u5f52\0\u82a9\0\u82fc\0\u834f" + + "\0\u033e\0\u83a2\0\u83f5\0\u8448\0\u849b\0\u84ee\0\u033e\0\u8541" + + "\0\u8594\0\u85e7\0\u863a\0\u868d\0\u86e0\0\u8733\0\u8786\0\u87d9" + + "\0\u882c\0\u887f\0\u88d2\0\u033e\0\u8925\0\u033e\0\u8978\0\u033e" + + "\0\u89cb\0\u8a1e\0\u8a71\0\u8ac4\0\u8b17\0\u8b6a\0\u8bbd\0\u033e" + + "\0\u033e\0\u8c10\0\u8c63\0\u8cb6\0\u8d09\0\u8d5c\0\u8daf\0\u7273" + + "\0\u8e02\0\u8e55\0\u8ea8\0\u033e\0\u8efb\0\u8f4e\0\u033e\0\u033e" + + "\0\u8fa1\0\u8ff4\0\u9047\0\u909a\0\u90ed\0\u9140\0\u033e\0\u9193" + + "\0\u91e6\0\u9239\0\u928c\0\u92df\0\u9332\0\u9385\0\u033e\0\u93d8" + + "\0\u942b\0\u947e\0\u94d1\0\u9524\0\u033e\0\u9577\0\u033e\0\u033e" + + "\0\u95ca\0\u961d\0\u9670\0\u033e\0\u96c3\0\u9716\0\u9769\0\u033e" + + "\0\u97bc\0\u033e\0\u033e\0\u980f\0\u9862\0\u98b5\0\u9908\0\u033e" + + "\0\u995b\0\u99ae\0\u033e\0\u9a01\0\u9a54\0\u033e\0\u9aa7\0\u9afa" + + "\0\u033e\0\u033e\0\u9b4d\0\u9ba0\0\u9bf3\0\u9c46\0\u9c99\0\u019f" + + "\0\u9cec\0\u9d3f\0\u9d92\0\u033e\0\u033e\0\u9de5\0\u033e\0\u9e38" + + "\0\u033e\0\u9e8b\0\u9ede\0\u9f31\0\u9f84\0\u9fd7\0\ua02a\0\u033e" + + "\0\ua07d\0\ua0d0\0\ua123\0\ua176\0\ua1c9\0\ua21c\0\ua26f\0\ua2c2" + + "\0\ua315\0\ua368\0\ua3bb\0\ua40e\0\u033e\0\u033e\0\u033e\0\ua461" + + "\0\ua4b4\0\ua507\0\ua55a\0\ua5ad\0\ua600\0\ua653\0\u033e\0\ua6a6" + + "\0\ua6f9\0\ua74c\0\ua79f\0\u033e\0\ua7f2\0\u033e\0\ua845\0\ua898" + + "\0\ua8eb\0\ua93e\0\ua991\0\ua9e4\0\uaa37\0\u033e\0\uaa8a\0\uaadd" + + "\0\uab30\0\uab83\0\u033e\0\uabd6\0\uac29\0\uac7c\0\uaccf\0\u1566" + + "\0\uad22\0\uad75\0\u033e\0\u033e\0\u033e\0\uadc8\0\u033e"; + + private static int[] zzUnpackRowMap() { + int[] result = new int[687]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + + int j = offset; /* index in unpacked array */ + + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int[] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 + = "\1\6\1\7\1\10\1\11\1\12\1\13\1\14\1\6" + + "\1\15\1\16\1\17\1\20\1\11\1\21\1\22\1\23" + + "\1\24\2\14\2\24\1\14\1\25\1\6\1\26\1\27" + + "\1\30\1\31\1\32\1\14\1\33\1\34\1\35\1\36" + + "\1\37\1\40\1\41\1\42\1\43\1\44\1\45\1\14" + + "\1\46\1\14\1\47\1\50\1\14\1\51\2\14\1\52" + + "\12\14\1\53\1\54\1\55\1\56\1\57\1\60\1\61" + + "\1\62\1\63\1\64\1\65\1\66\1\67\1\70\1\71" + + "\1\72\1\73\1\74\1\75\1\0\1\11\1\0\1\76" + + "\1\77\1\100\13\76\1\101\10\76\1\102\73\76\1\103" + + "\1\77\1\100\24\103\1\102\1\101\72\103\1\6\1\104" + + "\1\105\1\106\2\6\1\107\4\6\1\110\1\111\4\6" + + "\2\107\2\6\1\107\3\6\46\107\21\6\1\0\1\106" + + "\1\0\1\112\1\104\1\105\7\112\1\113\105\112\130\0" + + "\1\10\123\0\1\11\10\0\1\11\104\0\1\11\5\0" + + "\1\114\1\115\7\0\1\116\122\0\1\117\113\0\2\14" + + "\7\0\7\14\3\0\46\14\23\0\1\14\10\0\1\120" + + "\123\0\1\121\1\122\4\0\2\123\2\0\2\123\104\0" + + "\1\124\3\0\1\125\1\126\1\0\1\127\3\0\2\124" + + "\2\0\1\124\3\0\46\124\37\0\1\130\1\0\1\131" + + "\122\0\1\132\116\0\1\123\5\0\1\133\1\134\1\135" + + "\1\0\1\136\1\137\1\140\5\0\1\140\22\0\1\135" + + "\55\0\1\123\5\0\2\24\2\0\2\24\1\140\5\0" + + "\1\140\104\0\1\141\10\0\1\142\102\0\2\14\7\0" + + "\7\14\3\0\1\14\1\143\44\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\144\1\145\42\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14" + + "\1\146\12\14\1\147\5\14\1\150\1\151\20\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\152" + + "\1\14\1\153\35\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\154\3\14\1\155\6\14\1\156" + + "\1\14\1\157\25\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\160\6\14\1\161\1\14\1\162" + + "\3\14\1\163\26\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\1\14\1\164\22\14\1\165\21\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\166" + + "\1\167\7\14\1\170\32\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\1\14\1\171\1\172\1\173\3\14" + + "\1\174\10\14\1\175\1\14\1\176\23\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\6\14\1\177\1\14" + + "\1\200\4\14\1\201\5\14\1\202\22\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\6\14\1\203\1\14" + + "\1\204\35\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\2\14\1\205\4\14\1\206\3\14\1\207\6\14" + + "\1\210\23\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\3\14\1\211\2\14\1\212\1\213\2\14\1\214" + + "\1\215\32\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\2\14\1\216\4\14\1\217\36\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\220\5\14" + + "\1\221\25\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\1\14\1\222\1\14\1\223\7\14\1\224\2\14" + + "\1\225\27\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\1\226\45\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\227\3\14\1\230\36\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\231" + + "\4\14\1\232\36\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\233\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\3\14\1\234\7\14\1\235" + + "\32\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\10\14\1\236\35\14\23\0\1\14\15\0\1\237\122\0" + + "\1\240\74\0\1\241\25\0\1\242\75\0\1\243\24\0" + + "\1\244\76\0\1\245\23\0\1\246\122\0\1\247\105\0" + + "\1\76\2\0\13\76\1\0\10\76\1\0\73\76\2\0" + + "\1\100\120\0\1\250\2\0\13\250\1\251\1\252\3\250" + + "\1\252\1\253\2\250\1\254\1\255\1\256\1\257\6\250" + + "\1\260\1\261\3\250\1\262\51\250\3\0\1\103\2\0" + + "\24\103\2\0\72\103\2\0\1\105\123\0\1\106\10\0" + + "\1\106\104\0\1\106\7\0\2\263\1\264\3\0\1\265" + + "\1\266\1\0\7\263\3\0\46\263\23\0\1\263\3\0" + + "\1\106\2\0\1\263\5\0\1\111\4\0\2\263\2\0" + + "\1\263\3\0\46\263\22\0\1\106\5\0\1\267\1\0" + + "\1\270\12\0\2\270\2\0\1\270\3\0\46\270\24\0" + + "\1\114\1\271\1\272\120\114\5\273\1\274\115\273\11\0" + + "\1\275\117\0\1\276\12\0\2\276\2\0\1\276\3\0" + + "\46\276\43\0\2\123\2\0\2\123\1\140\5\0\1\140" + + "\75\0\1\277\1\124\1\300\2\0\1\301\1\302\2\0" + + "\2\124\2\277\2\124\1\277\3\0\46\277\23\0\1\124" + + "\15\0\1\303\120\0\1\304\1\0\1\305\122\0\1\306" + + "\116\0\1\123\5\0\1\133\1\134\2\0\1\136\1\137" + + "\1\140\5\0\1\140\100\0\1\123\5\0\2\134\2\0" + + "\2\134\1\140\5\0\1\140\106\0\1\307\1\310\1\0" + + "\4\310\3\0\1\310\1\0\2\310\1\0\1\310\6\0" + + "\2\310\12\0\1\310\1\0\1\310\5\0\2\310\41\0" + + "\1\123\5\0\1\137\1\134\2\0\2\137\1\140\5\0" + + "\1\140\100\0\1\123\5\0\1\311\1\134\2\0\2\311" + + "\1\140\5\0\1\140\106\0\2\312\2\0\2\312\1\0" + + "\1\313\65\0\1\313\14\0\2\14\7\0\7\14\3\0" + + "\2\14\1\314\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\315\11\14\1\316\22\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\317" + + "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\5\14\1\320\40\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\6\14\1\321\37\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\3\14\1\322\42\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\323" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\14\14\1\324\31\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\6\14\1\325\2\14\1\326\4\14\1\327" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\10\14\1\330\35\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\331\42\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\1\14\1\332\44\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\333" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\334\3\14\1\335\36\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\1\336\20\14\1\337\24\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\340\33\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\14\14\1\341\31\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\342\43\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\17\14\1\343\5\14" + + "\1\344\20\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\23\14\1\345\22\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\16\14\1\346\27\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\3\14\1\347\7\14" + + "\1\350\6\14\1\351\23\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\16\14\1\352\27\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\1\14\1\353\44\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14" + + "\1\354\17\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\1\14\1\355\10\14\1\356\33\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\21\14\1\357\24\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\6\14" + + "\1\360\2\14\1\361\34\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\32\14\1\362\13\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\21\14\1\363\24\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14" + + "\1\364\43\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\14\14\1\365\1\14\1\366\27\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\15\14\1\367\1\370" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\21\14\1\371\24\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\10\14\1\372\35\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\16\14\1\373\27\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\374" + + "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\1\14\1\375\44\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\10\14\1\376\35\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\10\14\1\377\35\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u0100" + + "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\u0101\42\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u0102\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u0103\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0104" + + "\4\14\1\u0105\2\14\1\u0106\33\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\5\14\1\u0107\40\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\1\u0108\45\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14" + + "\1\u0109\42\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\5\14\1\u010a\1\u010b\1\u010c\6\14\1\u010d\27\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\14" + + "\1\u010e\44\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\12\14\1\u010f\33\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\11\14\1\u0110\34\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\11\14\1\u0111\34\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\14" + + "\1\u0112\44\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\44\14\1\u0113\1\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\23\14\1\u0114\22\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\15\14\1\u0115\30\14" + + "\23\0\1\14\15\0\1\u0116\124\0\1\253\3\0\2\253" + + "\115\0\1\u0117\3\0\2\u0117\104\0\1\u0118\12\0\2\u0118" + + "\2\0\1\u0118\3\0\46\u0118\40\0\1\265\1\266\121\0" + + "\1\266\1\0\1\u0119\112\0\1\u011a\12\0\2\u011a\2\0" + + "\1\u011a\3\0\46\u011a\32\0\1\u011b\1\270\1\u011c\2\0" + + "\1\112\1\u011d\2\0\2\270\2\u011b\2\270\1\u011b\3\0" + + "\46\u011b\23\0\1\270\2\0\1\272\120\0\5\273\1\u011e" + + "\115\273\4\0\1\272\1\274\123\0\2\276\3\0\1\u011f" + + "\3\0\7\276\3\0\46\276\23\0\1\276\6\0\2\277" + + "\1\u0120\2\0\1\301\1\u0121\1\u0122\1\0\7\277\3\0" + + "\46\277\23\0\1\277\6\0\1\u0123\12\0\2\u0123\2\0" + + "\1\u0123\3\0\46\u0123\32\0\1\u0124\5\0\1\302\4\0" + + "\2\u0124\2\0\1\u0124\3\0\46\u0124\41\0\1\u0125\124\0" + + "\2\u0126\1\0\4\u0126\3\0\1\u0126\1\0\2\u0126\1\0" + + "\1\u0126\6\0\2\u0126\12\0\1\u0126\1\0\1\u0126\5\0" + + "\2\u0126\41\0\1\123\5\0\1\u0127\1\134\2\0\2\u0127" + + "\1\140\5\0\1\140\106\0\2\312\2\0\2\312\104\0" + + "\2\14\7\0\7\14\3\0\3\14\1\u0128\42\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\13\14\1\u0129" + + "\32\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\7\14\1\u012a\36\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\14\14\1\u012b\31\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\20\14\1\u012c\25\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u012d" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\16\14\1\u012e\27\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u012f\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0130\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u0131" + + "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\16\14\1\u0132\27\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\6\14\1\u0133\2\14\1\u0134\34\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0135" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\1\14\1\u0136\7\14\1\u0137\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\21\14\1\u0138\24\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0139" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u013a\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u013b\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\1\14\1\u013c\44\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u013d" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u013e\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u013f\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\16\14\1\u0140\27\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u0141" + + "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u0142\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\16\14\1\u0143\27\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\26\14\1\u0144\17\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\26\14\1\u0145" + + "\17\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\7\14\1\u0146\36\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\6\14\1\u0147\37\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0148\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u0149" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u014a\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\1\14\1\u014b\44\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\7\14\1\u014c\6\14\1\u014d" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u014e\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\7\14\1\u014f\36\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\3\14\1\u0150\42\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0151" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\16\14\1\u0152\27\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\u0153\42\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\6\14\1\u0154\37\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0155" + + "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\u0156\42\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\5\14\1\u0157\40\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\26\14\1\u0158\17\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\14\14\1\u0159" + + "\31\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\20\14\1\u015a\25\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\16\14\1\u015b\27\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\24\14\1\u015c\21\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u015d" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\10\14\1\u015e\13\14\1\u015f\21\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\4\14\1\u0160\41\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u0161" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\22\14\1\u0162\23\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\20\14\1\u0163\25\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\13\14\1\u0164\32\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\1\14\1\u0165" + + "\44\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u0166\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\14\14\1\u0167\31\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\27\14\1\u0168\3\14\1\u0169" + + "\6\14\1\u016a\3\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\7\14\1\u016b\36\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u016c\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\1\u016d\45\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\u016e\33\14\23\0\1\14\6\0\2\u0118\4\0\1\265" + + "\1\266\1\0\7\u0118\3\0\46\u0118\23\0\1\u0118\1\u0119" + + "\2\0\13\u0119\1\u016f\104\u0119\6\0\2\u011a\1\u0170\2\0" + + "\1\u0171\3\0\7\u011a\3\0\46\u011a\23\0\1\u011a\6\0" + + "\2\u011b\1\u0172\2\0\1\112\1\u0173\1\u0174\1\0\7\u011b" + + "\3\0\46\u011b\23\0\1\u011b\6\0\1\u0175\12\0\2\u0175" + + "\2\0\1\u0175\3\0\46\u0175\32\0\1\u0176\5\0\1\u011d" + + "\4\0\2\u0176\2\0\1\u0176\3\0\46\u0176\24\0\4\273" + + "\1\272\1\u011e\115\273\6\0\1\u0177\12\0\2\u0177\2\0" + + "\1\u0177\3\0\46\u0177\32\0\1\u0124\5\0\1\u0121\1\u0122" + + "\3\0\2\u0124\2\0\1\u0124\3\0\46\u0124\40\0\1\u0122" + + "\1\0\1\u0178\112\0\1\u0179\1\u0123\3\0\1\301\1\302" + + "\2\0\2\u0123\2\u0179\2\u0123\1\u0179\3\0\46\u0179\23\0" + + "\1\u0123\6\0\2\u0124\1\u017a\3\0\1\u017b\1\u0122\1\0" + + "\7\u0124\3\0\46\u0124\23\0\1\u0124\17\0\2\u017c\1\0" + + "\4\u017c\3\0\1\u017c\1\0\2\u017c\1\0\1\u017c\6\0" + + "\2\u017c\12\0\1\u017c\1\0\1\u017c\5\0\2\u017c\41\0" + + "\1\123\5\0\1\u017d\1\134\2\0\2\u017d\1\140\5\0" + + "\1\140\75\0\2\14\7\0\7\14\3\0\4\14\1\u017e" + + "\41\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\1\14\1\u017f\44\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\24\14\1\u0180\21\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\7\14\1\u0181\36\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u0182" + + "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\20\14\1\u0183\25\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u0184\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u0185\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0186" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u0187\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\12\14\1\u0188\33\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\30\14\1\u0189\10\14\1\u018a" + + "\4\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u018b\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\1\14\1\u018c\44\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\5\14\1\u018d\40\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\1\14\1\u018e" + + "\44\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\12\14\1\u018f\33\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\32\14\1\u0190\13\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\6\14\1\u0191\37\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0192" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\33\14\1\u0193\12\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u0194\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\16\14\1\u0195\27\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\17\14\1\u0196" + + "\26\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\7\14\1\u0197\36\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\u0198\42\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\1\14\1\u0199\44\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u019a" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\1\14\1\u019b\44\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u019c\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\15\14\1\u019d\30\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u019e" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\13\14\1\u019f\32\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u01a0\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u01a1\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\23\14\1\u01a2" + + "\22\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\2\14\1\u01a3\43\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\23\14\1\u01a4\22\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\16\14\1\u01a5\27\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u01a6" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u01a7\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\37\14\1\u01a8\2\14\1\u01a9\3\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u01aa" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\32\14\1\u01ab\13\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u01ac\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\11\14\1\u01ad\34\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u01ae" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\u01af\42\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\12\14\1\u01b0\33\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\1\14\1\u01b1\44\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\1\u01b2\45\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\14\14" + + "\1\u01b3\31\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\10\14\1\u01b4\35\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\1\14\1\u01b5\44\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01b6\33\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14" + + "\1\u01b7\43\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\30\14\1\u01b8\15\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u01b9\35\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01ba\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\10\14" + + "\1\u01bb\35\14\23\0\1\14\1\u0119\2\0\11\u0119\1\u016f" + + "\1\u0119\1\u016f\104\u0119\6\0\1\u01bc\12\0\2\u01bc\2\0" + + "\1\u01bc\3\0\46\u01bc\32\0\1\u01bd\12\0\2\u01bd\2\0" + + "\1\u01bd\3\0\46\u01bd\32\0\1\u0176\5\0\1\u0173\1\u0174" + + "\3\0\2\u0176\2\0\1\u0176\3\0\46\u0176\40\0\1\u0174" + + "\1\0\1\u01be\112\0\1\u01bf\1\u0175\3\0\1\112\1\u011d" + + "\2\0\2\u0175\2\u01bf\2\u0175\1\u01bf\3\0\46\u01bf\23\0" + + "\1\u0175\6\0\2\u0176\1\u01c0\3\0\1\u01c1\1\u0174\1\0" + + "\7\u0176\3\0\46\u0176\23\0\1\u0176\6\0\1\u0179\1\u0177" + + "\3\0\1\301\1\u0121\1\u0122\1\0\2\u0177\2\u0179\2\u0177" + + "\1\u0179\3\0\46\u0179\23\0\1\u0177\1\u0178\2\0\13\u0178" + + "\1\u01c2\104\u0178\6\0\2\u0179\1\u017a\2\0\1\301\1\u0121" + + "\1\u0122\1\0\7\u0179\3\0\46\u0179\23\0\1\u0179\6\0" + + "\1\u01c3\12\0\2\u01c3\2\0\1\u01c3\3\0\46\u01c3\40\0" + + "\1\u017b\1\u0122\124\0\2\u01c4\1\0\4\u01c4\3\0\1\u01c4" + + "\1\0\2\u01c4\1\0\1\u01c4\6\0\2\u01c4\12\0\1\u01c4" + + "\1\0\1\u01c4\5\0\2\u01c4\41\0\1\123\5\0\1\u01c5" + + "\1\134\2\0\2\u01c5\1\140\5\0\1\140\75\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u01c6\35\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01c7\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14" + + "\1\u01c8\22\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\14\14\1\u01c9\31\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u01ca\35\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\41\14\1\u01cb\4\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\5\14" + + "\1\u01cc\40\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\16\14\1\u01cd\27\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\1\14\1\u01ce\44\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01cf\44\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14" + + "\1\u01d0\25\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\12\14\1\u01d1\33\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u01d2\35\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01d3\44\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\21\14" + + "\1\u01d4\24\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\3\14\1\u01d5\42\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\11\14\1\u01d6\34\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u01d7\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\15\14" + + "\1\u01d8\30\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\10\14\1\u01d9\35\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u01da\4\14\1\u01db\30\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14" + + "\1\u01dc\22\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\11\14\1\u01dd\34\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\23\14\1\u01de\22\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01df\33\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\14\14" + + "\1\u01e0\31\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\16\14\1\u01e1\27\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\u01e2\43\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\5\14\1\u01e3\40\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\u01e4\33\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\23\14\1\u01e5\22\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\16\14\1\u01e6\27\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\u01e7\33\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14" + + "\1\u01e8\25\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\7\14\1\u01e9\36\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\3\14\1\u01ea\42\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\1\14\1\u01eb\44\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\5\14" + + "\1\u01ec\40\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\30\14\1\u01ed\13\14\1\u01ee\1\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\11\14\1\u01ef\34\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14" + + "\1\u01f0\17\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\5\14\1\u01f1\40\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\6\14\1\u01f2\37\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\26\14\1\u01f3\17\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\16\14" + + "\1\u01f4\27\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\23\14\1\u01f5\22\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\1\14\1\u01f6\44\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\10\14\1\u01f7\35\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\26\14" + + "\1\u01f8\17\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\1\14\1\u01f9\44\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\12\14\1\u01fa\33\14\23\0\1\14" + + "\6\0\2\u01bc\3\0\1\u0171\3\0\7\u01bc\3\0\46\u01bc" + + "\23\0\1\u01bc\6\0\1\u01bf\1\u01bd\3\0\1\112\1\u0173" + + "\1\u0174\1\0\2\u01bd\2\u01bf\2\u01bd\1\u01bf\3\0\46\u01bf" + + "\23\0\1\u01bd\1\u01be\2\0\13\u01be\1\u01fb\104\u01be\6\0" + + "\2\u01bf\1\u01c0\2\0\1\112\1\u0173\1\u0174\1\0\7\u01bf" + + "\3\0\46\u01bf\23\0\1\u01bf\6\0\1\u01fc\12\0\2\u01fc" + + "\2\0\1\u01fc\3\0\46\u01fc\40\0\1\u01c1\1\u0174\105\0" + + "\1\u0178\2\0\10\u0178\1\u01fd\1\u01c2\1\u0178\1\u01c2\104\u0178" + + "\6\0\2\u01c3\4\0\1\u017b\1\u0122\1\0\7\u01c3\3\0" + + "\46\u01c3\23\0\1\u01c3\17\0\2\u01fe\1\0\4\u01fe\3\0" + + "\1\u01fe\1\0\2\u01fe\1\0\1\u01fe\6\0\2\u01fe\12\0" + + "\1\u01fe\1\0\1\u01fe\5\0\2\u01fe\41\0\1\123\5\0" + + "\1\u01ff\1\134\2\0\2\u01ff\1\140\5\0\1\140\75\0" + + "\2\14\7\0\7\14\3\0\37\14\1\u0200\6\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0201" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\13\14\1\u0202\32\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\1\14\1\u0203\44\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\16\14\1\u0204\27\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0205" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\14\14\1\u0206\31\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u0207\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\3\14\1\u0208\42\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0209" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\1\14\1\u020a\44\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\43\14\1\u020b\2\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\34\14\1\u020c\11\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\5\14\1\u020d" + + "\40\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\u020e\42\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\u020f\42\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0210\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0211" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\10\14\1\u0212\35\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\37\14\1\u0213\6\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\11\14\1\u0214\34\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0215" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\5\14\1\u0216\40\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\3\14\1\u0217\42\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\22\14\1\u0218\23\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0219" + + "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\24\14\1\u021a\21\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\1\14\1\u021b\44\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\3\14\1\u021c\42\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\11\14\1\u021d" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\6\14\1\u021e\37\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\13\14\1\u021f\32\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0220\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\2\14\1\u0221" + + "\43\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u0222\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u0223\34\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0224\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0225" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\14\14\1\u0226\31\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\11\14\1\u0227\34\14\23\0\1\14\1\u01be" + + "\2\0\10\u01be\1\u0228\1\u01fb\1\u01be\1\u01fb\104\u01be\6\0" + + "\2\u01fc\4\0\1\u01c1\1\u0174\1\0\7\u01fc\3\0\46\u01fc" + + "\23\0\1\u01fc\17\0\2\u0229\1\0\4\u0229\3\0\1\u0229" + + "\1\0\2\u0229\1\0\1\u0229\6\0\2\u0229\12\0\1\u0229" + + "\1\0\1\u0229\5\0\2\u0229\41\0\1\123\5\0\1\u022a" + + "\1\134\2\0\2\u022a\1\140\5\0\1\140\75\0\2\14" + + "\7\0\7\14\3\0\7\14\1\u022b\36\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u022c\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\3\14" + + "\1\u022d\42\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\31\14\1\u022e\14\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\26\14\1\u022f\17\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0230\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14" + + "\1\u0231\22\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\5\14\1\u0232\40\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\26\14\1\u0233\17\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0234\42\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\u0235\33\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\2\14\1\u0236\43\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\16\14\1\u0237\27\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\5\14\1\u0238\40\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\36\14" + + "\1\u0239\7\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\10\14\1\u023a\35\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\u023b\43\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\7\14\1\u023c\36\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\11\14" + + "\1\u023d\34\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\10\14\1\u023e\35\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\10\14\1\u023f\35\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\u0240\33\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\u0241\33\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\23\14\1\u0242\22\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\u0243\43\14\23\0\1\14" + + "\6\0\2\14\7\0\3\14\1\u0244\3\14\3\0\46\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\23\14" + + "\1\u0245\22\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\1\14\1\u0246\44\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\20\14\1\u0247\25\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\1\14\1\u0248\44\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\12\14" + + "\1\u0249\33\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\31\14\1\u024a\11\14\1\u024b\2\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\22\14\1\u024c\23\14" + + "\23\0\1\14\17\0\2\u024d\1\0\4\u024d\3\0\1\u024d" + + "\1\0\2\u024d\1\0\1\u024d\6\0\2\u024d\12\0\1\u024d" + + "\1\0\1\u024d\5\0\2\u024d\41\0\1\123\5\0\1\u024e" + + "\1\134\2\0\2\u024e\1\140\5\0\1\140\75\0\2\14" + + "\7\0\7\14\3\0\24\14\1\u024f\21\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\26\14\1\u0250\17\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14" + + "\1\u0251\36\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\2\14\1\u0252\43\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\u0253\43\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0254\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\11\14" + + "\1\u0255\34\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\26\14\1\u0256\17\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\7\14\1\u0257\36\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\2\14\1\u0258\43\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14" + + "\1\u0259\36\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\11\14\1\u025a\34\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\14\14\1\u025b\31\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\24\14\1\u025c\21\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14" + + "\1\u025d\43\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\14\14\1\u025e\31\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\2\14\1\u025f\43\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0260\42\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\2\14" + + "\1\u0261\43\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\14\14\1\u0262\31\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\12\14\1\u0263\33\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\12\14\1\u0264\33\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\7\14" + + "\1\u0265\36\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\11\14\1\u0266\34\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\16\14\1\u0267\27\14\23\0\1\14" + + "\17\0\2\u0268\1\0\4\u0268\3\0\1\u0268\1\0\2\u0268" + + "\1\0\1\u0268\6\0\2\u0268\12\0\1\u0268\1\0\1\u0268" + + "\5\0\2\u0268\41\0\1\123\5\0\1\u0269\1\134\2\0" + + "\2\u0269\1\140\5\0\1\140\75\0\2\14\7\0\7\14" + + "\3\0\12\14\1\u026a\33\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\13\14\1\u026b\32\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\11\14\1\u026c\34\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\20\14" + + "\1\u026d\25\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\20\14\1\u026e\25\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\15\14\1\u026f\30\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\3\14\1\u0270\42\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\6\14" + + "\1\u0271\37\14\23\0\1\14\6\0\2\14\7\0\7\14" + + "\3\0\12\14\1\u0272\33\14\23\0\1\14\6\0\2\14" + + "\7\0\7\14\3\0\37\14\1\u0273\6\14\23\0\1\14" + + "\6\0\2\14\7\0\7\14\3\0\44\14\1\u0274\1\14" + + "\23\0\1\14\6\0\2\14\7\0\7\14\3\0\1\u0275" + + "\45\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\11\14\1\u0276\34\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\10\14\1\u0277\35\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\10\14\1\u0278\35\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0279" + + "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\3\14\1\u027a\42\14\23\0\1\14\11\0\1\123\5\0" + + "\1\u027b\1\134\2\0\2\u027b\1\140\5\0\1\140\75\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u027c\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\10\14\1\u027d" + + "\35\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\35\14\1\u027e\10\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\14\14\1\u027f\31\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0280\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\7\14\1\u0281" + + "\36\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\13\14\1\u0282\32\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\16\14\1\u0283\27\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\23\14\1\u0284\22\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\26\14\1\u0285" + + "\17\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\21\14\1\u0286\24\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\22\14\1\u0287\23\14\23\0\1\14\11\0" + + "\1\123\5\0\1\u0288\1\134\2\0\2\u0288\1\140\5\0" + + "\1\140\75\0\2\14\7\0\7\14\3\0\40\14\1\u0289" + + "\5\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\14\14\1\u028a\31\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\13\14\1\u028b\32\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u028c\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\44\14\1\u028d" + + "\1\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\24\14\1\u028e\21\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\23\14\1\u028f\22\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\2\14\1\u0290\43\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0291" + + "\42\14\23\0\1\14\11\0\1\123\5\0\1\u0292\1\134" + + "\2\0\2\u0292\1\140\5\0\1\140\75\0\2\14\7\0" + + "\7\14\3\0\16\14\1\u0293\27\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\6\14\1\u0294\37\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\3\14\1\u0295" + + "\42\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\14\14\1\u0296\31\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\13\14\1\u0297\32\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u0298\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\6\14\1\u0299" + + "\37\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\21\14\1\u029a\24\14\23\0\1\14\11\0\1\123\5\0" + + "\1\u029b\1\134\2\0\2\u029b\1\140\5\0\1\140\75\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u029c\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\16\14\1\u029d" + + "\27\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\23\14\1\u029e\22\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\2\14\1\u029f\43\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\44\14\1\u02a0\1\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\44\14\1\u02a1" + + "\1\14\23\0\1\14\11\0\1\123\5\0\1\u02a2\1\134" + + "\2\0\2\u02a2\1\140\5\0\1\140\75\0\2\14\7\0" + + "\7\14\3\0\21\14\1\u02a3\24\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\12\14\1\u02a4\33\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\40\14\1\u02a5" + + "\5\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\13\14\1\u02a6\32\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\13\14\1\u02a7\32\14\23\0\1\14\11\0" + + "\1\123\5\0\1\u02a8\1\134\2\0\2\u02a8\1\140\5\0" + + "\1\140\75\0\2\14\7\0\7\14\3\0\11\14\1\u02a9" + + "\34\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\16\14\1\u02aa\27\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\23\14\1\u02ab\22\14\23\0\1\14\6\0" + + "\2\14\7\0\7\14\3\0\23\14\1\u02ac\22\14\23\0" + + "\1\14\6\0\2\14\7\0\7\14\3\0\22\14\1\u02ad" + + "\23\14\23\0\1\14\6\0\2\14\7\0\7\14\3\0" + + "\12\14\1\u02ae\33\14\23\0\1\14\6\0\2\14\7\0" + + "\7\14\3\0\21\14\1\u02af\24\14\23\0\1\14"; + + private static int[] zzUnpackTrans() { + int[] result = new int[44571]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + + int j = offset; /* index in unpacked array */ + + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do { + result[j++] = value; + } while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 + = "\5\0\1\11\1\1\1\11\11\1\1\11\3\1\1\11" + + "\26\1\10\11\1\1\2\11\5\1\1\11\2\1\2\11" + + "\3\1\1\11\2\1\1\11\1\1\1\11\2\1\1\0" + + "\3\11\2\0\1\1\1\0\1\1\2\11\1\1\1\11" + + "\3\1\1\0\2\1\1\0\2\11\75\1\12\11\2\1" + + "\7\11\6\0\1\1\1\11\2\0\1\11\3\0\1\11" + + "\1\0\1\11\1\1\2\11\4\1\1\0\112\1\2\11" + + "\7\0\1\11\5\0\1\11\112\1\1\0\1\11\12\0" + + "\100\1\10\0\67\1\2\0\153\1\1\11\107\1"; + + private static int[] zzUnpackAttribute() { + int[] result = new int[687]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + + int j = offset; /* index in unpacked array */ + + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do { + result[j++] = value; + } while (--count > 0); + } + return j; + } + + /** + * the input device + */ + private java.io.Reader zzReader; + + /** + * the current state of the DFA + */ + private int zzState; + + /** + * the current lexical state + */ + private int zzLexicalState = YYINITIAL; + + /** + * this buffer contains the current text to be matched and is the source of + * the yytext() string + */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** + * the textposition at the last accepting state + */ + private int zzMarkedPos; + + /** + * the current text position in the buffer + */ + private int zzCurrentPos; + + /** + * startRead marks the beginning of the yytext() string in the buffer + */ + private int zzStartRead; + + /** + * endRead marks the last character in the buffer, that has been read from + * input + */ + private int zzEndRead; + + /** + * number of newlines encountered up to the start of the matched text + */ + private int yyline; + + /** + * the number of characters up to the start of the matched text + */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** + * zzAtEOF == true <=> the scanner is at the EOF + */ + private boolean zzAtEOF; + + /** + * denotes if the user-EOF-code has already been executed + */ + private boolean zzEOFDone; + + /* user code: */ + StringBuffer string = new StringBuffer(); + + private static String xmlTagName = ""; + + public int yychar() { + return yychar; + } + + private Stack pushedBack = new Stack(); + + public int yyline() { + return yyline + 1; + } + private List listeners = new ArrayList<>(); + + public void addListener(LexListener listener) { + listeners.add(listener); + } + + public void removeListener(LexListener listener) { + listeners.remove(listener); + } + + public void informListenersLex(ParsedSymbol s) { + for (LexListener l : listeners) { + l.onLex(s); + } + } + + public void informListenersPushBack(ParsedSymbol s) { + for (LexListener l : listeners) { + l.onPushBack(s); + } + } + + public void pushback(ParsedSymbol symb) { + pushedBack.push(symb); + last = null; + informListenersPushBack(symb); + } + ParsedSymbol last; + + public ParsedSymbol lex() throws java.io.IOException, ParseException { + ParsedSymbol ret = null; + if (!pushedBack.isEmpty()) { + ret = last = pushedBack.pop(); + } else { + ret = last = yylex(); + } + informListenersLex(ret); + return ret; + } + + /** + * Creates a new scanner There is also a java.io.InputStream version of this + * constructor. + * + * @param in the java.io.Reader to read input from. + */ + public ActionScriptLexer(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. There is also java.io.Reader version of this + * constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public ActionScriptLexer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in, java.nio.charset.Charset.forName("UTF-8"))); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char[] zzUnpackCMap(String packed) { + char[] map = new char[0x10000]; + int i = 0; /* index in packed string */ + + int j = 0; /* index in unpacked array */ + + while (i < 2272) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do { + map[j++] = value; + } while (--count > 0); + } + return map; + } + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead - zzStartRead); + + /* translate stored positions */ + zzEndRead -= zzStartRead; + zzCurrentPos -= zzStartRead; + zzMarkedPos -= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos * 2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length - zzEndRead); + + if (numRead > 0) { + zzEndRead += numRead; + return false; + } + // unlikely but not impossible: read 0 characters, but not at end of stream + if (numRead == 0) { + int c = zzReader.read(); + if (c == -1) { + return true; + } else { + zzBuffer[zzEndRead++] = (char) c; + return false; + } + } + + // numRead < 0 + return true; + } + + /** + * Closes the input stream. + * + * @throws java.io.IOException + */ + public final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) { + zzReader.close(); + } + } + + /** + * Resets the scanner to read from a new input stream. Does not close the + * old reader. + * + * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). Lexical + * state is set to ZZ_INITIAL. + * + * Internal scan buffer is resized down to its initial length, if it has + * grown. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEOFDone = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + if (zzBuffer.length > ZZ_BUFFERSIZE) { + zzBuffer = new char[ZZ_BUFFERSIZE]; + } + } + + /** + * Returns the current lexical state. + * + * @return + */ + public final int yystate() { + return zzLexicalState; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + /** + * Returns the text matched by the current regular expression. + * + * @return + */ + public final String yytext() { + return new String(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead); + } + + /** + * Returns the character at position pos from the matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. A value from 0 to + * yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead + pos]; + } + + /** + * Returns the length of the matched text region. + * + * @return + */ + public final int yylength() { + return zzMarkedPos - zzStartRead; + } + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of yypushback(int) and + * a match-all fallback rule) this method will only be called with things + * that "Can't Possibly Happen". If this method is called, something is + * seriously wrong (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done in error + * fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. This number must + * not be greater than yylength()! + */ + public void yypushback(int number) { + if (number > yylength()) { + zzScanError(ZZ_PUSHBACK_2BIG); + } + + zzMarkedPos -= number; + } + + /** + * Resumes scanning until the next regular expression is matched, the end of + * input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + * @throws com.jpexs.decompiler.flash.action.parser.ParseException + */ + public ParsedSymbol yylex() throws java.io.IOException, ParseException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char[] zzBufferL = zzBuffer; + char[] zzCMapL = ZZ_CMAP; + + int[] zzTransL = ZZ_TRANS; + int[] zzRowMapL = ZZ_ROWMAP; + int[] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar += zzMarkedPosL - zzStartRead; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + // set up zzAction for empty match case: + int zzAttributes = zzAttrL[zzState]; + if ((zzAttributes & 1) == 1) { + zzAction = zzState; + } + + zzForAction: + { + while (true) { + + if (zzCurrentPosL < zzEndReadL) { + zzInput = zzBufferL[zzCurrentPosL++]; + } else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput]]; + if (zzNext == -1) { + break zzForAction; + } + zzState = zzNext; + + zzAttributes = zzAttrL[zzState]; + if ((zzAttributes & 1) == 1) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ((zzAttributes & 8) == 8) { + break zzForAction; + } + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: { + } + case 180: + break; + case 2: { + yyline++; + } + case 181: + break; + case 3: { /*ignore*/ + + } + case 182: + break; + case 4: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DIVIDE, yytext()); + } + case 183: + break; + case 5: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.MULTIPLY, yytext()); + } + case 184: + break; + case 6: { + return new ParsedSymbol(SymbolGroup.IDENTIFIER, SymbolType.IDENTIFIER, yytext()); + } + case 185: + break; + case 7: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COLON, yytext()); + } + case 186: + break; + case 8: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DOT, yytext()); + } + case 187: + break; + case 9: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.LOWER_THAN, yytext()); + } + case 188: + break; + case 10: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_THAN, yytext()); + } + case 189: + break; + case 11: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN, yytext()); + } + case 190: + break; + case 12: { + string.setLength(0); + yybegin(STRING); + } + case 191: + break; + case 13: { + return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, new Long(Long.parseLong((yytext())))); + } + case 192: + break; + case 14: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.MINUS, yytext()); + } + case 193: + break; + case 15: { + string.setLength(0); + yybegin(CHARLITERAL); + } + case 194: + break; + case 16: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.PARENT_OPEN, yytext()); + } + case 195: + break; + case 17: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.PARENT_CLOSE, yytext()); + } + case 196: + break; + case 18: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_OPEN, yytext()); + } + case 197: + break; + case 19: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.CURLY_CLOSE, yytext()); + } + case 198: + break; + case 20: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_OPEN, yytext()); + } + case 199: + break; + case 21: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BRACKET_CLOSE, yytext()); + } + case 200: + break; + case 22: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.SEMICOLON, yytext()); + } + case 201: + break; + case 23: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.COMMA, yytext()); + } + case 202: + break; + case 24: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.NOT, yytext()); + } + case 203: + break; + case 25: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.NEGATE, yytext()); + } + case 204: + break; + case 26: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.TERNAR, yytext()); + } + case 205: + break; + case 27: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BITAND, yytext()); + } + case 206: + break; + case 28: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.BITOR, yytext()); + } + case 207: + break; + case 29: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.PLUS, yytext()); + } + case 208: + break; + case 30: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.XOR, yytext()); + } + case 209: + break; + case 31: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.MODULO, yytext()); + } + case 210: + break; + case 32: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ATTRIBUTE, yytext()); + } + case 211: + break; + case 33: { + string.append(yytext()); + } + case 212: + break; + case 34: { + yybegin(YYINITIAL); + yyline++; + } + case 213: + break; + case 35: { + yybegin(YYINITIAL); + // length also includes the trailing quote + return new ParsedSymbol(SymbolGroup.STRING, SymbolType.STRING, string.toString()); + } + case 214: + break; + case 36: { + string.append(yytext()); + yyline++; + } + case 215: + break; + case 37: { + yybegin(XML); + string.append(yytext()); + } + case 216: + break; + case 38: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_DIVIDE, yytext()); + } + case 217: + break; + case 39: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_MULTIPLY, yytext()); + } + case 218: + break; + case 40: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.NAMESPACE_OP, yytext()); + } + case 219: + break; + case 41: { + return new ParsedSymbol(SymbolGroup.DOUBLE, SymbolType.DOUBLE, new Double(Double.parseDouble((yytext())))); + } + case 220: + break; + case 42: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.SHIFT_LEFT, yytext()); + } + case 221: + break; + case 43: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.NOT_EQUAL, yytext()); + } + case 222: + break; + case 44: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.LOWER_EQUAL, yytext()); + } + case 223: + break; + case 45: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.SHIFT_RIGHT, yytext()); + } + case 224: + break; + case 46: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.GREATER_EQUAL, yytext()); + } + case 225: + break; + case 47: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.EQUALS, yytext()); + } + case 226: + break; + case 48: { + return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, new Long(Long.parseLong(yytext(), 8))); + } + case 227: + break; + case 49: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_MINUS, yytext()); + } + case 228: + break; + case 50: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DECREMENT, yytext()); + } + case 229: + break; + case 51: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.AS, yytext()); + } + case 230: + break; + case 52: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.FULLOR, yytext()); + } + case 231: + break; + case 53: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.IS, yytext()); + } + case 232: + break; + case 54: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.IN, yytext()); + } + case 233: + break; + case 55: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.IF, yytext()); + } + case 234: + break; + case 56: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.DO, yytext()); + } + case 235: + break; + case 57: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_BITAND, yytext()); + } + case 236: + break; + case 58: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.AND, yytext()); + } + case 237: + break; + case 59: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_BITOR, yytext()); + } + case 238: + break; + case 60: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.OR, yytext()); + } + case 239: + break; + case 61: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_PLUS, yytext()); + } + case 240: + break; + case 62: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.INCREMENT, yytext()); + } + case 241: + break; + case 63: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_XOR, yytext()); + } + case 242: + break; + case 64: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_MODULO, yytext()); + } + case 243: + break; + case 65: { + throw new ParseException("Illegal escape sequence \"" + yytext() + "\"", yyline + 1); + } + case 244: + break; + case 66: { + string.append('\"'); + } + case 245: + break; + case 67: { + char val = (char) Integer.parseInt(yytext().substring(1), 8); + string.append(val); + } + case 246: + break; + case 68: { + string.append('\\'); + } + case 247: + break; + case 69: { + string.append('\''); + } + case 248: + break; + case 70: { + string.append('\b'); + } + case 249: + break; + case 71: { + string.append('\r'); + } + case 250: + break; + case 72: { + string.append('\n'); + } + case 251: + break; + case 73: { + string.append('\t'); + } + case 252: + break; + case 74: { + string.append('\f'); + } + case 253: + break; + case 75: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.REST, yytext()); + } + case 254: + break; + case 76: { + string.setLength(0); + yybegin(XML); + String s = yytext(); + s = s.substring(1, s.length() - 1); + if (s.contains(" ")) { + s = s.substring(0, s.indexOf(' ')); + } + xmlTagName = s; + string.append(yytext()); + } + case 255: + break; + case 77: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_SHIFT_LEFT, yytext()); + } + case 256: + break; + case 78: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.USHIFT_RIGHT, yytext()); + } + case 257: + break; + case 79: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_SHIFT_RIGHT, yytext()); + } + case 258: + break; + case 80: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.STRICT_EQUALS, yytext()); + } + case 259: + break; + case 81: { + return new ParsedSymbol(SymbolGroup.INTEGER, SymbolType.INTEGER, new Long(Long.parseLong(yytext().substring(2), 16))); + } + case 260: + break; + case 82: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.FULLAND, yytext()); + } + case 261: + break; + case 83: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.CHR, yytext()); + } + case 262: + break; + case 84: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.SET, yytext()); + } + case 263: + break; + case 85: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.ORD, yytext()); + } + case 264: + break; + case 86: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.NEW, yytext()); + } + case 265: + break; + case 87: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRY, yytext()); + } + case 266: + break; + case 88: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.INT, yytext()); + } + case 267: + break; + case 89: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.USE, yytext()); + } + case 268: + break; + case 90: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FOR, yytext()); + } + case 269: + break; + case 91: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.VAR, yytext()); + } + case 270: + break; + case 92: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.GET, yytext()); + } + case 271: + break; + case 93: { + return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NAN, yytext()); + } + case 272: + break; + case 94: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.STRICT_NOT_EQUAL, yytext()); + } + case 273: + break; + case 95: { + String t = yytext(); + return new ParsedSymbol(SymbolGroup.TYPENAME, SymbolType.TYPENAME, t.substring(2, t.length() - 1)); + } + case 274: + break; + case 96: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.ASSIGN_USHIFT_RIGHT, yytext()); + } + case 275: + break; + case 97: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.EACH, yytext()); + } + case 276: + break; + case 98: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.ELSE, yytext()); + } + case 277: + break; + case 99: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.EVAL, yytext()); + } + case 278: + break; + case 100: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.CASE, yytext()); + } + case 279: + break; + case 101: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.CALL, yytext()); + } + case 280: + break; + case 102: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.STOP, yytext()); + } + case 281: + break; + case 103: { + return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NULL, yytext()); + } + case 282: + break; + case 104: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.TRUE, yytext()); + } + case 283: + break; + case 105: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.THIS, yytext()); + } + case 284: + break; + case 106: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.WITH, yytext()); + } + case 285: + break; + case 107: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PLAY, yytext()); + } + case 286: + break; + case 108: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.VOID, yytext()); + } + case 287: + break; + case 109: { + string.append(yytext()); + String endtagname = yytext(); + endtagname = endtagname.substring(2, endtagname.length() - 1); + if (endtagname.equals(xmlTagName)) { + yybegin(YYINITIAL); + return new ParsedSymbol(SymbolGroup.XML, SymbolType.XML, string.toString()); + } + } + case 288: + break; + case 110: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.BREAK, yytext()); + } + case 289: + break; + case 111: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.CATCH, yytext()); + } + case 290: + break; + case 112: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.CONST, yytext()); + } + case 291: + break; + case 113: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.CLASS, yytext()); + } + case 292: + break; + case 114: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.SUPER, yytext()); + } + case 293: + break; + case 115: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.TRACE, yytext()); + } + case 294: + break; + case 116: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.THROW, yytext()); + } + case 295: + break; + case 117: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FALSE, yytext()); + } + case 296: + break; + case 118: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.WHILE, yytext()); + } + case 297: + break; + case 119: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PRINT, yytext()); + } + case 298: + break; + case 120: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.MBCHR, yytext()); + } + case 299: + break; + case 121: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.MBORD, yytext()); + } + case 300: + break; + case 122: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.RETURN, yytext()); + } + case 301: + break; + case 123: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.RANDOM, yytext()); + } + case 302: + break; + case 124: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.STATIC, yytext()); + } + case 303: + break; + case 125: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.SUBSTR, yytext()); + } + case 304: + break; + case 126: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.SWITCH, yytext()); + } + case 305: + break; + case 127: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.TYPEOF, yytext()); + } + case 306: + break; + case 128: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.IMPORT, yytext()); + } + case 307: + break; + case 129: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.DELETE, yytext()); + } + case 308: + break; + case 130: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.LENGTH, yytext()); + } + case 309: + break; + case 131: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.PUBLIC, yytext()); + } + case 310: + break; + case 132: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.GETURL, yytext()); + } + case 311: + break; + case 133: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.STRING_OP, yytext()); + } + case 312: + break; + case 134: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.NUMBER_OP, yytext()); + } + case 313: + break; + case 135: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.EXTENDS, yytext()); + } + case 314: + break; + case 136: { + return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.NEWLINE, yytext()); + } + case 315: + break; + case 137: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.DEFAULT, yytext()); + } + case 316: + break; + case 138: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.DYNAMIC, yytext()); + } + case 317: + break; + case 139: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FINALLY, yytext()); + } + case 318: + break; + case 140: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.PRIVATE, yytext()); + } + case 319: + break; + case 141: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.PACKAGE, yytext()); + } + case 320: + break; + case 142: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.CONTINUE, yytext()); + } + case 321: + break; + case 143: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.STOPDRAG, yytext()); + } + case 322: + break; + case 144: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.OVERRIDE, yytext()); + } + case 323: + break; + case 145: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.INTERNAL, yytext()); + } + case 324: + break; + case 146: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.FUNCTION, yytext()); + } + case 325: + break; + case 147: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PRINTNUM, yytext()); + } + case 326: + break; + case 148: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.MBLENGTH, yytext()); + } + case 327: + break; + case 149: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.GETTIMER, yytext()); + } + case 328: + break; + case 150: { + return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.INFINITY, yytext()); + } + case 329: + break; + case 151: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.STARTDRAG, yytext()); + } + case 330: + break; + case 152: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.NEXTFRAME, yytext()); + } + case 331: + break; + case 153: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.NAMESPACE, yytext()); + } + case 332: + break; + case 154: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.INTERFACE, yytext()); + } + case 333: + break; + case 155: { + return new ParsedSymbol(SymbolGroup.GLOBALCONST, SymbolType.UNDEFINED, yytext()); + } + case 334: + break; + case 156: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.FSCOMMAND, yytext()); + } + case 335: + break; + case 157: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.LOADMOVIE, yytext()); + } + case 336: + break; + case 158: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PREVFRAME, yytext()); + } + case 337: + break; + case 159: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.PROTECTED, yytext()); + } + case 338: + break; + case 160: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.TELLTARGET, yytext()); + } + case 339: + break; + case 161: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.TARGETPATH, yytext()); + } + case 340: + break; + case 162: { + return new ParsedSymbol(SymbolGroup.OPERATOR, SymbolType.INSTANCEOF, yytext()); + } + case 341: + break; + case 163: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.IMPLEMENTS, yytext()); + } + case 342: + break; + case 164: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.GETVERSION, yytext()); + } + case 343: + break; + case 165: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.UNLOADMOVIE, yytext()); + } + case 344: + break; + case 166: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.MBSUBSTRING, yytext()); + } + case 345: + break; + case 167: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.GOTOANDSTOP, yytext()); + } + case 346: + break; + case 168: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.GOTOANDPLAY, yytext()); + } + case 347: + break; + case 169: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.LOADMOVIENUM, yytext()); + } + case 348: + break; + case 170: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.STOPALLSOUNDS, yytext()); + } + case 349: + break; + case 171: { + return new ParsedSymbol(SymbolGroup.KEYWORD, SymbolType.IFFRAMELOADED, yytext()); + } + case 350: + break; + case 172: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.LOADVARIABLES, yytext()); + } + case 351: + break; + case 173: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PRINTASBITMAP, yytext()); + } + case 352: + break; + case 174: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.UNLOADMOVIENUM, yytext()); + } + case 353: + break; + case 175: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.REMOVEMOVIECLIP, yytext()); + } + case 354: + break; + case 176: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.LOADVARIABLESNUM, yytext()); + } + case 355: + break; + case 177: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.PRINTASBITMAPNUM, yytext()); + } + case 356: + break; + case 178: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.TOGGLEHIGHQUALITY, yytext()); + } + case 357: + break; + case 179: { + return new ParsedSymbol(SymbolGroup.GLOBALFUNC, SymbolType.DUPLICATEMOVIECLIP, yytext()); + } + case 358: + break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { + return new ParsedSymbol(SymbolGroup.EOF, SymbolType.EOF, null); + } + } else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + +} diff --git a/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java b/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java index 1f80954c8..1ee93740d 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java +++ b/src/com/jpexs/decompiler/flash/action/parser/script/ActionScriptParser.java @@ -1,1808 +1,1806 @@ -/* - * Copyright (C) 2010-2014 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.action.parser.script; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem; -import com.jpexs.decompiler.flash.action.model.CallActionItem; -import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem; -import com.jpexs.decompiler.flash.action.model.CallMethodActionItem; -import com.jpexs.decompiler.flash.action.model.CastOpActionItem; -import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem; -import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem; -import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem; -import com.jpexs.decompiler.flash.action.model.DeleteActionItem; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.EvalActionItem; -import com.jpexs.decompiler.flash.action.model.FSCommand2ActionItem; -import com.jpexs.decompiler.flash.action.model.FSCommandActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetTimeActionItem; -import com.jpexs.decompiler.flash.action.model.GetURL2ActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.GetVersionActionItem; -import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; -import com.jpexs.decompiler.flash.action.model.InitArrayActionItem; -import com.jpexs.decompiler.flash.action.model.InitObjectActionItem; -import com.jpexs.decompiler.flash.action.model.LoadMovieActionItem; -import com.jpexs.decompiler.flash.action.model.LoadMovieNumActionItem; -import com.jpexs.decompiler.flash.action.model.LoadVariablesActionItem; -import com.jpexs.decompiler.flash.action.model.LoadVariablesNumActionItem; -import com.jpexs.decompiler.flash.action.model.MBAsciiToCharActionItem; -import com.jpexs.decompiler.flash.action.model.MBCharToAsciiActionItem; -import com.jpexs.decompiler.flash.action.model.MBStringExtractActionItem; -import com.jpexs.decompiler.flash.action.model.MBStringLengthActionItem; -import com.jpexs.decompiler.flash.action.model.NewMethodActionItem; -import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; -import com.jpexs.decompiler.flash.action.model.NextFrameActionItem; -import com.jpexs.decompiler.flash.action.model.PlayActionItem; -import com.jpexs.decompiler.flash.action.model.PostDecrementActionItem; -import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; -import com.jpexs.decompiler.flash.action.model.PrevFrameActionItem; -import com.jpexs.decompiler.flash.action.model.PrintActionItem; -import com.jpexs.decompiler.flash.action.model.PrintAsBitmapActionItem; -import com.jpexs.decompiler.flash.action.model.PrintAsBitmapNumActionItem; -import com.jpexs.decompiler.flash.action.model.PrintNumActionItem; -import com.jpexs.decompiler.flash.action.model.RandomNumberActionItem; -import com.jpexs.decompiler.flash.action.model.RemoveSpriteActionItem; -import com.jpexs.decompiler.flash.action.model.ReturnActionItem; -import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.StartDragActionItem; -import com.jpexs.decompiler.flash.action.model.StopActionItem; -import com.jpexs.decompiler.flash.action.model.StopAllSoundsActionItem; -import com.jpexs.decompiler.flash.action.model.StopDragActionItem; -import com.jpexs.decompiler.flash.action.model.StringExtractActionItem; -import com.jpexs.decompiler.flash.action.model.StringLengthActionItem; -import com.jpexs.decompiler.flash.action.model.TargetPathActionItem; -import com.jpexs.decompiler.flash.action.model.ThrowActionItem; -import com.jpexs.decompiler.flash.action.model.ToIntegerActionItem; -import com.jpexs.decompiler.flash.action.model.ToNumberActionItem; -import com.jpexs.decompiler.flash.action.model.ToStringActionItem; -import com.jpexs.decompiler.flash.action.model.ToggleHighQualityActionItem; -import com.jpexs.decompiler.flash.action.model.TraceActionItem; -import com.jpexs.decompiler.flash.action.model.TypeOfActionItem; -import com.jpexs.decompiler.flash.action.model.UnLoadMovieActionItem; -import com.jpexs.decompiler.flash.action.model.UnLoadMovieNumActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem; -import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitAndActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitOrActionItem; -import com.jpexs.decompiler.flash.action.model.operations.BitXorActionItem; -import com.jpexs.decompiler.flash.action.model.operations.DivideActionItem; -import com.jpexs.decompiler.flash.action.model.operations.EqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.GeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.GtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.InstanceOfActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LShiftActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.LtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.ModuloActionItem; -import com.jpexs.decompiler.flash.action.model.operations.MultiplyActionItem; -import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; -import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; -import com.jpexs.decompiler.flash.action.model.operations.RShiftActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringAddActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringEqActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringGeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringLeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringLtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringNeActionItem; -import com.jpexs.decompiler.flash.action.model.operations.SubtractActionItem; -import com.jpexs.decompiler.flash.action.model.operations.URShiftActionItem; -import com.jpexs.decompiler.flash.action.parser.ParseException; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.ecma.Undefined; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BinaryOp; -import com.jpexs.decompiler.graph.model.BlockItem; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.CommaExpressionItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.DoWhileItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -import com.jpexs.decompiler.graph.model.ParenthesisItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class ActionScriptParser { - - private int swfVersion; - - public ActionScriptParser(int swfVersion){ - this.swfVersion = swfVersion; - } - - private long uniqLast = 0; - private final boolean debugMode = false; - - private String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - private List commands(HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { - List ret = new ArrayList<>(); - if (debugMode) { - System.out.println("commands:"); - } - GraphTargetItem cmd = null; - while ((cmd = command(registerVars, inFunction, inMethod, forinlevel, true, variables)) != null) { - ret.add(cmd); - } - if (debugMode) { - System.out.println("/commands"); - } - return ret; - } - - private GraphTargetItem type(List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - - ParsedSymbol s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); - ret = new VariableActionItem(s.value.toString(), null, false); - variables.add((VariableActionItem) ret); - s = lex(); - while (s.type == SymbolType.DOT) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); - ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); - s = lex(); - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem memberOrCall(GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - ParsedSymbol s = lex(); - GraphTargetItem ret = newcmds; - while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.PARENT_OPEN)) { - switch (s.type) { - case DOT: - case BRACKET_OPEN: - lexer.pushback(s); - ret = member(ret, registerVars, inFunction, inMethod, variables); - break; - case PARENT_OPEN: - if (ret instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) ret; - ret = new CallMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod, variables)); - } else if (ret instanceof VariableActionItem) { - VariableActionItem var = (VariableActionItem) ret; - ret = new CallFunctionActionItem(null, pushConst(var.getVariableName()), call(registerVars, inFunction, inMethod, variables)); - } else { - ret = new CallFunctionActionItem(null, ret, call(registerVars, inFunction, inMethod, variables)); - } - break; - } - s = lex(); - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem member(GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - GraphTargetItem ret = obj; - ParsedSymbol s = lex(); - while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN)) { - if (s.type == SymbolType.BRACKET_OPEN) { - ret = new GetMemberActionItem(null, ret, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.BRACKET_CLOSE); - s = lex(); - continue; - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolGroup.GLOBALFUNC, SymbolGroup.GLOBALCONST); - //TODO: Handle properties (?) - if (false) {//GraphTargetItem.propertyNamesList.contains(s.value.toString())) { - //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString()))); - //ret.add(new ActionGetProperty()); - } else { - ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); - } - s = lex(); - } - lexer.pushback(s); - return ret; - } - - private GraphTargetItem variable(HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); - ret = new VariableActionItem(s.value.toString(), null, false); - variables.add((VariableActionItem) ret); - ret = (member(ret, registerVars, inFunction, inMethod, variables)); - return ret; - } - - private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ParseException { - boolean found = false; - for (Object t : expected) { - if (symb.type == t) { - found = true; - } - if (symb.group == t) { - found = true; - } - } - if (!found) { - String expStr = ""; - boolean first = true; - for (Object e : expected) { - if (!first) { - expStr += " or "; - } - expStr += e; - first = false; - } - throw new ParseException("" + expStr + " expected but " + symb.type + " found", line); - } - } - - private ParsedSymbol expectedType(Object... type) throws IOException, ParseException { - ParsedSymbol symb = lex(); - expected(symb, lexer.yyline(), type); - return symb; - } - - private ParsedSymbol lex() throws IOException, ParseException { - ParsedSymbol ret = lexer.lex(); - if (debugMode) { - System.out.println(ret); - } - return ret; - } - - private List call(HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - List ret = new ArrayList<>(); - //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER - ParsedSymbol s = lex(); - while (s.type != SymbolType.PARENT_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - ret.add(expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - return ret; - } - - private FunctionActionItem function(boolean withBody, String functionName, boolean isMethod, List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - ParsedSymbol s = null; - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - List paramNames = new ArrayList<>(); - - while (s.type != SymbolType.PARENT_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - paramNames.add(s.value.toString()); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - } - - if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); - } - } - List body = null; - List subvariables = new ArrayList<>(); - if (withBody) { - expectedType(SymbolType.CURLY_OPEN); - - body = commands(new HashMap(), true, isMethod, 0, subvariables); - expectedType(SymbolType.CURLY_CLOSE); - } - - return new FunctionActionItem(null, functionName, paramNames, body, constantPool, -1, subvariables); - } - - private GraphTargetItem traits(boolean isInterface, GraphTargetItem nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ParseException { - - GraphTargetItem ret = null; - /*for (int i = 0; i < nameStr.size() - 1; i++) { - List notBody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - for (int j = 0; j <= i; j++) { - globalClassTypeStr.add(nameStr.get(j)); - } - - List val = new ArrayList<>(); - val.add(new ActionPush((Long) 0L)); - val.add(pushConst("Object")); - val.add(new ActionNewObject()); - notBody.addAll(typeToActions(globalClassTypeStr, val)); - ret.addAll(typeToActions(globalClassTypeStr, null)); - ret.add(new ActionNot()); - ret.add(new ActionNot()); - ret.add(new ActionIf(GraphTargetItem.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); - ret.addAll(notBody); - } - List ifbody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - globalClassTypeStr.addAll(nameStr);*/ - - ParsedSymbol s = null; - FunctionActionItem constr = null; - List staticFunctions = new ArrayList<>(); - List> staticVars = new ArrayList<>(); - List functions = new ArrayList<>(); - List> vars = new ArrayList<>(); - - String classNameStr = ""; - if (nameStr instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) nameStr; - if (mem.memberName instanceof VariableActionItem) { - classNameStr = ((VariableActionItem) mem.memberName).getVariableName(); - } else if (mem.memberName instanceof DirectValueActionItem) { - classNameStr = ((DirectValueActionItem) mem.memberName).toStringNoQuotes(LocalData.empty); - } - } else if (nameStr instanceof VariableActionItem) { - VariableActionItem var = (VariableActionItem) nameStr; - classNameStr = var.getVariableName(); - } - - looptrait: - while (true) { - s = lex(); - boolean isStatic = false; - while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) { - if (s.type == SymbolType.STATIC) { - isStatic = true; - } - s = lex(); - } - switch (s.type) { - case FUNCTION: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); - String fname = s.value.toString(); - if (fname.equals(classNameStr)) { //constructor - constr = (function(!isInterface, "", true, variables)); - } else { - if (!isInterface) { - if (isStatic) { - FunctionActionItem ft = function(!isInterface, "", true, variables); - ft.calculatedFunctionName = pushConst(fname); - staticFunctions.add(ft); - } else { - FunctionActionItem ft = function(!isInterface, "", true, variables); - ft.calculatedFunctionName = pushConst(fname); - functions.add(ft); - } - } - } - break; - case VAR: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String ident = s.value.toString(); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - } - if (s.type == SymbolType.ASSIGN) { - if (isStatic) { - staticVars.add(new MyEntry(pushConst(ident), expression(new HashMap(), false, false, true, variables))); - } else { - vars.add(new MyEntry(pushConst(ident), expression(new HashMap(), false, false, true, variables))); - } - s = lex(); - } - if (s.type != SymbolType.SEMICOLON) { - lexer.pushback(s); - } - break; - default: - lexer.pushback(s); - break looptrait; - - } - } - - if (isInterface) { - return new InterfaceActionItem(nameStr, implementsStr); - } else { - return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, functions, vars, staticFunctions, staticVars); - } - } - - private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - switch (s.type) { - case GETVERSION: - expectedType(SymbolType.PARENT_OPEN); - ret = new GetVersionActionItem(null); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBORD: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBCharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBCHR: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBAsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBLENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBStringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBSUBSTRING: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem val1 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem index1 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem len1 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new MBStringExtractActionItem(null, val1, index1, len1); - break; - case SUBSTR: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem val2 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem index2 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem len2 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StringExtractActionItem(null, val2, index2, len2); - break; - case LENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new StringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case RANDOM: - expectedType(SymbolType.PARENT_OPEN); - ret = new RandomNumberActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case INT: - expectedType(SymbolType.PARENT_OPEN); - ret = new ToIntegerActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case NUMBER_OP: - s = lex(); - if (s.type == SymbolType.DOT) { - VariableActionItem vi = new VariableActionItem(s.value.toString(), null, false); - variables.add(vi); - ret = memberOrCall(vi, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToNumberActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - } - break; - case STRING_OP: - ParsedSymbol sop = s; - s = lex(); - if (s.type == SymbolType.DOT) { - lexer.pushback(s); - VariableActionItem vi2 = new VariableActionItem(sop.value.toString(), null, false); - variables.add(vi2); - ret = memberOrCall(vi2, registerVars, inFunction, inMethod, variables); - } else { - expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); - ret = new ToStringActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); - } - break; - case ORD: - expectedType(SymbolType.PARENT_OPEN); - ret = new CharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case CHR: - expectedType(SymbolType.PARENT_OPEN); - ret = new AsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case DUPLICATEMOVIECLIP: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem src3 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem tar3 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem dep3 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new CloneSpriteActionItem(null, src3, tar3, dep3); - break; - case GETTIMER: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new GetTimeActionItem(null); - break; - default: - return null; - } - return ret; - } - - private GraphTargetItem command(HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ParseException { - LexBufferer buf = new LexBufferer(); - lexer.addListener(buf); - GraphTargetItem ret = null; - if (debugMode) { - System.out.println("command:"); - } - ParsedSymbol s = lex(); - if (s.type == SymbolType.EOF) { - return null; - } - switch (s.type) { - case FSCOMMAND: - expectedType(SymbolType.PARENT_OPEN); - ret = new FSCommandActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - break; - case CALL: - expectedType(SymbolType.PARENT_OPEN); - ret = new CallActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case LENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new StringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case MBLENGTH: - expectedType(SymbolType.PARENT_OPEN); - ret = new MBStringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case SET: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem name1 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem value1 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new SetVariableActionItem(null, name1, value1); - break; - case WITH: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem wvar = (variable(registerVars, inFunction, inMethod, variables)); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List wcmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new WithActionItem(null, wvar, wcmd); - break; - case DELETE: - GraphTargetItem varDel = variable(registerVars, inFunction, inMethod, variables); - if (varDel instanceof GetMemberActionItem) { - GetMemberActionItem gm = (GetMemberActionItem) varDel; - ret = new DeleteActionItem(null, gm.object, gm.memberName); - } else if (varDel instanceof VariableActionItem) { - variables.remove(varDel); - ret = new DeleteActionItem(null, null, pushConst(((VariableActionItem) varDel).getVariableName())); - } else { - throw new ParseException("Not a property", lexer.yyline()); - } - break; - case TRACE: - expectedType(SymbolType.PARENT_OPEN); - ret = new TraceActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case GETURL: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem url = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); - int getuMethod = 1; - GraphTargetItem target = null; - if (s.type == SymbolType.COMMA) { - target = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.STRING); - if (s.value.equals("GET")) { - getuMethod = 1; - } else if (s.value.equals("POST")) { - getuMethod = 2; - } else { - throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); - } - } else { - lexer.pushback(s); - } - } else { - lexer.pushback(s); - target = new DirectValueActionItem(null, 0, "", new ArrayList()); - } - expectedType(SymbolType.PARENT_CLOSE); - ret = new GetURL2ActionItem(null, url, target, getuMethod); - break; - case GOTOANDSTOP: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem gtsFrame = expression(registerVars, inFunction, inMethod, true, variables); - s = lex(); - if (s.type == SymbolType.COMMA) { //Handle scene? - s = lex(); - gtsFrame = expression(registerVars, inFunction, inMethod, true, variables); - } else { - lexer.pushback(s); - } - ret = new GotoFrame2ActionItem(null, gtsFrame, false, false, 0); - expectedType(SymbolType.PARENT_CLOSE); - break; - case NEXTFRAME: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new NextFrameActionItem(null); - break; - case PLAY: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new PlayActionItem(null); - break; - case PREVFRAME: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new PrevFrameActionItem(null); - break; - case TELLTARGET: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem tellTarget = expression(registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List tellcmds = commands(registerVars, inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new TellTargetActionItem(null, tellTarget, tellcmds); - break; - case STOP: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopActionItem(null); - break; - case STOPALLSOUNDS: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopAllSoundsActionItem(null); - break; - case TOGGLEHIGHQUALITY: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new ToggleHighQualityActionItem(null); - break; - - case STOPDRAG: - expectedType(SymbolType.PARENT_OPEN); - expectedType(SymbolType.PARENT_CLOSE); - ret = new StopDragActionItem(null); - break; - - case TARGETPATH: - expectedType(SymbolType.PARENT_OPEN); - ret = new TargetPathActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case UNLOADMOVIE: - case UNLOADMOVIENUM: - SymbolType unloadType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem unTargetOrNum = expression(registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - if (unloadType == SymbolType.UNLOADMOVIE) { - ret = new UnLoadMovieActionItem(null, unTargetOrNum); - } - if (unloadType == SymbolType.UNLOADMOVIENUM) { - ret = new UnLoadMovieNumActionItem(null, unTargetOrNum); - } - break; - case PRINT: - case PRINTASBITMAP: - case PRINTASBITMAPNUM: - case PRINTNUM: - SymbolType printType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem printTarget = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem printBBox = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - - switch (printType) { - case PRINT: - ret = new PrintActionItem(null, printTarget, printBBox); - break; - case PRINTNUM: - ret = new PrintNumActionItem(null, printTarget, printBBox); - break; - case PRINTASBITMAP: - ret = new PrintAsBitmapActionItem(null, printTarget, printBBox); - break; - case PRINTASBITMAPNUM: - ret = new PrintAsBitmapNumActionItem(null, printTarget, printBBox); - break; - } - break; - case LOADVARIABLES: - case LOADMOVIE: - case LOADVARIABLESNUM: - case LOADMOVIENUM: - SymbolType loadType = s.type; - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem url2 = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.COMMA); - GraphTargetItem targetOrNum = (expression(registerVars, inFunction, inMethod, true, variables)); - - s = lex(); - expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); - int lvmethod = 1; - if (s.type == SymbolType.COMMA) { - s = lex(); - expected(s, lexer.yyline(), SymbolType.STRING); - if (s.value.equals("POST")) { - lvmethod = 2; - } else if (s.value.equals("GET")) { - lvmethod = 1; - } else { - throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); - } - } else { - lexer.pushback(s); - } - expectedType(SymbolType.PARENT_CLOSE); - switch (loadType) { - case LOADVARIABLES: - ret = new LoadVariablesActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADMOVIE: - ret = new LoadMovieActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADVARIABLESNUM: - ret = new LoadVariablesNumActionItem(null, url2, targetOrNum, lvmethod); - break; - case LOADMOVIENUM: - ret = new LoadMovieNumActionItem(null, url2, targetOrNum, lvmethod); - break; - } - break; - case GOTOANDPLAY: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem gtpFrame = expression(registerVars, inFunction, inMethod, true, variables); - s = lex(); - if (s.type == SymbolType.COMMA) { //Handle scene? - s = lex(); - gtpFrame = expression(registerVars, inFunction, inMethod, true, variables); - } else { - lexer.pushback(s); - } - ret = new GotoFrame2ActionItem(null, gtpFrame, false, true, 0); - expectedType(SymbolType.PARENT_CLOSE); - break; - - case REMOVEMOVIECLIP: - expectedType(SymbolType.PARENT_OPEN); - ret = new RemoveSpriteActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); - expectedType(SymbolType.PARENT_CLOSE); - break; - case STARTDRAG: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem dragTarget = (expression(registerVars, inFunction, inMethod, true, variables)); - GraphTargetItem lockCenter = null; - GraphTargetItem constrain = null; - GraphTargetItem x1 = null; - GraphTargetItem y1 = null; - GraphTargetItem x2 = null; - GraphTargetItem y2 = null; - s = lex(); - if (s.type == SymbolType.COMMA) { - lockCenter = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - constrain = new DirectValueActionItem(null, 0, new Long(1), new ArrayList()); - x1 = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - y1 = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - x2 = (expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (s.type == SymbolType.COMMA) { - y2 = (expression(registerVars, inFunction, inMethod, true, variables)); - } else { - lexer.pushback(s); - y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - } - } else { - lexer.pushback(s); - x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - } - } else { - lexer.pushback(s); - x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - y1 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); - - } - } else { - lexer.pushback(s); - constrain = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); - //ret.add(new ActionPush(Boolean.FALSE)); - } - } else { - lockCenter = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); - constrain = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); - lexer.pushback(s); - } - expectedType(SymbolType.PARENT_CLOSE); - ret = new StartDragActionItem(null, dragTarget, lockCenter, constrain, x1, y1, x2, y2); - break; - - case IFFRAMELOADED: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem iflExpr = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - List iflComs = commands(registerVars, inFunction, inMethod, forinlevel, variables); - expectedType(SymbolType.CURLY_CLOSE); - ret = new IfFrameLoadedActionItem(iflExpr, iflComs, null); - break; - case CLASS: - GraphTargetItem classTypeStr = type(variables); - s = lex(); - GraphTargetItem extendsTypeStr = null; - if (s.type == SymbolType.EXTENDS) { - extendsTypeStr = type(variables); - s = lex(); - } - List implementsTypeStrs = new ArrayList<>(); - if (s.type == SymbolType.IMPLEMENTS) { - do { - GraphTargetItem implementsTypeStr = type(variables); - implementsTypeStrs.add(implementsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - ret = (traits(false, classTypeStr, extendsTypeStr, implementsTypeStrs, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INTERFACE: - GraphTargetItem interfaceTypeStr = type(variables); - s = lex(); - List intExtendsTypeStrs = new ArrayList<>(); - - if (s.type == SymbolType.EXTENDS) { - do { - GraphTargetItem intExtendsTypeStr = type(variables); - intExtendsTypeStrs.add(intExtendsTypeStr); - s = lex(); - } while (s.type == SymbolType.COMMA); - } - expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); - ret = (traits(true, interfaceTypeStr, null, intExtendsTypeStrs, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case FUNCTION: - s = lexer.lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); - ret = (function(true, s.value.toString(), false, variables)); - break; - case VAR: - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - String varIdentifier = s.value.toString(); - s = lex(); - if (s.type == SymbolType.COLON) { - type(variables); - s = lex(); - //TODO: handle value type - } - - if (s.type == SymbolType.ASSIGN) { - GraphTargetItem varval = (expression(registerVars, inFunction, inMethod, true, variables)); - ret = new VariableActionItem(varIdentifier, varval, true); - variables.add((VariableActionItem) ret); - } else { - ret = new VariableActionItem(varIdentifier, null, true); - variables.add((VariableActionItem) ret); - lexer.pushback(s); - } - break; - case CURLY_OPEN: - ret = new BlockItem(null, commands(registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.CURLY_CLOSE); - break; - case INCREMENT: //preincrement - case DECREMENT: //predecrement - GraphTargetItem varincdec = variable(registerVars, inFunction, inMethod, variables); - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementActionItem(null, varincdec); - } else if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementActionItem(null, varincdec); - } - break; - case SUPER: //constructor call - ParsedSymbol ss2 = lex(); - if (ss2.type == SymbolType.PARENT_OPEN) { - List args = call(registerVars, inFunction, inMethod, variables); - VariableActionItem supItem = new VariableActionItem(s.value.toString(), null, false); - variables.add(supItem); - ret = new CallMethodActionItem(null, supItem, new DirectValueActionItem(null, 0, new Undefined(), constantPool), args); - } else {//no costructor call, but it could be calling parent methods... => handle in expression - lexer.pushback(ss2); - lexer.pushback(s); - } - break; - case IF: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem ifExpr = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - GraphTargetItem onTrue = command(registerVars, inFunction, inMethod, forinlevel, true, variables); - List onTrueList = new ArrayList<>(); - onTrueList.add(onTrue); - s = lex(); - List onFalseList = null; - if (s.type == SymbolType.ELSE) { - onFalseList = new ArrayList<>(); - onFalseList.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - } else { - lexer.pushback(s); - } - ret = new IfItem(null, ifExpr, onTrueList, onFalseList); - break; - case WHILE: - expectedType(SymbolType.PARENT_OPEN); - List whileExpr = new ArrayList<>(); - whileExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - List whileBody = new ArrayList<>(); - whileBody.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - ret = new WhileItem(null, null, whileExpr, whileBody); - break; - case DO: - List doBody = new ArrayList<>(); - doBody.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - expectedType(SymbolType.WHILE); - expectedType(SymbolType.PARENT_OPEN); - List doExpr = new ArrayList<>(); - doExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = new DoWhileItem(null, null, doBody, doExpr); - break; - case FOR: - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - boolean forin = false; - GraphTargetItem collection = null; - String objIdent = null; - int innerExprReg = 0; - if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER) { - ParsedSymbol s2 = null; - ParsedSymbol ssel = s; - if (s.type == SymbolType.VAR) { - s2 = lex(); - ssel = s2; - } - - if (ssel.type == SymbolType.IDENTIFIER) { - objIdent = ssel.value.toString(); - - ParsedSymbol s3 = lex(); - if (s3.type == SymbolType.IN) { - if (inFunction) { - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - registerVars.put(objIdent, i); - innerExprReg = i; - break; - } - } - } - collection = expression(registerVars, inFunction, inMethod, true, variables); - forin = true; - } else { - lexer.pushback(s3); - if (s2 != null) { - lexer.pushback(s2); - } - lexer.pushback(s); - } - } else { - if (s2 != null) { - lexer.pushback(s2); - } - lexer.pushback(s); - } - } else { - lexer.pushback(s); - } - List forFinalCommands = new ArrayList<>(); - GraphTargetItem forExpr = null; - List forFirstCommands = new ArrayList<>(); - if (!forin) { - GraphTargetItem fc = command(registerVars, inFunction, inMethod, forinlevel, true, variables); - if (fc != null) { //can be empty command - forFirstCommands.add(fc); - } - forExpr = (expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.SEMICOLON); - forFinalCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - } - expectedType(SymbolType.PARENT_CLOSE); - List forBody = new ArrayList<>(); - forBody.add(command(registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); - if (forin) { - ret = new ForInActionItem(null, null, pushConst(objIdent), collection, forBody); - } else { - ret = new ForItem(null, null, forFirstCommands, forExpr, forFinalCommands, forBody); - } - break; - case SWITCH: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem switchExpr = expression(registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.PARENT_CLOSE); - expectedType(SymbolType.CURLY_OPEN); - s = lex(); - //ret.addAll(switchExpr); - int exprReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - registerVars.put("__switch" + uniqId(), i); - exprReg = i; - break; - } - } - List> caseIfs = new ArrayList<>(); - List> caseCmds = new ArrayList<>(); - List caseExprsAll = new ArrayList<>(); - List valueMapping = new ArrayList<>(); - int pos = 0; - while (s.type == SymbolType.CASE) { - List caseExprs = new ArrayList<>(); - while (s.type == SymbolType.CASE) { - GraphTargetItem curCaseExpr = expression(registerVars, inFunction, inMethod, true, variables); - caseExprs.add(curCaseExpr); - expectedType(SymbolType.COLON); - s = lex(); - caseExprsAll.add(curCaseExpr); - valueMapping.add(pos); - } - pos++; - lexer.pushback(s); - List caseCmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); - caseCmds.add(caseCmd); - s = lex(); - } - List defCmd = new ArrayList<>(); - if (s.type == SymbolType.DEFAULT) { - expectedType(SymbolType.COLON); - defCmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); - s = lexer.lex(); - } - expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); - ret = new SwitchItem(null, null, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); - break; - case BREAK: - ret = new BreakItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 - break; - case CONTINUE: - ret = new ContinueItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 - break; - case RETURN: - GraphTargetItem retexpr = expression(true, registerVars, inFunction, inMethod, true, variables); - if (retexpr == null) { - retexpr = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); - } - ret = new ReturnActionItem(null, retexpr); - break; - case TRY: - List tryCommands = new ArrayList<>(); - tryCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - s = lex(); - boolean found = false; - List> catchCommands = null; - List catchExceptions = new ArrayList<>(); - if (s.type == SymbolType.CATCH) { - expectedType(SymbolType.PARENT_OPEN); - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); - catchExceptions.add(pushConst((String) s.value)); - expectedType(SymbolType.PARENT_CLOSE); - catchCommands = new ArrayList<>(); - List cc = new ArrayList<>(); - cc.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - catchCommands.add(cc); - s = lex(); - found = true; - } - List finallyCommands = null; - if (s.type == SymbolType.FINALLY) { - finallyCommands = new ArrayList<>(); - finallyCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); - found = true; - s = lex(); - } - if (!found) { - expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); - } - lexer.pushback(s); - ret = new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands); - break; - case THROW: - ret = new ThrowActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - break; - default: - GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel, variables); - if (valcmd != null) { - ret = valcmd; - break; - } - if (s.type == SymbolType.SEMICOLON) { - return null; - } - lexer.pushback(s); - ret = expression(registerVars, inFunction, inMethod, true, variables); - if (debugMode) { - System.out.println("/command"); - } - } - if (debugMode) { - System.out.println("/command"); - } - lexer.removeListener(buf); - if (ret == null) { //can be popped expression - buf.pushAllBack(lexer); - ret = expression(registerVars, inFunction, inMethod, true, variables); - } - s = lex(); - if ((s != null) && (s.type != SymbolType.SEMICOLON)) { - lexer.pushback(s); - } - - return ret; - - } - - private GraphTargetItem expression(HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { - return expression(false, registerVars, inFunction, inMethod, allowRemainder, variables); - } - - private GraphTargetItem fixPrecedence(GraphTargetItem expr) { - GraphTargetItem ret = expr; - if (expr instanceof BinaryOp) { - BinaryOp bo = (BinaryOp) expr; - GraphTargetItem left = bo.getLeftSide(); - //GraphTargetItem right=bo.getRightSide(); - if (left.getPrecedence() > bo.getPrecedence()) { - if (left instanceof BinaryOp) { - BinaryOp leftBo = (BinaryOp) left; - bo.setLeftSide(leftBo.getRightSide()); - leftBo.setRightSide(expr); - return left; - } - } - } - return ret; - } - - private GraphTargetItem expressionRemainder(GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - switch (s.type) { - case TERNAR: - GraphTargetItem terOnTrue = expression(registerVars, inFunction, inMethod, true, variables); - expectedType(SymbolType.COLON); - GraphTargetItem terOnFalse = expression(registerVars, inFunction, inMethod, true, variables); - ret = new TernarOpItem(null, expr, terOnTrue, terOnFalse); - break; - case SHIFT_LEFT: - ret = new LShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case SHIFT_RIGHT: - ret = new RShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case USHIFT_RIGHT: - ret = new URShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case BITAND: - ret = new BitAndActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case BITOR: - ret = new BitOrActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case DIVIDE: - ret = new DivideActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case MODULO: - ret = new ModuloActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case EQUALS: - ret = new EqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); - break; - case STRICT_EQUALS: - ret = new StrictEqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case NOT_EQUAL: - ret = new NeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); - break; - case STRICT_NOT_EQUAL: - ret = new StrictNeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case LOWER_THAN: - ret = new LtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); - break; - case LOWER_EQUAL: - ret = new LeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case GREATER_THAN: - ret = new GtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case GREATER_EQUAL: - ret = new GeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); - break; - case AND: - ret = new AndItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case OR: - ret = new OrItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case MINUS: - ret = new SubtractActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case MULTIPLY: - ret = new MultiplyActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case PLUS: - ret = new AddActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true); - break; - case XOR: - ret = new BitXorActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case AS: - - break; - case INSTANCEOF: - ret = new InstanceOfActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); - break; - case IS: - - break; - - case ASSIGN: - case ASSIGN_BITAND: - case ASSIGN_BITOR: - case ASSIGN_DIVIDE: - case ASSIGN_MINUS: - case ASSIGN_MODULO: - case ASSIGN_MULTIPLY: - case ASSIGN_PLUS: - case ASSIGN_SHIFT_LEFT: - case ASSIGN_SHIFT_RIGHT: - case ASSIGN_USHIFT_RIGHT: - case ASSIGN_XOR: - GraphTargetItem assigned = expression(registerVars, inFunction, inMethod, true, variables); - switch (s.type) { - case ASSIGN: - //assigned = assigned; - break; - case ASSIGN_BITAND: - assigned = new BitAndActionItem(null, expr, assigned); - break; - case ASSIGN_BITOR: - assigned = new BitOrActionItem(null, expr, assigned); - break; - case ASSIGN_DIVIDE: - assigned = new DivideActionItem(null, expr, assigned); - break; - case ASSIGN_MINUS: - assigned = new SubtractActionItem(null, expr, assigned); - break; - case ASSIGN_MODULO: - assigned = new ModuloActionItem(null, expr, assigned); - break; - case ASSIGN_MULTIPLY: - assigned = new MultiplyActionItem(null, expr, assigned); - break; - case ASSIGN_PLUS: - assigned = new AddActionItem(null, expr, assigned, true/*TODO:SWF version?*/); - break; - case ASSIGN_SHIFT_LEFT: - assigned = new LShiftActionItem(null, expr, assigned); - break; - case ASSIGN_SHIFT_RIGHT: - assigned = new RShiftActionItem(null, expr, assigned); - break; - case ASSIGN_USHIFT_RIGHT: - assigned = new URShiftActionItem(null, expr, assigned); - break; - case ASSIGN_XOR: - assigned = new BitXorActionItem(null, expr, assigned); - break; - } - if (expr instanceof VariableActionItem) { - ((VariableActionItem) expr).setStoreValue(assigned); - ((VariableActionItem) expr).setDefinition(false); - ret = expr; - } else if (expr instanceof GetMemberActionItem) { - ret = new SetMemberActionItem(null, ((GetMemberActionItem) expr).object, ((GetMemberActionItem) expr).memberName, assigned); - } else { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - break; - case INCREMENT: //postincrement - if (!(expr instanceof VariableActionItem) && !(expr instanceof GetMemberActionItem)) { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostIncrementActionItem(null, expr); - break; - case DECREMENT: //postdecrement - if (!(expr instanceof VariableActionItem) && !(expr instanceof GetMemberActionItem)) { - throw new ParseException("Invalid assignment", lexer.yyline()); - } - ret = new PostDecrementActionItem(null, expr); - break; - case DOT: //member - case BRACKET_OPEN: //member - case PARENT_OPEN: //function call - lexer.pushback(s); - ret = memberOrCall(expr, registerVars, inFunction, inMethod, variables); - break; - case IDENTIFIER: - switch(s.value.toString()){ - case "add": - ret = new StringAddActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "eq": - ret = new StringEqActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "ne": - ret = new StringNeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "lt": - ret = new StringLtActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "ge": - ret = new StringGeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "gt": - ret = new StringGtActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - case "le": - ret = new StringLeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); - break; - default: - lexer.pushback(s); - } - break; - default: - lexer.pushback(s); - } - - if(ret == null){ - if (expr instanceof ParenthesisItem) { - if (isType(((ParenthesisItem) expr).value)) { - GraphTargetItem expr2 = expression(false, registerVars, inFunction, inMethod, true, variables); - if (expr2 != null) { - ret = new CastOpActionItem(null, ((ParenthesisItem) expr).value, expr2); - } - } - } - } - - ret = fixPrecedence(ret); - return ret; - } - - private boolean isType(GraphTargetItem item) { - if (item == null) { - return false; - } - while (item instanceof GetMemberActionItem) { - item = ((GetMemberActionItem) item).object; - } - if (item instanceof VariableActionItem) { - return true; - } - return false; - } - - private int brackets(List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { - ParsedSymbol s = lex(); - int arrCnt = 0; - if (s.type == SymbolType.BRACKET_OPEN) { - s = lex(); - - while (s.type != SymbolType.BRACKET_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - arrCnt++; - ret.add(expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE); - } - } - } else { - lexer.pushback(s); - return -1; - } - return arrCnt; - } - - private GraphTargetItem commaExpression(HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ParseException { - GraphTargetItem cmd = null; - List expr = new ArrayList<>(); - ParsedSymbol s; - do { - cmd = command(registerVars, inFunction, inMethod, forInLevel, false, variables); - if (cmd != null) { - expr.add(cmd); - } - s = lex(); - } while (s.type == SymbolType.COMMA && cmd != null); - lexer.pushback(s); - if (cmd == null) { - expr.add(expression(registerVars, inFunction, inMethod, true, variables)); - } else { - if (!cmd.hasReturnValue()) { - throw new ParseException("Expression expected", lexer.yyline()); - } - } - return new CommaExpressionItem(null, expr); - } - - private GraphTargetItem expression(boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { - if (debugMode) { - System.out.println("expression:"); - } - GraphTargetItem ret = null; - ParsedSymbol s = lex(); - boolean existsRemainder = false; - boolean assocRight = false; - switch (s.type) { - case NEGATE: - versionRequired(s,5); - ret = expression(registerVars, inFunction, inMethod, false, variables); - ret = new BitXorActionItem(null, ret, new DirectValueActionItem(4.294967295E9)); - existsRemainder = true; - break; - case MINUS: - s = lex(); - if (s.isType(SymbolType.DOUBLE)) { - ret = new DirectValueActionItem(null, 0, -(double) (Double) s.value, new ArrayList()); - existsRemainder = true; - } else if (s.isType(SymbolType.INTEGER)) { - ret = new DirectValueActionItem(null, 0, -(long) (Long) s.value, new ArrayList()); - existsRemainder = true; - } else { - lexer.pushback(s); - GraphTargetItem num = expression(registerVars, inFunction, inMethod, true, variables); - if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Long)) { - ((DirectValueActionItem) num).value = -(Long) ((DirectValueActionItem) num).value; - ret = num; - } else if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Double)) { - Double d = (Double) ((DirectValueActionItem) num).value; - if (d.isInfinite()) { - ((DirectValueActionItem) num).value = Double.NEGATIVE_INFINITY; - } else { - ((DirectValueActionItem) num).value = -d; - } - ret = (num); - } else if ((num instanceof DirectValueActionItem) - && (((DirectValueActionItem) num).value instanceof Float)) { - ((DirectValueActionItem) num).value = -(Float) ((DirectValueActionItem) num).value; - ret = (num); - } else {; - ret = (new SubtractActionItem(null, new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()), num)); - } - } - break; - case TYPEOF: - ret = new TypeOfActionItem(null, expression(registerVars, inFunction, inMethod, false, variables)); - existsRemainder = true; - break; - case TRUE: - ret = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList()); - existsRemainder = true; - break; - case NULL: - ret = new DirectValueActionItem(null, 0, new Null(), new ArrayList()); - existsRemainder = true; - break; - case UNDEFINED: - ret = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); - break; - case FALSE: - ret = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList()); - existsRemainder = true; - break; - case CURLY_OPEN: //Object literal - s = lex(); - List objectNames = new ArrayList<>(); - List objectValues = new ArrayList<>(); - while (s.type != SymbolType.CURLY_CLOSE) { - if (s.type != SymbolType.COMMA) { - lexer.pushback(s); - } - s = lex(); - expected(s, lexer.yyline(), SymbolType.IDENTIFIER); - objectNames.add(0, pushConst((String) s.value)); - expectedType(SymbolType.COLON); - objectValues.add(0, expression(registerVars, inFunction, inMethod, true, variables)); - s = lex(); - if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { - expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); - } - } - ret = new InitObjectActionItem(null, objectNames, objectValues); - break; - case BRACKET_OPEN: //Array literal or just brackets - lexer.pushback(s); - List inBrackets = new ArrayList<>(); - int arrCnt = brackets(inBrackets, registerVars, inFunction, inMethod, variables); - ret = new InitArrayActionItem(null, inBrackets); - break; - case FUNCTION: - s = lexer.lex(); - String fname = ""; - if (s.isType(SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC)) { - fname = s.value.toString(); - } else { - lexer.pushback(s); - } - ret = function(true, fname, false, variables); - break; - case STRING: - ret = pushConst(s.value.toString()); - ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); - existsRemainder = true; - break; - case NEWLINE: - ret = new DirectValueActionItem(null, 0, "\r", new ArrayList()); - existsRemainder = true; - break; - case NAN: - ret = new DirectValueActionItem(null, 0, Double.NaN, new ArrayList()); - existsRemainder = true; - break; - case INFINITY: - ret = new DirectValueActionItem(null, 0, Double.POSITIVE_INFINITY, new ArrayList()); - existsRemainder = true; - break; - case INTEGER: - case DOUBLE: - ret = new DirectValueActionItem(null, 0, s.value, new ArrayList()); - existsRemainder = true; - break; - case DELETE: - GraphTargetItem varDel = variable(registerVars, inFunction, inMethod, variables); - if (varDel instanceof GetMemberActionItem) { - GetMemberActionItem gm = (GetMemberActionItem) varDel; - ret = new DeleteActionItem(null, gm.object, gm.memberName); - } else { - throw new ParseException("Not a property", lexer.yyline()); - } - break; - case INCREMENT: - case DECREMENT: //preincrement - GraphTargetItem prevar = variable(registerVars, inFunction, inMethod, variables); - if (s.type == SymbolType.INCREMENT) { - ret = new PreIncrementActionItem(null, prevar); - } - if (s.type == SymbolType.DECREMENT) { - ret = new PreDecrementActionItem(null, prevar); - } - existsRemainder = true; - break; - case NOT: - ret = new NotItem(null, expression(registerVars, inFunction, inMethod, false, variables)); - existsRemainder = true; - break; - case PARENT_OPEN: - ret = new ParenthesisItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); - existsRemainder = true; - break; - case NEW: - GraphTargetItem newvar = variable(registerVars, inFunction, inMethod, variables); - expectedType(SymbolType.PARENT_OPEN); - if (newvar instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) newvar; - ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod, variables)); - } else if (newvar instanceof VariableActionItem) { - ret = new NewObjectActionItem(null, newvar, call(registerVars, inFunction, inMethod, variables)); - } else { - throw new ParseException("Invalid new item", lexer.yyline()); - } - existsRemainder = true; - break; - case EVAL: - expectedType(SymbolType.PARENT_OPEN); - GraphTargetItem evar = new EvalActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); - expectedType(SymbolType.PARENT_CLOSE); - evar = memberOrCall(evar, registerVars, inFunction, inMethod, variables); - ret = evar; - existsRemainder = true; - break; - case IDENTIFIER: - case THIS: - case SUPER: - if(s.value.equals("not")){ - ret = new NotItem(null, expression(registerVars, inFunction, inMethod, false, variables)); - }else{ - lexer.pushback(s); - GraphTargetItem var = variable(registerVars, inFunction, inMethod, variables); - var = memberOrCall(var, registerVars, inFunction, inMethod, variables); - ret = var; - } - existsRemainder = true; - break; - default: - GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1, variables); - if (excmd != null) { - existsRemainder = true; //? - ret = excmd; - break; - } - lexer.pushback(s); - } - if (allowRemainder && existsRemainder) { - GraphTargetItem rem = ret; - do { - rem = expressionRemainder(rem, registerVars, inFunction, inMethod, assocRight, variables); - if (rem != null) { - ret = rem; - } - } while ((!assocRight) && (rem != null)); - } - if (debugMode) { - System.out.println("/expression"); - } - return ret; - } - - private DirectValueActionItem pushConst(String s) throws IOException, ParseException { - int index = constantPool.indexOf(s); - if (index == -1) { - constantPool.add(s); - index = constantPool.indexOf(s); - } - return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); - } - private ActionScriptLexer lexer = null; - private List constantPool; - - public List treeFromString(String str, List constantPool) throws ParseException, IOException { - List retTree = new ArrayList<>(); - this.constantPool = constantPool; - lexer = new ActionScriptLexer(new StringReader(str)); - - List vars = new ArrayList<>(); - retTree.addAll(commands(new HashMap(), false, false, 0, vars)); - for (VariableActionItem v : vars) { - String varName = v.getVariableName(); - GraphTargetItem stored = v.getStoreValue(); - if (v.isDefinition()) { - v.setBoxedValue(new DefineLocalActionItem(null, pushConst(varName), stored)); - } else { - if (stored != null) { - v.setBoxedValue(new SetVariableActionItem(null, pushConst(varName), stored)); - } else { - v.setBoxedValue(new GetVariableActionItem(null, pushConst(varName))); - } - } - } - if (lexer.lex().type != SymbolType.EOF) { - throw new ParseException("Parsing finisned before end of the file", lexer.yyline()); - } - return retTree; - } - - public List actionsFromTree(List tree, List constantPool) throws CompilationException { - ActionSourceGenerator gen = new ActionSourceGenerator(swfVersion,constantPool); - List ret = new ArrayList<>(); - SourceGeneratorLocalData localData = new SourceGeneratorLocalData( - new HashMap(), 0, Boolean.FALSE, 0); - List srcList = gen.generate(localData, tree); - for (GraphSourceItem s : srcList) { - if (s instanceof Action) { - ret.add((Action) s); - } - } - ret.add(0, new ActionConstantPool(constantPool)); - return ret; - } - - public List actionsFromString(String s) throws ParseException, IOException, CompilationException { - List constantPool = new ArrayList<>(); - List tree = treeFromString(s, constantPool); - return actionsFromTree(tree, constantPool); - } - - - private void versionRequired(ParsedSymbol s, int min) throws ParseException{ - versionRequired(s.value.toString(), min, Integer.MAX_VALUE); - } - - private void versionRequired(ParsedSymbol s, int min,int max) throws ParseException{ - versionRequired(s.value.toString(), min, max); - } - - private void versionRequired(String type, int min,int max) throws ParseException{ - if(min == max && swfVersion!=min){ - throw new ParseException(type+" requires SWF version "+min, lexer.yyline()); - } - if(swfVersionmax){ - throw new ParseException(type+" requires SWF version lower than "+max, lexer.yyline()); - } - } -} +/* + * Copyright (C) 2010-2014 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.action.parser.script; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem; +import com.jpexs.decompiler.flash.action.model.CallActionItem; +import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem; +import com.jpexs.decompiler.flash.action.model.CallMethodActionItem; +import com.jpexs.decompiler.flash.action.model.CastOpActionItem; +import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem; +import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem; +import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem; +import com.jpexs.decompiler.flash.action.model.DeleteActionItem; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.EvalActionItem; +import com.jpexs.decompiler.flash.action.model.FSCommandActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetTimeActionItem; +import com.jpexs.decompiler.flash.action.model.GetURL2ActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.GetVersionActionItem; +import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; +import com.jpexs.decompiler.flash.action.model.InitArrayActionItem; +import com.jpexs.decompiler.flash.action.model.InitObjectActionItem; +import com.jpexs.decompiler.flash.action.model.LoadMovieActionItem; +import com.jpexs.decompiler.flash.action.model.LoadMovieNumActionItem; +import com.jpexs.decompiler.flash.action.model.LoadVariablesActionItem; +import com.jpexs.decompiler.flash.action.model.LoadVariablesNumActionItem; +import com.jpexs.decompiler.flash.action.model.MBAsciiToCharActionItem; +import com.jpexs.decompiler.flash.action.model.MBCharToAsciiActionItem; +import com.jpexs.decompiler.flash.action.model.MBStringExtractActionItem; +import com.jpexs.decompiler.flash.action.model.MBStringLengthActionItem; +import com.jpexs.decompiler.flash.action.model.NewMethodActionItem; +import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; +import com.jpexs.decompiler.flash.action.model.NextFrameActionItem; +import com.jpexs.decompiler.flash.action.model.PlayActionItem; +import com.jpexs.decompiler.flash.action.model.PostDecrementActionItem; +import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem; +import com.jpexs.decompiler.flash.action.model.PrevFrameActionItem; +import com.jpexs.decompiler.flash.action.model.PrintActionItem; +import com.jpexs.decompiler.flash.action.model.PrintAsBitmapActionItem; +import com.jpexs.decompiler.flash.action.model.PrintAsBitmapNumActionItem; +import com.jpexs.decompiler.flash.action.model.PrintNumActionItem; +import com.jpexs.decompiler.flash.action.model.RandomNumberActionItem; +import com.jpexs.decompiler.flash.action.model.RemoveSpriteActionItem; +import com.jpexs.decompiler.flash.action.model.ReturnActionItem; +import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.StartDragActionItem; +import com.jpexs.decompiler.flash.action.model.StopActionItem; +import com.jpexs.decompiler.flash.action.model.StopAllSoundsActionItem; +import com.jpexs.decompiler.flash.action.model.StopDragActionItem; +import com.jpexs.decompiler.flash.action.model.StringExtractActionItem; +import com.jpexs.decompiler.flash.action.model.StringLengthActionItem; +import com.jpexs.decompiler.flash.action.model.TargetPathActionItem; +import com.jpexs.decompiler.flash.action.model.ThrowActionItem; +import com.jpexs.decompiler.flash.action.model.ToIntegerActionItem; +import com.jpexs.decompiler.flash.action.model.ToNumberActionItem; +import com.jpexs.decompiler.flash.action.model.ToStringActionItem; +import com.jpexs.decompiler.flash.action.model.ToggleHighQualityActionItem; +import com.jpexs.decompiler.flash.action.model.TraceActionItem; +import com.jpexs.decompiler.flash.action.model.TypeOfActionItem; +import com.jpexs.decompiler.flash.action.model.UnLoadMovieActionItem; +import com.jpexs.decompiler.flash.action.model.UnLoadMovieNumActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem; +import com.jpexs.decompiler.flash.action.model.operations.AddActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitAndActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitOrActionItem; +import com.jpexs.decompiler.flash.action.model.operations.BitXorActionItem; +import com.jpexs.decompiler.flash.action.model.operations.DivideActionItem; +import com.jpexs.decompiler.flash.action.model.operations.EqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.GeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.GtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.InstanceOfActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LShiftActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.LtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.ModuloActionItem; +import com.jpexs.decompiler.flash.action.model.operations.MultiplyActionItem; +import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem; +import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem; +import com.jpexs.decompiler.flash.action.model.operations.RShiftActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringAddActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringEqActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringGeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringLeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringLtActionItem; +import com.jpexs.decompiler.flash.action.model.operations.StringNeActionItem; +import com.jpexs.decompiler.flash.action.model.operations.SubtractActionItem; +import com.jpexs.decompiler.flash.action.model.operations.URShiftActionItem; +import com.jpexs.decompiler.flash.action.parser.ParseException; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.ecma.Undefined; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BinaryOp; +import com.jpexs.decompiler.graph.model.BlockItem; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.DoWhileItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +import com.jpexs.decompiler.graph.model.ParenthesisItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ActionScriptParser { + + private int swfVersion; + + public ActionScriptParser(int swfVersion) { + this.swfVersion = swfVersion; + } + + private long uniqLast = 0; + private final boolean debugMode = false; + + private String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + private List commands(HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { + List ret = new ArrayList<>(); + if (debugMode) { + System.out.println("commands:"); + } + GraphTargetItem cmd = null; + while ((cmd = command(registerVars, inFunction, inMethod, forinlevel, true, variables)) != null) { + ret.add(cmd); + } + if (debugMode) { + System.out.println("/commands"); + } + return ret; + } + + private GraphTargetItem type(List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + + ParsedSymbol s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); + ret = new VariableActionItem(s.value.toString(), null, false); + variables.add((VariableActionItem) ret); + s = lex(); + while (s.type == SymbolType.DOT) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING_OP); + ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); + s = lex(); + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem memberOrCall(GraphTargetItem newcmds, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + ParsedSymbol s = lex(); + GraphTargetItem ret = newcmds; + while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.PARENT_OPEN)) { + switch (s.type) { + case DOT: + case BRACKET_OPEN: + lexer.pushback(s); + ret = member(ret, registerVars, inFunction, inMethod, variables); + break; + case PARENT_OPEN: + if (ret instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) ret; + ret = new CallMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod, variables)); + } else if (ret instanceof VariableActionItem) { + VariableActionItem var = (VariableActionItem) ret; + ret = new CallFunctionActionItem(null, pushConst(var.getVariableName()), call(registerVars, inFunction, inMethod, variables)); + } else { + ret = new CallFunctionActionItem(null, ret, call(registerVars, inFunction, inMethod, variables)); + } + break; + } + s = lex(); + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem member(GraphTargetItem obj, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + GraphTargetItem ret = obj; + ParsedSymbol s = lex(); + while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN)) { + if (s.type == SymbolType.BRACKET_OPEN) { + ret = new GetMemberActionItem(null, ret, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.BRACKET_CLOSE); + s = lex(); + continue; + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolGroup.GLOBALFUNC, SymbolGroup.GLOBALCONST); + //TODO: Handle properties (?) + if (false) {//GraphTargetItem.propertyNamesList.contains(s.value.toString())) { + //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString()))); + //ret.add(new ActionGetProperty()); + } else { + ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString())); + } + s = lex(); + } + lexer.pushback(s); + return ret; + } + + private GraphTargetItem variable(HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolType.STRING_OP); + ret = new VariableActionItem(s.value.toString(), null, false); + variables.add((VariableActionItem) ret); + ret = (member(ret, registerVars, inFunction, inMethod, variables)); + return ret; + } + + private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ParseException { + boolean found = false; + for (Object t : expected) { + if (symb.type == t) { + found = true; + } + if (symb.group == t) { + found = true; + } + } + if (!found) { + String expStr = ""; + boolean first = true; + for (Object e : expected) { + if (!first) { + expStr += " or "; + } + expStr += e; + first = false; + } + throw new ParseException("" + expStr + " expected but " + symb.type + " found", line); + } + } + + private ParsedSymbol expectedType(Object... type) throws IOException, ParseException { + ParsedSymbol symb = lex(); + expected(symb, lexer.yyline(), type); + return symb; + } + + private ParsedSymbol lex() throws IOException, ParseException { + ParsedSymbol ret = lexer.lex(); + if (debugMode) { + System.out.println(ret); + } + return ret; + } + + private List call(HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + List ret = new ArrayList<>(); + //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER + ParsedSymbol s = lex(); + while (s.type != SymbolType.PARENT_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + ret.add(expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + return ret; + } + + private FunctionActionItem function(boolean withBody, String functionName, boolean isMethod, List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + ParsedSymbol s = null; + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + List paramNames = new ArrayList<>(); + + while (s.type != SymbolType.PARENT_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + paramNames.add(s.value.toString()); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + } + + if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE); + } + } + List body = null; + List subvariables = new ArrayList<>(); + if (withBody) { + expectedType(SymbolType.CURLY_OPEN); + + body = commands(new HashMap(), true, isMethod, 0, subvariables); + expectedType(SymbolType.CURLY_CLOSE); + } + + return new FunctionActionItem(null, functionName, paramNames, body, constantPool, -1, subvariables); + } + + private GraphTargetItem traits(boolean isInterface, GraphTargetItem nameStr, GraphTargetItem extendsStr, List implementsStr, List variables) throws IOException, ParseException { + + GraphTargetItem ret = null; + /*for (int i = 0; i < nameStr.size() - 1; i++) { + List notBody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + for (int j = 0; j <= i; j++) { + globalClassTypeStr.add(nameStr.get(j)); + } + + List val = new ArrayList<>(); + val.add(new ActionPush((Long) 0L)); + val.add(pushConst("Object")); + val.add(new ActionNewObject()); + notBody.addAll(typeToActions(globalClassTypeStr, val)); + ret.addAll(typeToActions(globalClassTypeStr, null)); + ret.add(new ActionNot()); + ret.add(new ActionNot()); + ret.add(new ActionIf(GraphTargetItem.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); + ret.addAll(notBody); + } + List ifbody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + globalClassTypeStr.addAll(nameStr);*/ + + ParsedSymbol s = null; + FunctionActionItem constr = null; + List staticFunctions = new ArrayList<>(); + List> staticVars = new ArrayList<>(); + List functions = new ArrayList<>(); + List> vars = new ArrayList<>(); + + String classNameStr = ""; + if (nameStr instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) nameStr; + if (mem.memberName instanceof VariableActionItem) { + classNameStr = ((VariableActionItem) mem.memberName).getVariableName(); + } else if (mem.memberName instanceof DirectValueActionItem) { + classNameStr = ((DirectValueActionItem) mem.memberName).toStringNoQuotes(LocalData.empty); + } + } else if (nameStr instanceof VariableActionItem) { + VariableActionItem var = (VariableActionItem) nameStr; + classNameStr = var.getVariableName(); + } + + looptrait: + while (true) { + s = lex(); + boolean isStatic = false; + while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) { + if (s.type == SymbolType.STATIC) { + isStatic = true; + } + s = lex(); + } + switch (s.type) { + case FUNCTION: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); + String fname = s.value.toString(); + if (fname.equals(classNameStr)) { //constructor + constr = (function(!isInterface, "", true, variables)); + } else { + if (!isInterface) { + if (isStatic) { + FunctionActionItem ft = function(!isInterface, "", true, variables); + ft.calculatedFunctionName = pushConst(fname); + staticFunctions.add(ft); + } else { + FunctionActionItem ft = function(!isInterface, "", true, variables); + ft.calculatedFunctionName = pushConst(fname); + functions.add(ft); + } + } + } + break; + case VAR: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String ident = s.value.toString(); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + } + if (s.type == SymbolType.ASSIGN) { + if (isStatic) { + staticVars.add(new MyEntry(pushConst(ident), expression(new HashMap(), false, false, true, variables))); + } else { + vars.add(new MyEntry(pushConst(ident), expression(new HashMap(), false, false, true, variables))); + } + s = lex(); + } + if (s.type != SymbolType.SEMICOLON) { + lexer.pushback(s); + } + break; + default: + lexer.pushback(s); + break looptrait; + + } + } + + if (isInterface) { + return new InterfaceActionItem(nameStr, implementsStr); + } else { + return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, functions, vars, staticFunctions, staticVars); + } + } + + private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + switch (s.type) { + case GETVERSION: + expectedType(SymbolType.PARENT_OPEN); + ret = new GetVersionActionItem(null); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBORD: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBCharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBCHR: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBAsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBLENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBStringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBSUBSTRING: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem val1 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem index1 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem len1 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new MBStringExtractActionItem(null, val1, index1, len1); + break; + case SUBSTR: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem val2 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem index2 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem len2 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StringExtractActionItem(null, val2, index2, len2); + break; + case LENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new StringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case RANDOM: + expectedType(SymbolType.PARENT_OPEN); + ret = new RandomNumberActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case INT: + expectedType(SymbolType.PARENT_OPEN); + ret = new ToIntegerActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case NUMBER_OP: + s = lex(); + if (s.type == SymbolType.DOT) { + VariableActionItem vi = new VariableActionItem(s.value.toString(), null, false); + variables.add(vi); + ret = memberOrCall(vi, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToNumberActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + } + break; + case STRING_OP: + ParsedSymbol sop = s; + s = lex(); + if (s.type == SymbolType.DOT) { + lexer.pushback(s); + VariableActionItem vi2 = new VariableActionItem(sop.value.toString(), null, false); + variables.add(vi2); + ret = memberOrCall(vi2, registerVars, inFunction, inMethod, variables); + } else { + expected(s, lexer.yyline(), SymbolType.PARENT_OPEN); + ret = new ToStringActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); + } + break; + case ORD: + expectedType(SymbolType.PARENT_OPEN); + ret = new CharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case CHR: + expectedType(SymbolType.PARENT_OPEN); + ret = new AsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case DUPLICATEMOVIECLIP: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem src3 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem tar3 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem dep3 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new CloneSpriteActionItem(null, src3, tar3, dep3); + break; + case GETTIMER: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new GetTimeActionItem(null); + break; + default: + return null; + } + return ret; + } + + private GraphTargetItem command(HashMap registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand, List variables) throws IOException, ParseException { + LexBufferer buf = new LexBufferer(); + lexer.addListener(buf); + GraphTargetItem ret = null; + if (debugMode) { + System.out.println("command:"); + } + ParsedSymbol s = lex(); + if (s.type == SymbolType.EOF) { + return null; + } + switch (s.type) { + case FSCOMMAND: + expectedType(SymbolType.PARENT_OPEN); + ret = new FSCommandActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + break; + case CALL: + expectedType(SymbolType.PARENT_OPEN); + ret = new CallActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case LENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new StringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case MBLENGTH: + expectedType(SymbolType.PARENT_OPEN); + ret = new MBStringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case SET: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem name1 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem value1 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new SetVariableActionItem(null, name1, value1); + break; + case WITH: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem wvar = (variable(registerVars, inFunction, inMethod, variables)); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List wcmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new WithActionItem(null, wvar, wcmd); + break; + case DELETE: + GraphTargetItem varDel = variable(registerVars, inFunction, inMethod, variables); + if (varDel instanceof GetMemberActionItem) { + GetMemberActionItem gm = (GetMemberActionItem) varDel; + ret = new DeleteActionItem(null, gm.object, gm.memberName); + } else if (varDel instanceof VariableActionItem) { + variables.remove(varDel); + ret = new DeleteActionItem(null, null, pushConst(((VariableActionItem) varDel).getVariableName())); + } else { + throw new ParseException("Not a property", lexer.yyline()); + } + break; + case TRACE: + expectedType(SymbolType.PARENT_OPEN); + ret = new TraceActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case GETURL: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem url = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); + int getuMethod = 1; + GraphTargetItem target = null; + if (s.type == SymbolType.COMMA) { + target = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.STRING); + if (s.value.equals("GET")) { + getuMethod = 1; + } else if (s.value.equals("POST")) { + getuMethod = 2; + } else { + throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); + } + } else { + lexer.pushback(s); + } + } else { + lexer.pushback(s); + target = new DirectValueActionItem(null, 0, "", new ArrayList()); + } + expectedType(SymbolType.PARENT_CLOSE); + ret = new GetURL2ActionItem(null, url, target, getuMethod); + break; + case GOTOANDSTOP: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem gtsFrame = expression(registerVars, inFunction, inMethod, true, variables); + s = lex(); + if (s.type == SymbolType.COMMA) { //Handle scene? + s = lex(); + gtsFrame = expression(registerVars, inFunction, inMethod, true, variables); + } else { + lexer.pushback(s); + } + ret = new GotoFrame2ActionItem(null, gtsFrame, false, false, 0); + expectedType(SymbolType.PARENT_CLOSE); + break; + case NEXTFRAME: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new NextFrameActionItem(null); + break; + case PLAY: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new PlayActionItem(null); + break; + case PREVFRAME: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new PrevFrameActionItem(null); + break; + case TELLTARGET: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem tellTarget = expression(registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List tellcmds = commands(registerVars, inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new TellTargetActionItem(null, tellTarget, tellcmds); + break; + case STOP: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopActionItem(null); + break; + case STOPALLSOUNDS: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopAllSoundsActionItem(null); + break; + case TOGGLEHIGHQUALITY: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new ToggleHighQualityActionItem(null); + break; + + case STOPDRAG: + expectedType(SymbolType.PARENT_OPEN); + expectedType(SymbolType.PARENT_CLOSE); + ret = new StopDragActionItem(null); + break; + + case TARGETPATH: + expectedType(SymbolType.PARENT_OPEN); + ret = new TargetPathActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case UNLOADMOVIE: + case UNLOADMOVIENUM: + SymbolType unloadType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem unTargetOrNum = expression(registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + if (unloadType == SymbolType.UNLOADMOVIE) { + ret = new UnLoadMovieActionItem(null, unTargetOrNum); + } + if (unloadType == SymbolType.UNLOADMOVIENUM) { + ret = new UnLoadMovieNumActionItem(null, unTargetOrNum); + } + break; + case PRINT: + case PRINTASBITMAP: + case PRINTASBITMAPNUM: + case PRINTNUM: + SymbolType printType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem printTarget = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem printBBox = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + + switch (printType) { + case PRINT: + ret = new PrintActionItem(null, printTarget, printBBox); + break; + case PRINTNUM: + ret = new PrintNumActionItem(null, printTarget, printBBox); + break; + case PRINTASBITMAP: + ret = new PrintAsBitmapActionItem(null, printTarget, printBBox); + break; + case PRINTASBITMAPNUM: + ret = new PrintAsBitmapNumActionItem(null, printTarget, printBBox); + break; + } + break; + case LOADVARIABLES: + case LOADMOVIE: + case LOADVARIABLESNUM: + case LOADMOVIENUM: + SymbolType loadType = s.type; + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem url2 = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.COMMA); + GraphTargetItem targetOrNum = (expression(registerVars, inFunction, inMethod, true, variables)); + + s = lex(); + expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA); + int lvmethod = 1; + if (s.type == SymbolType.COMMA) { + s = lex(); + expected(s, lexer.yyline(), SymbolType.STRING); + if (s.value.equals("POST")) { + lvmethod = 2; + } else if (s.value.equals("GET")) { + lvmethod = 1; + } else { + throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline()); + } + } else { + lexer.pushback(s); + } + expectedType(SymbolType.PARENT_CLOSE); + switch (loadType) { + case LOADVARIABLES: + ret = new LoadVariablesActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADMOVIE: + ret = new LoadMovieActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADVARIABLESNUM: + ret = new LoadVariablesNumActionItem(null, url2, targetOrNum, lvmethod); + break; + case LOADMOVIENUM: + ret = new LoadMovieNumActionItem(null, url2, targetOrNum, lvmethod); + break; + } + break; + case GOTOANDPLAY: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem gtpFrame = expression(registerVars, inFunction, inMethod, true, variables); + s = lex(); + if (s.type == SymbolType.COMMA) { //Handle scene? + s = lex(); + gtpFrame = expression(registerVars, inFunction, inMethod, true, variables); + } else { + lexer.pushback(s); + } + ret = new GotoFrame2ActionItem(null, gtpFrame, false, true, 0); + expectedType(SymbolType.PARENT_CLOSE); + break; + + case REMOVEMOVIECLIP: + expectedType(SymbolType.PARENT_OPEN); + ret = new RemoveSpriteActionItem(null, (expression(registerVars, inFunction, inMethod, true, variables))); + expectedType(SymbolType.PARENT_CLOSE); + break; + case STARTDRAG: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem dragTarget = (expression(registerVars, inFunction, inMethod, true, variables)); + GraphTargetItem lockCenter = null; + GraphTargetItem constrain = null; + GraphTargetItem x1 = null; + GraphTargetItem y1 = null; + GraphTargetItem x2 = null; + GraphTargetItem y2 = null; + s = lex(); + if (s.type == SymbolType.COMMA) { + lockCenter = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + constrain = new DirectValueActionItem(null, 0, new Long(1), new ArrayList()); + x1 = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + y1 = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + x2 = (expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (s.type == SymbolType.COMMA) { + y2 = (expression(registerVars, inFunction, inMethod, true, variables)); + } else { + lexer.pushback(s); + y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + } + } else { + lexer.pushback(s); + x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + } + } else { + lexer.pushback(s); + x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + y1 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()); + + } + } else { + lexer.pushback(s); + constrain = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); + //ret.add(new ActionPush(Boolean.FALSE)); + } + } else { + lockCenter = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); + constrain = new DirectValueActionItem(null, 0, new Long(0), new ArrayList()); + lexer.pushback(s); + } + expectedType(SymbolType.PARENT_CLOSE); + ret = new StartDragActionItem(null, dragTarget, lockCenter, constrain, x1, y1, x2, y2); + break; + + case IFFRAMELOADED: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem iflExpr = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + List iflComs = commands(registerVars, inFunction, inMethod, forinlevel, variables); + expectedType(SymbolType.CURLY_CLOSE); + ret = new IfFrameLoadedActionItem(iflExpr, iflComs, null); + break; + case CLASS: + GraphTargetItem classTypeStr = type(variables); + s = lex(); + GraphTargetItem extendsTypeStr = null; + if (s.type == SymbolType.EXTENDS) { + extendsTypeStr = type(variables); + s = lex(); + } + List implementsTypeStrs = new ArrayList<>(); + if (s.type == SymbolType.IMPLEMENTS) { + do { + GraphTargetItem implementsTypeStr = type(variables); + implementsTypeStrs.add(implementsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + ret = (traits(false, classTypeStr, extendsTypeStr, implementsTypeStrs, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INTERFACE: + GraphTargetItem interfaceTypeStr = type(variables); + s = lex(); + List intExtendsTypeStrs = new ArrayList<>(); + + if (s.type == SymbolType.EXTENDS) { + do { + GraphTargetItem intExtendsTypeStr = type(variables); + intExtendsTypeStrs.add(intExtendsTypeStr); + s = lex(); + } while (s.type == SymbolType.COMMA); + } + expected(s, lexer.yyline(), SymbolType.CURLY_OPEN); + ret = (traits(true, interfaceTypeStr, null, intExtendsTypeStrs, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case FUNCTION: + s = lexer.lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC); + ret = (function(true, s.value.toString(), false, variables)); + break; + case VAR: + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + String varIdentifier = s.value.toString(); + s = lex(); + if (s.type == SymbolType.COLON) { + type(variables); + s = lex(); + //TODO: handle value type + } + + if (s.type == SymbolType.ASSIGN) { + GraphTargetItem varval = (expression(registerVars, inFunction, inMethod, true, variables)); + ret = new VariableActionItem(varIdentifier, varval, true); + variables.add((VariableActionItem) ret); + } else { + ret = new VariableActionItem(varIdentifier, null, true); + variables.add((VariableActionItem) ret); + lexer.pushback(s); + } + break; + case CURLY_OPEN: + ret = new BlockItem(null, commands(registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.CURLY_CLOSE); + break; + case INCREMENT: //preincrement + case DECREMENT: //predecrement + GraphTargetItem varincdec = variable(registerVars, inFunction, inMethod, variables); + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementActionItem(null, varincdec); + } else if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementActionItem(null, varincdec); + } + break; + case SUPER: //constructor call + ParsedSymbol ss2 = lex(); + if (ss2.type == SymbolType.PARENT_OPEN) { + List args = call(registerVars, inFunction, inMethod, variables); + VariableActionItem supItem = new VariableActionItem(s.value.toString(), null, false); + variables.add(supItem); + ret = new CallMethodActionItem(null, supItem, new DirectValueActionItem(null, 0, new Undefined(), constantPool), args); + } else {//no costructor call, but it could be calling parent methods... => handle in expression + lexer.pushback(ss2); + lexer.pushback(s); + } + break; + case IF: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem ifExpr = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + GraphTargetItem onTrue = command(registerVars, inFunction, inMethod, forinlevel, true, variables); + List onTrueList = new ArrayList<>(); + onTrueList.add(onTrue); + s = lex(); + List onFalseList = null; + if (s.type == SymbolType.ELSE) { + onFalseList = new ArrayList<>(); + onFalseList.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + } else { + lexer.pushback(s); + } + ret = new IfItem(null, ifExpr, onTrueList, onFalseList); + break; + case WHILE: + expectedType(SymbolType.PARENT_OPEN); + List whileExpr = new ArrayList<>(); + whileExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + List whileBody = new ArrayList<>(); + whileBody.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + ret = new WhileItem(null, null, whileExpr, whileBody); + break; + case DO: + List doBody = new ArrayList<>(); + doBody.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + expectedType(SymbolType.WHILE); + expectedType(SymbolType.PARENT_OPEN); + List doExpr = new ArrayList<>(); + doExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = new DoWhileItem(null, null, doBody, doExpr); + break; + case FOR: + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + boolean forin = false; + GraphTargetItem collection = null; + String objIdent = null; + int innerExprReg = 0; + if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER) { + ParsedSymbol s2 = null; + ParsedSymbol ssel = s; + if (s.type == SymbolType.VAR) { + s2 = lex(); + ssel = s2; + } + + if (ssel.type == SymbolType.IDENTIFIER) { + objIdent = ssel.value.toString(); + + ParsedSymbol s3 = lex(); + if (s3.type == SymbolType.IN) { + if (inFunction) { + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + registerVars.put(objIdent, i); + innerExprReg = i; + break; + } + } + } + collection = expression(registerVars, inFunction, inMethod, true, variables); + forin = true; + } else { + lexer.pushback(s3); + if (s2 != null) { + lexer.pushback(s2); + } + lexer.pushback(s); + } + } else { + if (s2 != null) { + lexer.pushback(s2); + } + lexer.pushback(s); + } + } else { + lexer.pushback(s); + } + List forFinalCommands = new ArrayList<>(); + GraphTargetItem forExpr = null; + List forFirstCommands = new ArrayList<>(); + if (!forin) { + GraphTargetItem fc = command(registerVars, inFunction, inMethod, forinlevel, true, variables); + if (fc != null) { //can be empty command + forFirstCommands.add(fc); + } + forExpr = (expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.SEMICOLON); + forFinalCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + } + expectedType(SymbolType.PARENT_CLOSE); + List forBody = new ArrayList<>(); + forBody.add(command(registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true, variables)); + if (forin) { + ret = new ForInActionItem(null, null, pushConst(objIdent), collection, forBody); + } else { + ret = new ForItem(null, null, forFirstCommands, forExpr, forFinalCommands, forBody); + } + break; + case SWITCH: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem switchExpr = expression(registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.PARENT_CLOSE); + expectedType(SymbolType.CURLY_OPEN); + s = lex(); + //ret.addAll(switchExpr); + int exprReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + registerVars.put("__switch" + uniqId(), i); + exprReg = i; + break; + } + } + List> caseIfs = new ArrayList<>(); + List> caseCmds = new ArrayList<>(); + List caseExprsAll = new ArrayList<>(); + List valueMapping = new ArrayList<>(); + int pos = 0; + while (s.type == SymbolType.CASE) { + List caseExprs = new ArrayList<>(); + while (s.type == SymbolType.CASE) { + GraphTargetItem curCaseExpr = expression(registerVars, inFunction, inMethod, true, variables); + caseExprs.add(curCaseExpr); + expectedType(SymbolType.COLON); + s = lex(); + caseExprsAll.add(curCaseExpr); + valueMapping.add(pos); + } + pos++; + lexer.pushback(s); + List caseCmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); + caseCmds.add(caseCmd); + s = lex(); + } + List defCmd = new ArrayList<>(); + if (s.type == SymbolType.DEFAULT) { + expectedType(SymbolType.COLON); + defCmd = commands(registerVars, inFunction, inMethod, forinlevel, variables); + s = lexer.lex(); + } + expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE); + ret = new SwitchItem(null, null, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping); + break; + case BREAK: + ret = new BreakItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 + break; + case CONTINUE: + ret = new ContinueItem(null, 0); //? There is no more than 1 level continue/break in AS1/2 + break; + case RETURN: + GraphTargetItem retexpr = expression(true, registerVars, inFunction, inMethod, true, variables); + if (retexpr == null) { + retexpr = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); + } + ret = new ReturnActionItem(null, retexpr); + break; + case TRY: + List tryCommands = new ArrayList<>(); + tryCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + s = lex(); + boolean found = false; + List> catchCommands = null; + List catchExceptions = new ArrayList<>(); + if (s.type == SymbolType.CATCH) { + expectedType(SymbolType.PARENT_OPEN); + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING); + catchExceptions.add(pushConst((String) s.value)); + expectedType(SymbolType.PARENT_CLOSE); + catchCommands = new ArrayList<>(); + List cc = new ArrayList<>(); + cc.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + catchCommands.add(cc); + s = lex(); + found = true; + } + List finallyCommands = null; + if (s.type == SymbolType.FINALLY) { + finallyCommands = new ArrayList<>(); + finallyCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true, variables)); + found = true; + s = lex(); + } + if (!found) { + expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY); + } + lexer.pushback(s); + ret = new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands); + break; + case THROW: + ret = new ThrowActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + break; + default: + GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel, variables); + if (valcmd != null) { + ret = valcmd; + break; + } + if (s.type == SymbolType.SEMICOLON) { + return null; + } + lexer.pushback(s); + ret = expression(registerVars, inFunction, inMethod, true, variables); + if (debugMode) { + System.out.println("/command"); + } + } + if (debugMode) { + System.out.println("/command"); + } + lexer.removeListener(buf); + if (ret == null) { //can be popped expression + buf.pushAllBack(lexer); + ret = expression(registerVars, inFunction, inMethod, true, variables); + } + s = lex(); + if ((s != null) && (s.type != SymbolType.SEMICOLON)) { + lexer.pushback(s); + } + + return ret; + + } + + private GraphTargetItem expression(HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { + return expression(false, registerVars, inFunction, inMethod, allowRemainder, variables); + } + + private GraphTargetItem fixPrecedence(GraphTargetItem expr) { + GraphTargetItem ret = expr; + if (expr instanceof BinaryOp) { + BinaryOp bo = (BinaryOp) expr; + GraphTargetItem left = bo.getLeftSide(); + //GraphTargetItem right=bo.getRightSide(); + if (left.getPrecedence() > bo.getPrecedence()) { + if (left instanceof BinaryOp) { + BinaryOp leftBo = (BinaryOp) left; + bo.setLeftSide(leftBo.getRightSide()); + leftBo.setRightSide(expr); + return left; + } + } + } + return ret; + } + + private GraphTargetItem expressionRemainder(GraphTargetItem expr, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + switch (s.type) { + case TERNAR: + GraphTargetItem terOnTrue = expression(registerVars, inFunction, inMethod, true, variables); + expectedType(SymbolType.COLON); + GraphTargetItem terOnFalse = expression(registerVars, inFunction, inMethod, true, variables); + ret = new TernarOpItem(null, expr, terOnTrue, terOnFalse); + break; + case SHIFT_LEFT: + ret = new LShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case SHIFT_RIGHT: + ret = new RShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case USHIFT_RIGHT: + ret = new URShiftActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case BITAND: + ret = new BitAndActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case BITOR: + ret = new BitOrActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case DIVIDE: + ret = new DivideActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case MODULO: + ret = new ModuloActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case EQUALS: + ret = new EqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); + break; + case STRICT_EQUALS: + ret = new StrictEqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case NOT_EQUAL: + ret = new NeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); + break; + case STRICT_NOT_EQUAL: + ret = new StrictNeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case LOWER_THAN: + ret = new LtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); + break; + case LOWER_EQUAL: + ret = new LeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case GREATER_THAN: + ret = new GtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case GREATER_EQUAL: + ret = new GeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true/*FIXME SWF version?*/); + break; + case AND: + ret = new AndItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case OR: + ret = new OrItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case MINUS: + ret = new SubtractActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case MULTIPLY: + ret = new MultiplyActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case PLUS: + ret = new AddActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables), true); + break; + case XOR: + ret = new BitXorActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case AS: + + break; + case INSTANCEOF: + ret = new InstanceOfActionItem(null, expr, expression(registerVars, inFunction, inMethod, false, variables)); + break; + case IS: + + break; + + case ASSIGN: + case ASSIGN_BITAND: + case ASSIGN_BITOR: + case ASSIGN_DIVIDE: + case ASSIGN_MINUS: + case ASSIGN_MODULO: + case ASSIGN_MULTIPLY: + case ASSIGN_PLUS: + case ASSIGN_SHIFT_LEFT: + case ASSIGN_SHIFT_RIGHT: + case ASSIGN_USHIFT_RIGHT: + case ASSIGN_XOR: + GraphTargetItem assigned = expression(registerVars, inFunction, inMethod, true, variables); + switch (s.type) { + case ASSIGN: + //assigned = assigned; + break; + case ASSIGN_BITAND: + assigned = new BitAndActionItem(null, expr, assigned); + break; + case ASSIGN_BITOR: + assigned = new BitOrActionItem(null, expr, assigned); + break; + case ASSIGN_DIVIDE: + assigned = new DivideActionItem(null, expr, assigned); + break; + case ASSIGN_MINUS: + assigned = new SubtractActionItem(null, expr, assigned); + break; + case ASSIGN_MODULO: + assigned = new ModuloActionItem(null, expr, assigned); + break; + case ASSIGN_MULTIPLY: + assigned = new MultiplyActionItem(null, expr, assigned); + break; + case ASSIGN_PLUS: + assigned = new AddActionItem(null, expr, assigned, true/*TODO:SWF version?*/); + break; + case ASSIGN_SHIFT_LEFT: + assigned = new LShiftActionItem(null, expr, assigned); + break; + case ASSIGN_SHIFT_RIGHT: + assigned = new RShiftActionItem(null, expr, assigned); + break; + case ASSIGN_USHIFT_RIGHT: + assigned = new URShiftActionItem(null, expr, assigned); + break; + case ASSIGN_XOR: + assigned = new BitXorActionItem(null, expr, assigned); + break; + } + if (expr instanceof VariableActionItem) { + ((VariableActionItem) expr).setStoreValue(assigned); + ((VariableActionItem) expr).setDefinition(false); + ret = expr; + } else if (expr instanceof GetMemberActionItem) { + ret = new SetMemberActionItem(null, ((GetMemberActionItem) expr).object, ((GetMemberActionItem) expr).memberName, assigned); + } else { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + break; + case INCREMENT: //postincrement + if (!(expr instanceof VariableActionItem) && !(expr instanceof GetMemberActionItem)) { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostIncrementActionItem(null, expr); + break; + case DECREMENT: //postdecrement + if (!(expr instanceof VariableActionItem) && !(expr instanceof GetMemberActionItem)) { + throw new ParseException("Invalid assignment", lexer.yyline()); + } + ret = new PostDecrementActionItem(null, expr); + break; + case DOT: //member + case BRACKET_OPEN: //member + case PARENT_OPEN: //function call + lexer.pushback(s); + ret = memberOrCall(expr, registerVars, inFunction, inMethod, variables); + break; + case IDENTIFIER: + switch (s.value.toString()) { + case "add": + ret = new StringAddActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "eq": + ret = new StringEqActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "ne": + ret = new StringNeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "lt": + ret = new StringLtActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "ge": + ret = new StringGeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "gt": + ret = new StringGtActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + case "le": + ret = new StringLeActionItem(null, expr, expression(registerVars, inFunction, inMethod, allowRemainder, variables)); + break; + default: + lexer.pushback(s); + } + break; + default: + lexer.pushback(s); + } + + if (ret == null) { + if (expr instanceof ParenthesisItem) { + if (isType(((ParenthesisItem) expr).value)) { + GraphTargetItem expr2 = expression(false, registerVars, inFunction, inMethod, true, variables); + if (expr2 != null) { + ret = new CastOpActionItem(null, ((ParenthesisItem) expr).value, expr2); + } + } + } + } + + ret = fixPrecedence(ret); + return ret; + } + + private boolean isType(GraphTargetItem item) { + if (item == null) { + return false; + } + while (item instanceof GetMemberActionItem) { + item = ((GetMemberActionItem) item).object; + } + if (item instanceof VariableActionItem) { + return true; + } + return false; + } + + private int brackets(List ret, HashMap registerVars, boolean inFunction, boolean inMethod, List variables) throws IOException, ParseException { + ParsedSymbol s = lex(); + int arrCnt = 0; + if (s.type == SymbolType.BRACKET_OPEN) { + s = lex(); + + while (s.type != SymbolType.BRACKET_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + arrCnt++; + ret.add(expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE); + } + } + } else { + lexer.pushback(s); + return -1; + } + return arrCnt; + } + + private GraphTargetItem commaExpression(HashMap registerVars, boolean inFunction, boolean inMethod, int forInLevel, List variables) throws IOException, ParseException { + GraphTargetItem cmd = null; + List expr = new ArrayList<>(); + ParsedSymbol s; + do { + cmd = command(registerVars, inFunction, inMethod, forInLevel, false, variables); + if (cmd != null) { + expr.add(cmd); + } + s = lex(); + } while (s.type == SymbolType.COMMA && cmd != null); + lexer.pushback(s); + if (cmd == null) { + expr.add(expression(registerVars, inFunction, inMethod, true, variables)); + } else { + if (!cmd.hasReturnValue()) { + throw new ParseException("Expression expected", lexer.yyline()); + } + } + return new CommaExpressionItem(null, expr); + } + + private GraphTargetItem expression(boolean allowEmpty, HashMap registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder, List variables) throws IOException, ParseException { + if (debugMode) { + System.out.println("expression:"); + } + GraphTargetItem ret = null; + ParsedSymbol s = lex(); + boolean existsRemainder = false; + boolean assocRight = false; + switch (s.type) { + case NEGATE: + versionRequired(s, 5); + ret = expression(registerVars, inFunction, inMethod, false, variables); + ret = new BitXorActionItem(null, ret, new DirectValueActionItem(4.294967295E9)); + existsRemainder = true; + break; + case MINUS: + s = lex(); + if (s.isType(SymbolType.DOUBLE)) { + ret = new DirectValueActionItem(null, 0, -(double) (Double) s.value, new ArrayList()); + existsRemainder = true; + } else if (s.isType(SymbolType.INTEGER)) { + ret = new DirectValueActionItem(null, 0, -(long) (Long) s.value, new ArrayList()); + existsRemainder = true; + } else { + lexer.pushback(s); + GraphTargetItem num = expression(registerVars, inFunction, inMethod, true, variables); + if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Long)) { + ((DirectValueActionItem) num).value = -(Long) ((DirectValueActionItem) num).value; + ret = num; + } else if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Double)) { + Double d = (Double) ((DirectValueActionItem) num).value; + if (d.isInfinite()) { + ((DirectValueActionItem) num).value = Double.NEGATIVE_INFINITY; + } else { + ((DirectValueActionItem) num).value = -d; + } + ret = (num); + } else if ((num instanceof DirectValueActionItem) + && (((DirectValueActionItem) num).value instanceof Float)) { + ((DirectValueActionItem) num).value = -(Float) ((DirectValueActionItem) num).value; + ret = (num); + } else {; + ret = (new SubtractActionItem(null, new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList()), num)); + } + } + break; + case TYPEOF: + ret = new TypeOfActionItem(null, expression(registerVars, inFunction, inMethod, false, variables)); + existsRemainder = true; + break; + case TRUE: + ret = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList()); + existsRemainder = true; + break; + case NULL: + ret = new DirectValueActionItem(null, 0, new Null(), new ArrayList()); + existsRemainder = true; + break; + case UNDEFINED: + ret = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList()); + break; + case FALSE: + ret = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList()); + existsRemainder = true; + break; + case CURLY_OPEN: //Object literal + s = lex(); + List objectNames = new ArrayList<>(); + List objectValues = new ArrayList<>(); + while (s.type != SymbolType.CURLY_CLOSE) { + if (s.type != SymbolType.COMMA) { + lexer.pushback(s); + } + s = lex(); + expected(s, lexer.yyline(), SymbolType.IDENTIFIER); + objectNames.add(0, pushConst((String) s.value)); + expectedType(SymbolType.COLON); + objectValues.add(0, expression(registerVars, inFunction, inMethod, true, variables)); + s = lex(); + if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) { + expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE); + } + } + ret = new InitObjectActionItem(null, objectNames, objectValues); + break; + case BRACKET_OPEN: //Array literal or just brackets + lexer.pushback(s); + List inBrackets = new ArrayList<>(); + int arrCnt = brackets(inBrackets, registerVars, inFunction, inMethod, variables); + ret = new InitArrayActionItem(null, inBrackets); + break; + case FUNCTION: + s = lexer.lex(); + String fname = ""; + if (s.isType(SymbolType.IDENTIFIER, SymbolGroup.GLOBALFUNC)) { + fname = s.value.toString(); + } else { + lexer.pushback(s); + } + ret = function(true, fname, false, variables); + break; + case STRING: + ret = pushConst(s.value.toString()); + ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); + existsRemainder = true; + break; + case NEWLINE: + ret = new DirectValueActionItem(null, 0, "\r", new ArrayList()); + existsRemainder = true; + break; + case NAN: + ret = new DirectValueActionItem(null, 0, Double.NaN, new ArrayList()); + existsRemainder = true; + break; + case INFINITY: + ret = new DirectValueActionItem(null, 0, Double.POSITIVE_INFINITY, new ArrayList()); + existsRemainder = true; + break; + case INTEGER: + case DOUBLE: + ret = new DirectValueActionItem(null, 0, s.value, new ArrayList()); + existsRemainder = true; + break; + case DELETE: + GraphTargetItem varDel = variable(registerVars, inFunction, inMethod, variables); + if (varDel instanceof GetMemberActionItem) { + GetMemberActionItem gm = (GetMemberActionItem) varDel; + ret = new DeleteActionItem(null, gm.object, gm.memberName); + } else { + throw new ParseException("Not a property", lexer.yyline()); + } + break; + case INCREMENT: + case DECREMENT: //preincrement + GraphTargetItem prevar = variable(registerVars, inFunction, inMethod, variables); + if (s.type == SymbolType.INCREMENT) { + ret = new PreIncrementActionItem(null, prevar); + } + if (s.type == SymbolType.DECREMENT) { + ret = new PreDecrementActionItem(null, prevar); + } + existsRemainder = true; + break; + case NOT: + ret = new NotItem(null, expression(registerVars, inFunction, inMethod, false, variables)); + existsRemainder = true; + break; + case PARENT_OPEN: + ret = new ParenthesisItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + ret = memberOrCall(ret, registerVars, inFunction, inMethod, variables); + existsRemainder = true; + break; + case NEW: + GraphTargetItem newvar = variable(registerVars, inFunction, inMethod, variables); + expectedType(SymbolType.PARENT_OPEN); + if (newvar instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) newvar; + ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod, variables)); + } else if (newvar instanceof VariableActionItem) { + ret = new NewObjectActionItem(null, newvar, call(registerVars, inFunction, inMethod, variables)); + } else { + throw new ParseException("Invalid new item", lexer.yyline()); + } + existsRemainder = true; + break; + case EVAL: + expectedType(SymbolType.PARENT_OPEN); + GraphTargetItem evar = new EvalActionItem(null, expression(registerVars, inFunction, inMethod, true, variables)); + expectedType(SymbolType.PARENT_CLOSE); + evar = memberOrCall(evar, registerVars, inFunction, inMethod, variables); + ret = evar; + existsRemainder = true; + break; + case IDENTIFIER: + case THIS: + case SUPER: + if (s.value.equals("not")) { + ret = new NotItem(null, expression(registerVars, inFunction, inMethod, false, variables)); + } else { + lexer.pushback(s); + GraphTargetItem var = variable(registerVars, inFunction, inMethod, variables); + var = memberOrCall(var, registerVars, inFunction, inMethod, variables); + ret = var; + } + existsRemainder = true; + break; + default: + GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1, variables); + if (excmd != null) { + existsRemainder = true; //? + ret = excmd; + break; + } + lexer.pushback(s); + } + if (allowRemainder && existsRemainder) { + GraphTargetItem rem = ret; + do { + rem = expressionRemainder(rem, registerVars, inFunction, inMethod, assocRight, variables); + if (rem != null) { + ret = rem; + } + } while ((!assocRight) && (rem != null)); + } + if (debugMode) { + System.out.println("/expression"); + } + return ret; + } + + private DirectValueActionItem pushConst(String s) throws IOException, ParseException { + int index = constantPool.indexOf(s); + if (index == -1) { + constantPool.add(s); + index = constantPool.indexOf(s); + } + return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); + } + private ActionScriptLexer lexer = null; + private List constantPool; + + public List treeFromString(String str, List constantPool) throws ParseException, IOException { + List retTree = new ArrayList<>(); + this.constantPool = constantPool; + lexer = new ActionScriptLexer(new StringReader(str)); + + List vars = new ArrayList<>(); + retTree.addAll(commands(new HashMap(), false, false, 0, vars)); + for (VariableActionItem v : vars) { + String varName = v.getVariableName(); + GraphTargetItem stored = v.getStoreValue(); + if (v.isDefinition()) { + v.setBoxedValue(new DefineLocalActionItem(null, pushConst(varName), stored)); + } else { + if (stored != null) { + v.setBoxedValue(new SetVariableActionItem(null, pushConst(varName), stored)); + } else { + v.setBoxedValue(new GetVariableActionItem(null, pushConst(varName))); + } + } + } + if (lexer.lex().type != SymbolType.EOF) { + throw new ParseException("Parsing finisned before end of the file", lexer.yyline()); + } + return retTree; + } + + public List actionsFromTree(List tree, List constantPool) throws CompilationException { + ActionSourceGenerator gen = new ActionSourceGenerator(swfVersion, constantPool); + List ret = new ArrayList<>(); + SourceGeneratorLocalData localData = new SourceGeneratorLocalData( + new HashMap(), 0, Boolean.FALSE, 0); + List srcList = gen.generate(localData, tree); + for (GraphSourceItem s : srcList) { + if (s instanceof Action) { + ret.add((Action) s); + } + } + ret.add(0, new ActionConstantPool(constantPool)); + return ret; + } + + public List actionsFromString(String s) throws ParseException, IOException, CompilationException { + List constantPool = new ArrayList<>(); + List tree = treeFromString(s, constantPool); + return actionsFromTree(tree, constantPool); + } + + private void versionRequired(ParsedSymbol s, int min) throws ParseException { + versionRequired(s.value.toString(), min, Integer.MAX_VALUE); + } + + private void versionRequired(ParsedSymbol s, int min, int max) throws ParseException { + versionRequired(s.value.toString(), min, max); + } + + private void versionRequired(String type, int min, int max) throws ParseException { + if (min == max && swfVersion != min) { + throw new ParseException(type + " requires SWF version " + min, lexer.yyline()); + } + if (swfVersion < min) { + throw new ParseException(type + " requires at least SWF version " + min, lexer.yyline()); + } + if (swfVersion > max) { + throw new ParseException(type + " requires SWF version lower than " + max, lexer.yyline()); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/action/parser/script/ActionSourceGenerator.java b/src/com/jpexs/decompiler/flash/action/parser/script/ActionSourceGenerator.java index f02b27fec..acd78ed4a 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/script/ActionSourceGenerator.java +++ b/src/com/jpexs/decompiler/flash/action/parser/script/ActionSourceGenerator.java @@ -1,782 +1,780 @@ -/* - * Copyright (C) 2010-2014 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.action.parser.script; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.operations.Inverted; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionJump; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf4.ActionPop; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; -import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; -import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; -import com.jpexs.decompiler.flash.action.swf7.ActionExtends; -import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.SourceGenerator; -import com.jpexs.decompiler.graph.TypeItem; -import com.jpexs.decompiler.graph.model.AndItem; -import com.jpexs.decompiler.graph.model.BreakItem; -import com.jpexs.decompiler.graph.model.CommaExpressionItem; -import com.jpexs.decompiler.graph.model.ContinueItem; -import com.jpexs.decompiler.graph.model.DoWhileItem; -import com.jpexs.decompiler.graph.model.DuplicateItem; -import com.jpexs.decompiler.graph.model.ForItem; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.NotItem; -import com.jpexs.decompiler.graph.model.OrItem; -import com.jpexs.decompiler.graph.model.SwitchItem; -import com.jpexs.decompiler.graph.model.TernarOpItem; -import com.jpexs.decompiler.graph.model.WhileItem; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class ActionSourceGenerator implements SourceGenerator { - - @Override - public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - ret.add(new ActionPushDuplicate()); - ret.add(new ActionNot()); - List andExpr = generateToActionList(localData, item.rightSide); - andExpr.add(0, new ActionPop()); - int andExprLen = Action.actionsToBytes(andExpr, false, SWF.DEFAULT_VERSION).length; - ret.add(new ActionIf(andExprLen)); - ret.addAll(andExpr); - return ret; - - } - - @Override - public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { - List ret = new ArrayList<>(); - ret.addAll(generateToActionList(localData, item.leftSide)); - ret.add(new ActionPushDuplicate()); - List orExpr = generateToActionList(localData, item.rightSide); - orExpr.add(0, new ActionPop()); - int orExprLen = Action.actionsToBytes(orExpr, false, SWF.DEFAULT_VERSION).length; - ret.add(new ActionIf(orExprLen)); - ret.addAll(orExpr); - return ret; - } - - public List toActionList(List items) { - List ret = new ArrayList<>(); - for (GraphSourceItem s : items) { - if (s instanceof Action) { - ret.add((Action) s); - } - } - return ret; - } - - private List nonempty(List list) { - if (list == null) { - return new ArrayList<>(); - } - return list; - } - - private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { - List ret = new ArrayList<>(); - if (expression instanceof Inverted) { - ret.addAll(((Inverted) expression).invert().toSource(localData, this)); - } else { - ret.addAll(expression.toSource(localData, this)); - ret.add(new ActionNot()); - } - List onTrue = null; - List onFalse = null; - if (ternar) { - onTrue = toActionList(onTrueCmds.get(0).toSource(localData, this)); - } else { - onTrue = generateToActionList(localData, onTrueCmds); - } - - if (onFalseCmds != null && !onFalseCmds.isEmpty()) { - if (ternar) { - onFalse = toActionList(onFalseCmds.get(0).toSource(localData, this)); - } else { - onFalse = generateToActionList(localData, onFalseCmds); - } - } - byte[] onTrueBytes = Action.actionsToBytes(onTrue, false, SWF.DEFAULT_VERSION); - int onTrueLen = onTrueBytes.length; - - ActionIf ifaif = new ActionIf(0); - ret.add(ifaif); - ret.addAll(onTrue); - ifaif.setJumpOffset(onTrueLen); - ActionJump ajmp = null; - if (onFalse != null) { - if (!((!nonempty(onTrue).isEmpty()) - && (onTrue.get(onTrue.size() - 1) instanceof ActionJump) - && ((((ActionJump) onTrue.get(onTrue.size() - 1)).isContinue) - || (((ActionJump) onTrue.get(onTrue.size() - 1)).isBreak)))) { - ajmp = new ActionJump(0); - ret.add(ajmp); - onTrueLen += ajmp.getBytes(SWF.DEFAULT_VERSION).length; - } - ifaif.setJumpOffset(onTrueLen); - byte[] onFalseBytes = Action.actionsToBytes(onFalse, false, SWF.DEFAULT_VERSION); - int onFalseLen = onFalseBytes.length; - if (ajmp != null) { - ajmp.setJumpOffset(onFalseLen); - } - ret.addAll(onFalse); - } - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { - return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); - } - - private void fixLoop(List code, int breakOffset) { - fixLoop(code, breakOffset, Integer.MAX_VALUE); - } - - private void fixLoop(List code, int breakOffset, int continueOffset) { - int pos = 0; - for (Action a : code) { - pos += a.getBytes(SWF.DEFAULT_VERSION).length; - if (a instanceof ActionJump) { - ActionJump aj = (ActionJump) a; - if (aj.isContinue && (continueOffset != Integer.MAX_VALUE)) { - aj.setJumpOffset(-pos + continueOffset); - aj.isContinue = false; - } - if (aj.isBreak) { - aj.setJumpOffset(-pos + breakOffset); - aj.isBreak = false; - } - } - } - } - - @Override - public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { - List onTrue = new ArrayList<>(); - onTrue.add(item.onTrue); - List onFalse = new ArrayList<>(); - onFalse.add(item.onFalse); - return generateIf(localData, item.expression, onTrue, onFalse, true); - } - - @Override - public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List whileExpr = new ArrayList<>(); - - List ex = new ArrayList<>(item.expression); - if (!ex.isEmpty()) { - GraphTargetItem lastItem = ex.remove(ex.size() - 1); - whileExpr.addAll(generateToActionList(localData, ex)); - whileExpr.addAll(toActionList(lastItem.toSource(localData, this))); //Want result - } - - List whileBody = generateToActionList(localData, item.commands); - whileExpr.add(new ActionNot()); - ActionIf whileaif = new ActionIf(0); - whileExpr.add(whileaif); - ActionJump whileajmp = new ActionJump(0); - whileBody.add(whileajmp); - int whileExprLen = Action.actionsToBytes(whileExpr, false, SWF.DEFAULT_VERSION).length; - int whileBodyLen = Action.actionsToBytes(whileBody, false, SWF.DEFAULT_VERSION).length; - whileajmp.setJumpOffset(-(whileExprLen - + whileBodyLen)); - whileaif.setJumpOffset(whileBodyLen); - ret.addAll(whileExpr); - fixLoop(whileBody, whileBodyLen, -whileExprLen); - ret.addAll(whileBody); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { - List ret = new ArrayList<>(); - List doExpr = generateToActionList(localData, item.expression); - List doBody = generateToActionList(localData, item.commands); - - int doBodyLen = Action.actionsToBytes(doBody, false, SWF.DEFAULT_VERSION).length; - int doExprLen = Action.actionsToBytes(doExpr, false, SWF.DEFAULT_VERSION).length; - - ret.addAll(doBody); - ret.addAll(doExpr); - ActionIf doif = new ActionIf(0); - ret.add(doif); - doif.setJumpOffset(-doBodyLen - doExprLen - doif.getBytes(SWF.DEFAULT_VERSION).length); - fixLoop(doBody, doBodyLen + doExprLen + doif.getBytes(SWF.DEFAULT_VERSION).length, doBodyLen); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { - List ret = new ArrayList<>(); - List forExpr = generateToActionList(localData, item.expression); - List forBody = generateToActionList(localData, item.commands); - List forFinalCommands = generateToActionList(localData, item.finalCommands); - - forExpr.add(new ActionNot()); - ActionIf foraif = new ActionIf(0); - forExpr.add(foraif); - ActionJump forajmp = new ActionJump(0); - int forajmpLen = forajmp.getBytes(SWF.DEFAULT_VERSION).length; - int forExprLen = Action.actionsToBytes(forExpr, false, SWF.DEFAULT_VERSION).length; - int forBodyLen = Action.actionsToBytes(forBody, false, SWF.DEFAULT_VERSION).length; - int forFinalLen = Action.actionsToBytes(forFinalCommands, false, SWF.DEFAULT_VERSION).length; - forajmp.setJumpOffset(-(forExprLen - + forBodyLen + forFinalLen + forajmpLen)); - foraif.setJumpOffset(forBodyLen + forFinalLen + forajmpLen); - ret.addAll(forExpr); - ret.addAll(forBody); - ret.addAll(forFinalCommands); - ret.add(forajmp); - fixLoop(forBody, forBodyLen + forFinalLen + forajmpLen, forBodyLen); - return ret; - } - private long uniqLast = 0; - - public String uniqId() { - uniqLast++; - return "" + uniqLast; - } - - @Override - public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { - List ret = new ArrayList<>(); - HashMap registerVars = getRegisterVars(localData); - int exprReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - registerVars.put("__switch" + uniqId(), i); - exprReg = i; - break; - } - } - - ret.addAll(toActionList(item.switchedObject.toSource(localData, this))); - - boolean firstCase = true; - List> caseIfs = new ArrayList<>(); - List> caseCmds = new ArrayList<>(); - List>> caseExprsAll = new ArrayList<>(); - - loopm: - for (int m = 0; m < item.caseValues.size(); m++) { - List> caseExprs = new ArrayList<>(); - List caseIfsOne = new ArrayList<>(); - int mapping = item.valuesMapping.get(m); - for (; m < item.caseValues.size(); m++) { - int newmapping = item.valuesMapping.get(m); - if (newmapping != mapping) { - m--; - break; - } - List curCaseExpr = generateToActionList(localData, item.caseValues.get(m)); - caseExprs.add(curCaseExpr); - if (firstCase) { - curCaseExpr.add(0, new ActionStoreRegister(exprReg)); - } else { - curCaseExpr.add(0, new ActionPush(new RegisterNumber(exprReg))); - } - curCaseExpr.add(new ActionStrictEquals()); - ActionIf aif = new ActionIf(0); - caseIfsOne.add(aif); - curCaseExpr.add(aif); - ret.addAll(curCaseExpr); - firstCase = false; - } - caseExprsAll.add(caseExprs); - caseIfs.add(caseIfsOne); - List caseCmd = generateToActionList(localData, item.caseCommands.get(mapping)); - caseCmds.add(caseCmd); - } - ActionJump defJump = new ActionJump(0); - ret.add(defJump); - List defCmd = new ArrayList<>(); - if (!item.defaultCommands.isEmpty()) { - defCmd = generateToActionList(localData, item.defaultCommands); - } - for (List caseCmd : caseCmds) { - ret.addAll(caseCmd); - } - ret.addAll(defCmd); - - List> exprLengths = new ArrayList<>(); - for (List> caseExprs : caseExprsAll) { - List lengths = new ArrayList<>(); - for (List caseExpr : caseExprs) { - lengths.add(Action.actionsToBytes(caseExpr, false, SWF.DEFAULT_VERSION).length); - } - exprLengths.add(lengths); - } - List caseLengths = new ArrayList<>(); - for (List caseCmd : caseCmds) { - caseLengths.add(Action.actionsToBytes(caseCmd, false, SWF.DEFAULT_VERSION).length); - } - int defLength = Action.actionsToBytes(defCmd, false, SWF.DEFAULT_VERSION).length; - - for (int i = 0; i < caseIfs.size(); i++) { - for (int c = 0; c < caseIfs.get(i).size(); c++) { - int jmpPos = 0; - for (int j = c + 1; j < caseIfs.get(i).size(); j++) { - jmpPos += exprLengths.get(i).get(j); - } - for (int k = i + 1; k < caseIfs.size(); k++) { - for (int m = 0; m < caseIfs.get(k).size(); m++) { - jmpPos += exprLengths.get(k).get(m); - } - } - jmpPos += defJump.getBytes(SWF.DEFAULT_VERSION).length; - for (int n = 0; n < i; n++) { - jmpPos += caseLengths.get(n); - } - caseIfs.get(i).get(c).setJumpOffset(jmpPos); - } - } - int defJmpPos = 0; - for (int i = 0; i < caseIfs.size(); i++) { - defJmpPos += caseLengths.get(i); - } - - defJump.setJumpOffset(defJmpPos); - List caseCmdsAll = new ArrayList<>(); - int breakOffset = 0; - for (int i = 0; i < caseCmds.size(); i++) { - caseCmdsAll.addAll(caseCmds.get(i)); - breakOffset += caseLengths.get(i); - } - breakOffset += defLength; - fixLoop(caseCmdsAll, breakOffset); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { - if (item.getOriginal() instanceof Inverted) { - GraphTargetItem norig = ((Inverted) item).invert(); - return norig.toSource(localData, this); - } - List ret = new ArrayList<>(); - ret.addAll(item.getOriginal().toSource(localData, this)); - ret.add(new ActionNot()); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { - List ret = new ArrayList<>(); - ret.add(new ActionPushDuplicate()); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, BreakItem item) { - List ret = new ArrayList<>(); - ActionJump abreak = new ActionJump(0); - abreak.isBreak = true; - ret.add(abreak); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, ContinueItem item) { - List ret = new ArrayList<>(); - ActionJump acontinue = new ActionJump(0); - acontinue.isContinue = true; - ret.add(acontinue); - return ret; - } - - private List generateToActionList(SourceGeneratorLocalData localData, List commands) throws CompilationException { - return toActionList(generate(localData, commands)); - } - - private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { - return toActionList(command.toSource(localData, this)); - } - - @Override - public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { - List ret = new ArrayList<>(); - for (GraphTargetItem item : commands) { - ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); - } - return ret; - } - - public HashMap getRegisterVars(SourceGeneratorLocalData localData) { - return localData.registerVars; - } - - public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { - localData.registerVars = value; - } - - public void setInFunction(SourceGeneratorLocalData localData, int value) { - localData.inFunction = value; - } - - public int isInFunction(SourceGeneratorLocalData localData) { - return localData.inFunction; - } - - public boolean isInMethod(SourceGeneratorLocalData localData) { - return localData.inMethod; - } - - public void setInMethod(SourceGeneratorLocalData localData, boolean value) { - localData.inMethod = value; - } - - public int getForInLevel(SourceGeneratorLocalData localData) { - return localData.forInLevel; - } - - public void setForInLevel(SourceGeneratorLocalData localData, int value) { - localData.forInLevel = value; - } - - public int getTempRegister(SourceGeneratorLocalData localData) { - HashMap registerVars = getRegisterVars(localData); - int tmpReg = 0; - for (int i = 0; i < 256; i++) { - if (!registerVars.containsValue(i)) { - tmpReg = i; - break; - } - } - return tmpReg; - } - - private String getName(GraphTargetItem item) { - if (item instanceof VariableActionItem) { - return ((VariableActionItem) item).getVariableName(); - } - if (item instanceof DirectValueActionItem) { - DirectValueActionItem dv = (DirectValueActionItem) item; - return (String) dv.getResult(); - } - if (item instanceof GetVariableActionItem) { - GetVariableActionItem gv = (GetVariableActionItem) item; - return getName(gv.name); - } - if (item instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) item; - return getName(mem.memberName); - } - return null; - } - - private List getVarParts(GraphTargetItem item) { - List ret = new ArrayList<>(); - do { - if (item instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) item; - ret.add(0, getName(mem)); - item = mem.object; - } - } while (item instanceof GetMemberActionItem); - String f = getName(item); - if (f != null) { - ret.add(0, f); - } - return ret; - } - - private int getVarLength(GraphTargetItem item) { - int len = 1; - do { - if (item instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) item; - item = mem.object; - len++; - } - } while (item instanceof GetMemberActionItem); - return len; - } - - private GraphTargetItem removeVarLast(GraphTargetItem item, int cnt) { - item = Helper.deepCopy(item); - - for (int i = 0; i < cnt; i++) { - if (item instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) item; - item = mem.object; - } - } - return item; - } - - private GraphTargetItem addGlobalPrefix(GraphTargetItem item) { - item = Helper.deepCopy(item); - GraphTargetItem first = item; - GetMemberActionItem mem = null; - do { - if (item instanceof GetMemberActionItem) { - mem = (GetMemberActionItem) item; - item = mem.object; - } - } while (item instanceof GetMemberActionItem); - if (item instanceof GetVariableActionItem) { - GetVariableActionItem v = (GetVariableActionItem) item; - item = new GetMemberActionItem(null, new GetVariableActionItem(null, new DirectValueActionItem(null, 0, "_global", new ArrayList())), v.name); - if (mem != null) { - mem.object = item; - } - } - return first; - } - - private List typeToActions(List type, List value) { - List ret = new ArrayList<>(); - if (type.isEmpty()) { - return ret; - } - ret.add(pushConst(type.get(0))); - if (type.size() == 1 && (value != null)) { - ret.addAll(value); - ret.add(new ActionSetVariable()); - } else { - ret.add(new ActionGetVariable()); - } - for (int i = 1; i < type.size(); i++) { - ret.add(pushConst(type.get(i))); - if ((i == type.size() - 1) && (value != null)) { - ret.addAll(value); - ret.add(new ActionSetMember()); - } else { - ret.add(new ActionGetMember()); - } - } - return ret; - } - private final List constantPool; - private int swfVersion; - - public int getSwfVersion() { - return swfVersion; - } - - - - public ActionSourceGenerator(int swfVersion,List constantPool) { - this.constantPool = constantPool; - this.swfVersion = swfVersion; - } - - public List getConstantPool() { - return constantPool; - } - - public DirectValueActionItem pushConstTargetItem(String s) { - int index = constantPool.indexOf(s); - if (index == -1) { - constantPool.add(s); - index = constantPool.indexOf(s); - } - return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); - } - - public ActionPush pushConst(String s) { - int index = constantPool.indexOf(s); - if (index == -1) { - constantPool.add(s); - index = constantPool.indexOf(s); - } - return new ActionPush(new ConstantIndex(index)); - } - - public List generateTraits(SourceGeneratorLocalData localData, boolean isInterface, GraphTargetItem name, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List functions, List> vars, List staticFunctions, List> staticVars) throws CompilationException { - List extendsStr = getVarParts(extendsVal); - List ret = new ArrayList<>(); - List nameStr = getVarParts(name); - for (int i = 0; i < nameStr.size() - 1; i++) { - List notBody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - for (int j = 0; j <= i; j++) { - globalClassTypeStr.add(nameStr.get(j)); - } - - List val = new ArrayList<>(); - val.add(new ActionPush((Long) 0L)); - val.add(pushConst("Object")); - val.add(new ActionNewObject()); - notBody.addAll(typeToActions(globalClassTypeStr, val)); - ret.addAll(typeToActions(globalClassTypeStr, null)); - ret.add(new ActionNot()); - ret.add(new ActionNot()); - ret.add(new ActionIf(Action.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); - ret.addAll(notBody); - } - List ifbody = new ArrayList<>(); - List globalClassTypeStr = new ArrayList<>(); - globalClassTypeStr.add("_global"); - globalClassTypeStr.addAll(nameStr); - - ParsedSymbol s = null; - List constr = new ArrayList<>(); - - if (constructor == null) { - List val = new ArrayList<>(); - val.add(new ActionDefineFunction("", new ArrayList(), 0, SWF.DEFAULT_VERSION)); - if (!isInterface) { - val.add(new ActionStoreRegister(1)); - } - constr.addAll(typeToActions(globalClassTypeStr, val)); - } else { - constr.addAll(toActionList(((FunctionActionItem) constructor).toSource(localData, this))); - constr.add(new ActionStoreRegister(1)); - constr = (typeToActions(globalClassTypeStr, constr)); - } - if (!isInterface) { - for (GraphTargetItem f : staticFunctions) { - FunctionActionItem fi = (FunctionActionItem) f; - ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); - ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); - ifbody.addAll(toActionList(fi.toSource(localData, this))); - ifbody.add(new ActionSetMember()); - } - for (GraphTargetItem f : functions) { - FunctionActionItem fi = (FunctionActionItem) f; - ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); - ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); - ifbody.addAll(toActionList(fi.toSource(localData, this))); - ifbody.add(new ActionSetMember()); - } - for (MyEntry en : staticVars) { - ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); - ifbody.add(new ActionPush(getName(en.key))); - ifbody.addAll(toActionList(en.value.toSource(localData, this))); - ifbody.add(new ActionSetMember()); - } - for (MyEntry en : vars) { - ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); - ifbody.add(new ActionPush(getName(en.key))); - ifbody.addAll(toActionList(en.value.toSource(localData, this))); - ifbody.add(new ActionSetMember()); - } - } - - if (!isInterface) { - ifbody.add(new ActionPush((Long) 1L)); - ifbody.add(new ActionPush(new Null())); - ifbody.addAll(typeToActions(globalClassTypeStr, null)); - ifbody.add(pushConst("prototype")); - ifbody.add(new ActionGetMember()); - ifbody.add(new ActionPush((Long) 3L)); - ifbody.add(pushConst("ASSetPropFlags")); - ifbody.add(new ActionCallFunction()); - } - - if (constr.isEmpty()) { - List val = new ArrayList<>(); - val.add(new ActionDefineFunction("", new ArrayList(), 0, SWF.DEFAULT_VERSION)); - if (!isInterface) { - val.add(new ActionStoreRegister(1)); - } - constr.addAll(typeToActions(globalClassTypeStr, val)); - } - if (!extendsStr.isEmpty()) { - constr.addAll(typeToActions(globalClassTypeStr, null)); - constr.addAll(typeToActions(extendsStr, null)); - constr.add(new ActionExtends()); - } - if (!isInterface) { - constr.add(new ActionPush(new RegisterNumber(1))); - constr.add(pushConst("prototype")); - constr.add(new ActionGetMember()); - constr.add(new ActionStoreRegister(2)); - constr.add(new ActionPop()); - } - - if (!implementsStr.isEmpty()) { - for (GraphTargetItem imp : implementsStr) { - List impList = getVarParts(imp); - List globImp = new ArrayList<>(); - globImp.add("_global"); - globImp.addAll(impList); - constr.addAll(typeToActions(globImp, null)); - } - constr.add(new ActionPush(new Long(implementsStr.size()))); - constr.addAll(typeToActions(globalClassTypeStr, null)); - constr.add(new ActionImplementsOp()); - } - ifbody.addAll(0, constr); - - ret.addAll(typeToActions(globalClassTypeStr, null)); - ret.add(new ActionNot()); - ret.add(new ActionNot()); - ret.add(new ActionIf(Action.actionsToBytes(ifbody, false, SWF.DEFAULT_VERSION).length)); - ret.addAll(ifbody); - ret.add(new ActionPop()); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { - if (item.commands.isEmpty()) { - return new ArrayList<>(); - } - - //We need to handle commands and last expression separately, otherwise last expression result will be popped - List cmds = new ArrayList<>(item.commands); - GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); - List ret = new ArrayList<>(); - ret.addAll(generate(localData, cmds)); - ret.addAll(lastExpr.toSource(localData, this)); - return ret; - } - - @Override - public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { - //Unsupported in AS1/2 - return new ArrayList<>(); - } - -} +/* + * Copyright (C) 2010-2014 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.action.parser.script; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.operations.Inverted; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionJump; +import com.jpexs.decompiler.flash.action.swf4.ActionNot; +import com.jpexs.decompiler.flash.action.swf4.ActionPop; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; +import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; +import com.jpexs.decompiler.flash.action.swf7.ActionExtends; +import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.TypeItem; +import com.jpexs.decompiler.graph.model.AndItem; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.CommaExpressionItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.DoWhileItem; +import com.jpexs.decompiler.graph.model.DuplicateItem; +import com.jpexs.decompiler.graph.model.ForItem; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.NotItem; +import com.jpexs.decompiler.graph.model.OrItem; +import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TernarOpItem; +import com.jpexs.decompiler.graph.model.WhileItem; +import com.jpexs.helpers.Helper; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class ActionSourceGenerator implements SourceGenerator { + + @Override + public List generate(SourceGeneratorLocalData localData, AndItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + ret.add(new ActionPushDuplicate()); + ret.add(new ActionNot()); + List andExpr = generateToActionList(localData, item.rightSide); + andExpr.add(0, new ActionPop()); + int andExprLen = Action.actionsToBytes(andExpr, false, SWF.DEFAULT_VERSION).length; + ret.add(new ActionIf(andExprLen)); + ret.addAll(andExpr); + return ret; + + } + + @Override + public List generate(SourceGeneratorLocalData localData, OrItem item) throws CompilationException { + List ret = new ArrayList<>(); + ret.addAll(generateToActionList(localData, item.leftSide)); + ret.add(new ActionPushDuplicate()); + List orExpr = generateToActionList(localData, item.rightSide); + orExpr.add(0, new ActionPop()); + int orExprLen = Action.actionsToBytes(orExpr, false, SWF.DEFAULT_VERSION).length; + ret.add(new ActionIf(orExprLen)); + ret.addAll(orExpr); + return ret; + } + + public List toActionList(List items) { + List ret = new ArrayList<>(); + for (GraphSourceItem s : items) { + if (s instanceof Action) { + ret.add((Action) s); + } + } + return ret; + } + + private List nonempty(List list) { + if (list == null) { + return new ArrayList<>(); + } + return list; + } + + private List generateIf(SourceGeneratorLocalData localData, GraphTargetItem expression, List onTrueCmds, List onFalseCmds, boolean ternar) throws CompilationException { + List ret = new ArrayList<>(); + if (expression instanceof Inverted) { + ret.addAll(((Inverted) expression).invert().toSource(localData, this)); + } else { + ret.addAll(expression.toSource(localData, this)); + ret.add(new ActionNot()); + } + List onTrue = null; + List onFalse = null; + if (ternar) { + onTrue = toActionList(onTrueCmds.get(0).toSource(localData, this)); + } else { + onTrue = generateToActionList(localData, onTrueCmds); + } + + if (onFalseCmds != null && !onFalseCmds.isEmpty()) { + if (ternar) { + onFalse = toActionList(onFalseCmds.get(0).toSource(localData, this)); + } else { + onFalse = generateToActionList(localData, onFalseCmds); + } + } + byte[] onTrueBytes = Action.actionsToBytes(onTrue, false, SWF.DEFAULT_VERSION); + int onTrueLen = onTrueBytes.length; + + ActionIf ifaif = new ActionIf(0); + ret.add(ifaif); + ret.addAll(onTrue); + ifaif.setJumpOffset(onTrueLen); + ActionJump ajmp = null; + if (onFalse != null) { + if (!((!nonempty(onTrue).isEmpty()) + && (onTrue.get(onTrue.size() - 1) instanceof ActionJump) + && ((((ActionJump) onTrue.get(onTrue.size() - 1)).isContinue) + || (((ActionJump) onTrue.get(onTrue.size() - 1)).isBreak)))) { + ajmp = new ActionJump(0); + ret.add(ajmp); + onTrueLen += ajmp.getBytes(SWF.DEFAULT_VERSION).length; + } + ifaif.setJumpOffset(onTrueLen); + byte[] onFalseBytes = Action.actionsToBytes(onFalse, false, SWF.DEFAULT_VERSION); + int onFalseLen = onFalseBytes.length; + if (ajmp != null) { + ajmp.setJumpOffset(onFalseLen); + } + ret.addAll(onFalse); + } + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, IfItem item) throws CompilationException { + return generateIf(localData, item.expression, item.onTrue, item.onFalse, false); + } + + private void fixLoop(List code, int breakOffset) { + fixLoop(code, breakOffset, Integer.MAX_VALUE); + } + + private void fixLoop(List code, int breakOffset, int continueOffset) { + int pos = 0; + for (Action a : code) { + pos += a.getBytes(SWF.DEFAULT_VERSION).length; + if (a instanceof ActionJump) { + ActionJump aj = (ActionJump) a; + if (aj.isContinue && (continueOffset != Integer.MAX_VALUE)) { + aj.setJumpOffset(-pos + continueOffset); + aj.isContinue = false; + } + if (aj.isBreak) { + aj.setJumpOffset(-pos + breakOffset); + aj.isBreak = false; + } + } + } + } + + @Override + public List generate(SourceGeneratorLocalData localData, TernarOpItem item) throws CompilationException { + List onTrue = new ArrayList<>(); + onTrue.add(item.onTrue); + List onFalse = new ArrayList<>(); + onFalse.add(item.onFalse); + return generateIf(localData, item.expression, onTrue, onFalse, true); + } + + @Override + public List generate(SourceGeneratorLocalData localData, WhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List whileExpr = new ArrayList<>(); + + List ex = new ArrayList<>(item.expression); + if (!ex.isEmpty()) { + GraphTargetItem lastItem = ex.remove(ex.size() - 1); + whileExpr.addAll(generateToActionList(localData, ex)); + whileExpr.addAll(toActionList(lastItem.toSource(localData, this))); //Want result + } + + List whileBody = generateToActionList(localData, item.commands); + whileExpr.add(new ActionNot()); + ActionIf whileaif = new ActionIf(0); + whileExpr.add(whileaif); + ActionJump whileajmp = new ActionJump(0); + whileBody.add(whileajmp); + int whileExprLen = Action.actionsToBytes(whileExpr, false, SWF.DEFAULT_VERSION).length; + int whileBodyLen = Action.actionsToBytes(whileBody, false, SWF.DEFAULT_VERSION).length; + whileajmp.setJumpOffset(-(whileExprLen + + whileBodyLen)); + whileaif.setJumpOffset(whileBodyLen); + ret.addAll(whileExpr); + fixLoop(whileBody, whileBodyLen, -whileExprLen); + ret.addAll(whileBody); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DoWhileItem item) throws CompilationException { + List ret = new ArrayList<>(); + List doExpr = generateToActionList(localData, item.expression); + List doBody = generateToActionList(localData, item.commands); + + int doBodyLen = Action.actionsToBytes(doBody, false, SWF.DEFAULT_VERSION).length; + int doExprLen = Action.actionsToBytes(doExpr, false, SWF.DEFAULT_VERSION).length; + + ret.addAll(doBody); + ret.addAll(doExpr); + ActionIf doif = new ActionIf(0); + ret.add(doif); + doif.setJumpOffset(-doBodyLen - doExprLen - doif.getBytes(SWF.DEFAULT_VERSION).length); + fixLoop(doBody, doBodyLen + doExprLen + doif.getBytes(SWF.DEFAULT_VERSION).length, doBodyLen); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ForItem item) throws CompilationException { + List ret = new ArrayList<>(); + List forExpr = generateToActionList(localData, item.expression); + List forBody = generateToActionList(localData, item.commands); + List forFinalCommands = generateToActionList(localData, item.finalCommands); + + forExpr.add(new ActionNot()); + ActionIf foraif = new ActionIf(0); + forExpr.add(foraif); + ActionJump forajmp = new ActionJump(0); + int forajmpLen = forajmp.getBytes(SWF.DEFAULT_VERSION).length; + int forExprLen = Action.actionsToBytes(forExpr, false, SWF.DEFAULT_VERSION).length; + int forBodyLen = Action.actionsToBytes(forBody, false, SWF.DEFAULT_VERSION).length; + int forFinalLen = Action.actionsToBytes(forFinalCommands, false, SWF.DEFAULT_VERSION).length; + forajmp.setJumpOffset(-(forExprLen + + forBodyLen + forFinalLen + forajmpLen)); + foraif.setJumpOffset(forBodyLen + forFinalLen + forajmpLen); + ret.addAll(forExpr); + ret.addAll(forBody); + ret.addAll(forFinalCommands); + ret.add(forajmp); + fixLoop(forBody, forBodyLen + forFinalLen + forajmpLen, forBodyLen); + return ret; + } + private long uniqLast = 0; + + public String uniqId() { + uniqLast++; + return "" + uniqLast; + } + + @Override + public List generate(SourceGeneratorLocalData localData, SwitchItem item) throws CompilationException { + List ret = new ArrayList<>(); + HashMap registerVars = getRegisterVars(localData); + int exprReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + registerVars.put("__switch" + uniqId(), i); + exprReg = i; + break; + } + } + + ret.addAll(toActionList(item.switchedObject.toSource(localData, this))); + + boolean firstCase = true; + List> caseIfs = new ArrayList<>(); + List> caseCmds = new ArrayList<>(); + List>> caseExprsAll = new ArrayList<>(); + + loopm: + for (int m = 0; m < item.caseValues.size(); m++) { + List> caseExprs = new ArrayList<>(); + List caseIfsOne = new ArrayList<>(); + int mapping = item.valuesMapping.get(m); + for (; m < item.caseValues.size(); m++) { + int newmapping = item.valuesMapping.get(m); + if (newmapping != mapping) { + m--; + break; + } + List curCaseExpr = generateToActionList(localData, item.caseValues.get(m)); + caseExprs.add(curCaseExpr); + if (firstCase) { + curCaseExpr.add(0, new ActionStoreRegister(exprReg)); + } else { + curCaseExpr.add(0, new ActionPush(new RegisterNumber(exprReg))); + } + curCaseExpr.add(new ActionStrictEquals()); + ActionIf aif = new ActionIf(0); + caseIfsOne.add(aif); + curCaseExpr.add(aif); + ret.addAll(curCaseExpr); + firstCase = false; + } + caseExprsAll.add(caseExprs); + caseIfs.add(caseIfsOne); + List caseCmd = generateToActionList(localData, item.caseCommands.get(mapping)); + caseCmds.add(caseCmd); + } + ActionJump defJump = new ActionJump(0); + ret.add(defJump); + List defCmd = new ArrayList<>(); + if (!item.defaultCommands.isEmpty()) { + defCmd = generateToActionList(localData, item.defaultCommands); + } + for (List caseCmd : caseCmds) { + ret.addAll(caseCmd); + } + ret.addAll(defCmd); + + List> exprLengths = new ArrayList<>(); + for (List> caseExprs : caseExprsAll) { + List lengths = new ArrayList<>(); + for (List caseExpr : caseExprs) { + lengths.add(Action.actionsToBytes(caseExpr, false, SWF.DEFAULT_VERSION).length); + } + exprLengths.add(lengths); + } + List caseLengths = new ArrayList<>(); + for (List caseCmd : caseCmds) { + caseLengths.add(Action.actionsToBytes(caseCmd, false, SWF.DEFAULT_VERSION).length); + } + int defLength = Action.actionsToBytes(defCmd, false, SWF.DEFAULT_VERSION).length; + + for (int i = 0; i < caseIfs.size(); i++) { + for (int c = 0; c < caseIfs.get(i).size(); c++) { + int jmpPos = 0; + for (int j = c + 1; j < caseIfs.get(i).size(); j++) { + jmpPos += exprLengths.get(i).get(j); + } + for (int k = i + 1; k < caseIfs.size(); k++) { + for (int m = 0; m < caseIfs.get(k).size(); m++) { + jmpPos += exprLengths.get(k).get(m); + } + } + jmpPos += defJump.getBytes(SWF.DEFAULT_VERSION).length; + for (int n = 0; n < i; n++) { + jmpPos += caseLengths.get(n); + } + caseIfs.get(i).get(c).setJumpOffset(jmpPos); + } + } + int defJmpPos = 0; + for (int i = 0; i < caseIfs.size(); i++) { + defJmpPos += caseLengths.get(i); + } + + defJump.setJumpOffset(defJmpPos); + List caseCmdsAll = new ArrayList<>(); + int breakOffset = 0; + for (int i = 0; i < caseCmds.size(); i++) { + caseCmdsAll.addAll(caseCmds.get(i)); + breakOffset += caseLengths.get(i); + } + breakOffset += defLength; + fixLoop(caseCmdsAll, breakOffset); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, NotItem item) throws CompilationException { + if (item.getOriginal() instanceof Inverted) { + GraphTargetItem norig = ((Inverted) item).invert(); + return norig.toSource(localData, this); + } + List ret = new ArrayList<>(); + ret.addAll(item.getOriginal().toSource(localData, this)); + ret.add(new ActionNot()); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, DuplicateItem item) { + List ret = new ArrayList<>(); + ret.add(new ActionPushDuplicate()); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, BreakItem item) { + List ret = new ArrayList<>(); + ActionJump abreak = new ActionJump(0); + abreak.isBreak = true; + ret.add(abreak); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, ContinueItem item) { + List ret = new ArrayList<>(); + ActionJump acontinue = new ActionJump(0); + acontinue.isContinue = true; + ret.add(acontinue); + return ret; + } + + private List generateToActionList(SourceGeneratorLocalData localData, List commands) throws CompilationException { + return toActionList(generate(localData, commands)); + } + + private List generateToActionList(SourceGeneratorLocalData localData, GraphTargetItem command) throws CompilationException { + return toActionList(command.toSource(localData, this)); + } + + @Override + public List generate(SourceGeneratorLocalData localData, List commands) throws CompilationException { + List ret = new ArrayList<>(); + for (GraphTargetItem item : commands) { + ret.addAll(item.toSourceIgnoreReturnValue(localData, this)); + } + return ret; + } + + public HashMap getRegisterVars(SourceGeneratorLocalData localData) { + return localData.registerVars; + } + + public void setRegisterVars(SourceGeneratorLocalData localData, HashMap value) { + localData.registerVars = value; + } + + public void setInFunction(SourceGeneratorLocalData localData, int value) { + localData.inFunction = value; + } + + public int isInFunction(SourceGeneratorLocalData localData) { + return localData.inFunction; + } + + public boolean isInMethod(SourceGeneratorLocalData localData) { + return localData.inMethod; + } + + public void setInMethod(SourceGeneratorLocalData localData, boolean value) { + localData.inMethod = value; + } + + public int getForInLevel(SourceGeneratorLocalData localData) { + return localData.forInLevel; + } + + public void setForInLevel(SourceGeneratorLocalData localData, int value) { + localData.forInLevel = value; + } + + public int getTempRegister(SourceGeneratorLocalData localData) { + HashMap registerVars = getRegisterVars(localData); + int tmpReg = 0; + for (int i = 0; i < 256; i++) { + if (!registerVars.containsValue(i)) { + tmpReg = i; + break; + } + } + return tmpReg; + } + + private String getName(GraphTargetItem item) { + if (item instanceof VariableActionItem) { + return ((VariableActionItem) item).getVariableName(); + } + if (item instanceof DirectValueActionItem) { + DirectValueActionItem dv = (DirectValueActionItem) item; + return (String) dv.getResult(); + } + if (item instanceof GetVariableActionItem) { + GetVariableActionItem gv = (GetVariableActionItem) item; + return getName(gv.name); + } + if (item instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) item; + return getName(mem.memberName); + } + return null; + } + + private List getVarParts(GraphTargetItem item) { + List ret = new ArrayList<>(); + do { + if (item instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) item; + ret.add(0, getName(mem)); + item = mem.object; + } + } while (item instanceof GetMemberActionItem); + String f = getName(item); + if (f != null) { + ret.add(0, f); + } + return ret; + } + + private int getVarLength(GraphTargetItem item) { + int len = 1; + do { + if (item instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) item; + item = mem.object; + len++; + } + } while (item instanceof GetMemberActionItem); + return len; + } + + private GraphTargetItem removeVarLast(GraphTargetItem item, int cnt) { + item = Helper.deepCopy(item); + + for (int i = 0; i < cnt; i++) { + if (item instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) item; + item = mem.object; + } + } + return item; + } + + private GraphTargetItem addGlobalPrefix(GraphTargetItem item) { + item = Helper.deepCopy(item); + GraphTargetItem first = item; + GetMemberActionItem mem = null; + do { + if (item instanceof GetMemberActionItem) { + mem = (GetMemberActionItem) item; + item = mem.object; + } + } while (item instanceof GetMemberActionItem); + if (item instanceof GetVariableActionItem) { + GetVariableActionItem v = (GetVariableActionItem) item; + item = new GetMemberActionItem(null, new GetVariableActionItem(null, new DirectValueActionItem(null, 0, "_global", new ArrayList())), v.name); + if (mem != null) { + mem.object = item; + } + } + return first; + } + + private List typeToActions(List type, List value) { + List ret = new ArrayList<>(); + if (type.isEmpty()) { + return ret; + } + ret.add(pushConst(type.get(0))); + if (type.size() == 1 && (value != null)) { + ret.addAll(value); + ret.add(new ActionSetVariable()); + } else { + ret.add(new ActionGetVariable()); + } + for (int i = 1; i < type.size(); i++) { + ret.add(pushConst(type.get(i))); + if ((i == type.size() - 1) && (value != null)) { + ret.addAll(value); + ret.add(new ActionSetMember()); + } else { + ret.add(new ActionGetMember()); + } + } + return ret; + } + private final List constantPool; + private int swfVersion; + + public int getSwfVersion() { + return swfVersion; + } + + public ActionSourceGenerator(int swfVersion, List constantPool) { + this.constantPool = constantPool; + this.swfVersion = swfVersion; + } + + public List getConstantPool() { + return constantPool; + } + + public DirectValueActionItem pushConstTargetItem(String s) { + int index = constantPool.indexOf(s); + if (index == -1) { + constantPool.add(s); + index = constantPool.indexOf(s); + } + return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool); + } + + public ActionPush pushConst(String s) { + int index = constantPool.indexOf(s); + if (index == -1) { + constantPool.add(s); + index = constantPool.indexOf(s); + } + return new ActionPush(new ConstantIndex(index)); + } + + public List generateTraits(SourceGeneratorLocalData localData, boolean isInterface, GraphTargetItem name, GraphTargetItem extendsVal, List implementsStr, GraphTargetItem constructor, List functions, List> vars, List staticFunctions, List> staticVars) throws CompilationException { + List extendsStr = getVarParts(extendsVal); + List ret = new ArrayList<>(); + List nameStr = getVarParts(name); + for (int i = 0; i < nameStr.size() - 1; i++) { + List notBody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + for (int j = 0; j <= i; j++) { + globalClassTypeStr.add(nameStr.get(j)); + } + + List val = new ArrayList<>(); + val.add(new ActionPush((Long) 0L)); + val.add(pushConst("Object")); + val.add(new ActionNewObject()); + notBody.addAll(typeToActions(globalClassTypeStr, val)); + ret.addAll(typeToActions(globalClassTypeStr, null)); + ret.add(new ActionNot()); + ret.add(new ActionNot()); + ret.add(new ActionIf(Action.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length)); + ret.addAll(notBody); + } + List ifbody = new ArrayList<>(); + List globalClassTypeStr = new ArrayList<>(); + globalClassTypeStr.add("_global"); + globalClassTypeStr.addAll(nameStr); + + ParsedSymbol s = null; + List constr = new ArrayList<>(); + + if (constructor == null) { + List val = new ArrayList<>(); + val.add(new ActionDefineFunction("", new ArrayList(), 0, SWF.DEFAULT_VERSION)); + if (!isInterface) { + val.add(new ActionStoreRegister(1)); + } + constr.addAll(typeToActions(globalClassTypeStr, val)); + } else { + constr.addAll(toActionList(((FunctionActionItem) constructor).toSource(localData, this))); + constr.add(new ActionStoreRegister(1)); + constr = (typeToActions(globalClassTypeStr, constr)); + } + if (!isInterface) { + for (GraphTargetItem f : staticFunctions) { + FunctionActionItem fi = (FunctionActionItem) f; + ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); + ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); + ifbody.addAll(toActionList(fi.toSource(localData, this))); + ifbody.add(new ActionSetMember()); + } + for (GraphTargetItem f : functions) { + FunctionActionItem fi = (FunctionActionItem) f; + ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); + ifbody.add(new ActionPush(getName(fi.calculatedFunctionName))); + ifbody.addAll(toActionList(fi.toSource(localData, this))); + ifbody.add(new ActionSetMember()); + } + for (MyEntry en : staticVars) { + ifbody.add(new ActionPush(new RegisterNumber(1/*static*/))); + ifbody.add(new ActionPush(getName(en.key))); + ifbody.addAll(toActionList(en.value.toSource(localData, this))); + ifbody.add(new ActionSetMember()); + } + for (MyEntry en : vars) { + ifbody.add(new ActionPush(new RegisterNumber(2/*instance*/))); + ifbody.add(new ActionPush(getName(en.key))); + ifbody.addAll(toActionList(en.value.toSource(localData, this))); + ifbody.add(new ActionSetMember()); + } + } + + if (!isInterface) { + ifbody.add(new ActionPush((Long) 1L)); + ifbody.add(new ActionPush(new Null())); + ifbody.addAll(typeToActions(globalClassTypeStr, null)); + ifbody.add(pushConst("prototype")); + ifbody.add(new ActionGetMember()); + ifbody.add(new ActionPush((Long) 3L)); + ifbody.add(pushConst("ASSetPropFlags")); + ifbody.add(new ActionCallFunction()); + } + + if (constr.isEmpty()) { + List val = new ArrayList<>(); + val.add(new ActionDefineFunction("", new ArrayList(), 0, SWF.DEFAULT_VERSION)); + if (!isInterface) { + val.add(new ActionStoreRegister(1)); + } + constr.addAll(typeToActions(globalClassTypeStr, val)); + } + if (!extendsStr.isEmpty()) { + constr.addAll(typeToActions(globalClassTypeStr, null)); + constr.addAll(typeToActions(extendsStr, null)); + constr.add(new ActionExtends()); + } + if (!isInterface) { + constr.add(new ActionPush(new RegisterNumber(1))); + constr.add(pushConst("prototype")); + constr.add(new ActionGetMember()); + constr.add(new ActionStoreRegister(2)); + constr.add(new ActionPop()); + } + + if (!implementsStr.isEmpty()) { + for (GraphTargetItem imp : implementsStr) { + List impList = getVarParts(imp); + List globImp = new ArrayList<>(); + globImp.add("_global"); + globImp.addAll(impList); + constr.addAll(typeToActions(globImp, null)); + } + constr.add(new ActionPush(new Long(implementsStr.size()))); + constr.addAll(typeToActions(globalClassTypeStr, null)); + constr.add(new ActionImplementsOp()); + } + ifbody.addAll(0, constr); + + ret.addAll(typeToActions(globalClassTypeStr, null)); + ret.add(new ActionNot()); + ret.add(new ActionNot()); + ret.add(new ActionIf(Action.actionsToBytes(ifbody, false, SWF.DEFAULT_VERSION).length)); + ret.addAll(ifbody); + ret.add(new ActionPop()); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, CommaExpressionItem item) throws CompilationException { + if (item.commands.isEmpty()) { + return new ArrayList<>(); + } + + //We need to handle commands and last expression separately, otherwise last expression result will be popped + List cmds = new ArrayList<>(item.commands); + GraphTargetItem lastExpr = cmds.remove(cmds.size() - 1); + List ret = new ArrayList<>(); + ret.addAll(generate(localData, cmds)); + ret.addAll(lastExpr.toSource(localData, this)); + return ret; + } + + @Override + public List generate(SourceGeneratorLocalData localData, TypeItem item) throws CompilationException { + //Unsupported in AS1/2 + return new ArrayList<>(); + } + +} diff --git a/src/com/jpexs/decompiler/flash/action/swf3/ActionPlay.java b/src/com/jpexs/decompiler/flash/action/swf3/ActionPlay.java index 79a764448..3e1f49ddd 100644 --- a/src/com/jpexs/decompiler/flash/action/swf3/ActionPlay.java +++ b/src/com/jpexs/decompiler/flash/action/swf3/ActionPlay.java @@ -1,49 +1,49 @@ -/* - * Copyright (C) 2010-2014 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.action.swf3; - -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; -import com.jpexs.decompiler.flash.action.model.GotoFrameActionItem; -import com.jpexs.decompiler.flash.action.model.PlayActionItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class ActionPlay extends Action { - - public ActionPlay() { - super(0x06, 0); - } - - @Override - public String toString() { - return "Play"; - } - - @Override - public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { - if(!output.isEmpty() && (output.get(output.size()-1) instanceof GotoFrameActionItem)){ - GotoFrameActionItem gta=(GotoFrameActionItem)output.remove(output.size()-1); - output.add(new GotoFrame2ActionItem(this, new DirectValueActionItem(gta.frame+1), false, true, 0)); - }else{ - output.add(new PlayActionItem(this)); - } - } -} +/* + * Copyright (C) 2010-2014 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.action.swf3; + +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem; +import com.jpexs.decompiler.flash.action.model.GotoFrameActionItem; +import com.jpexs.decompiler.flash.action.model.PlayActionItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class ActionPlay extends Action { + + public ActionPlay() { + super(0x06, 0); + } + + @Override + public String toString() { + return "Play"; + } + + @Override + public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { + if (!output.isEmpty() && (output.get(output.size() - 1) instanceof GotoFrameActionItem)) { + GotoFrameActionItem gta = (GotoFrameActionItem) output.remove(output.size() - 1); + output.add(new GotoFrame2ActionItem(this, new DirectValueActionItem(gta.frame + 1), false, true, 0)); + } else { + output.add(new PlayActionItem(this)); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/action/swf6/ActionStringGreater.java b/src/com/jpexs/decompiler/flash/action/swf6/ActionStringGreater.java index a818aa638..2c8ce0033 100644 --- a/src/com/jpexs/decompiler/flash/action/swf6/ActionStringGreater.java +++ b/src/com/jpexs/decompiler/flash/action/swf6/ActionStringGreater.java @@ -1,44 +1,43 @@ -/* - * Copyright (C) 2010-2014 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.action.swf6; - -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.model.operations.GtActionItem; -import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; -import com.jpexs.decompiler.graph.GraphTargetItem; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; - -public class ActionStringGreater extends Action { - - public ActionStringGreater() { - super(0x68, 0); - } - - @Override - public String toString() { - return "StringGreater"; - } - - @Override - public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { - GraphTargetItem a = stack.pop(); - GraphTargetItem b = stack.pop(); - stack.push(new StringGtActionItem(this, b, a)); - } -} +/* + * Copyright (C) 2010-2014 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.action.swf6; + +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.model.operations.StringGtActionItem; +import com.jpexs.decompiler.graph.GraphTargetItem; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +public class ActionStringGreater extends Action { + + public ActionStringGreater() { + super(0x68, 0); + } + + @Override + public String toString() { + return "StringGreater"; + } + + @Override + public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) { + GraphTargetItem a = stack.pop(); + GraphTargetItem b = stack.pop(); + stack.push(new StringGtActionItem(this, b, a)); + } +} diff --git a/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/src/com/jpexs/decompiler/flash/configuration/Configuration.java index bcd858020..40c086555 100644 --- a/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -1,693 +1,693 @@ -/* - * Copyright (C) 2010-2014 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.configuration; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.helpers.CodeFormatting; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8InputStreamReader; -import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; -import com.jpexs.proxy.Replacement; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -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.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JOptionPane; - -public class Configuration { - - private static final String CONFIG_NAME = "config.bin"; - private static final String REPLACEMENTS_NAME = "replacements.cfg"; - private static final File unspecifiedFile = new File("unspecified"); - private static File directory = unspecifiedFile; - - /** - * List of replacements - */ - private static List replacements = new ArrayList<>(); - - public static final Level logLevel; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem openMultipleFiles = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("script") - public static final ConfigurationItem decompile = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem parallelSpeedUp = null; - - @ConfigurationDefaultInt(20) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem parallelThreadCount = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem autoDeobfuscate = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("decompilation") - public static final ConfigurationItem cacheOnDisk = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("display") - public static final ConfigurationItem internalFlashViewer = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("ui") - public static final ConfigurationItem gotoMainClassOnStartup = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem autoRenameIdentifiers = null; - - @ConfigurationDefaultBoolean(false) - public static final ConfigurationItem offeredAssociation = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem decimalAddress = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem showAllAddresses = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("display") - public static final ConfigurationItem useFrameCache = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem useRibbonInterface = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem openFolderAfterFlaExport = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem useDetailedLogging = null; - - @ConfigurationDefaultInt(65536) - @ConfigurationCategory("display") - public static final ConfigurationItem binaryDataDisplayLimit = null; - - /** - * Debug mode = throwing an error when comparing original file and - * recompiled - */ - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem debugMode = null; - /** - * Turn off resolving constants in ActionScript 2 - */ - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("script") - public static final ConfigurationItem resolveConstants = null; - /** - * Limit of code subs (for obfuscated code) - */ - @ConfigurationDefaultInt(500) - @ConfigurationCategory("limit") - public static final ConfigurationItem sublimiter = null; - /** - * Total export timeout in seconds - */ - @ConfigurationDefaultInt(30 * 60) - @ConfigurationCategory("limit") - public static final ConfigurationItem exportTimeout = null; - /** - * Decompilation timeout in seconds for a single file - */ - @ConfigurationDefaultInt(5 * 60) - @ConfigurationCategory("limit") - public static final ConfigurationItem decompilationTimeoutFile = null; - /** - * Using parameter names in decompiling may cause problems because official - * programs like Flash CS 5.5 inserts wrong parameter names indices - */ - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("script") - public static final ConfigurationItem paramNamesEnable = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("ui") - public static final ConfigurationItem displayFileName = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem debugCopy = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("debug") - public static final ConfigurationItem dumpTags = null; - - @ConfigurationDefaultInt(60) - @ConfigurationCategory("limit") - public static final ConfigurationItem decompilationTimeoutSingleMethod = null; - @ConfigurationDefaultInt(1) - public static final ConfigurationItem lastRenameType = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastSaveDir = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastOpenDir = null; - - @ConfigurationDefaultString(".") - public static final ConfigurationItem lastExportDir = null; - - @ConfigurationDefaultString("en") - @ConfigurationCategory("ui") - public static final ConfigurationItem locale = null; - - @ConfigurationDefaultString("_loc%d_") - @ConfigurationCategory("script") - public static final ConfigurationItem registerNameFormat = null; - - @ConfigurationDefaultInt(10) - public static final ConfigurationItem maxRecentFileCount = null; - - public static final ConfigurationItem recentFiles = null; - - public static final ConfigurationItem fontPairing = null; - - public static final ConfigurationItem lastUpdatesCheckDate = null; - - @ConfigurationDefaultInt(1000) - @ConfigurationName("gui.window.width") - public static final ConfigurationItem guiWindowWidth = null; - @ConfigurationDefaultInt(700) - @ConfigurationName("gui.window.height") - public static final ConfigurationItem guiWindowHeight = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationName("gui.window.maximized.horizontal") - public static final ConfigurationItem guiWindowMaximizedHorizontal = null; - @ConfigurationDefaultBoolean(false) - @ConfigurationName("gui.window.maximized.vertical") - public static final ConfigurationItem guiWindowMaximizedVertical = null; - @ConfigurationName("gui.avm2.splitPane.dividerLocation") - public static final ConfigurationItem guiAvm2SplitPaneDividerLocation = null; - @ConfigurationName("guiActionSplitPaneDividerLocation") - public static final ConfigurationItem guiActionSplitPaneDividerLocation = null; - @ConfigurationName("guiPreviewSplitPaneDividerLocation") - public static final ConfigurationItem guiPreviewSplitPaneDividerLocation = null; - @ConfigurationName("gui.splitPane1.dividerLocation") - public static final ConfigurationItem guiSplitPane1DividerLocation = null; - @ConfigurationName("gui.splitPane2.dividerLocation") - public static final ConfigurationItem guiSplitPane2DividerLocation = null; - @ConfigurationDefaultInt(3) - @ConfigurationCategory("export") - public static final ConfigurationItem saveAsExeScaleMode = null; - - @ConfigurationDefaultInt(1024 * 100/*100KB*/) - @ConfigurationCategory("limit") - public static final ConfigurationItem syntaxHighlightLimit = null; - public static final ConfigurationItem guiFontPreviewSampleText = null; - @ConfigurationName("gui.fontPreviewWindow.width") - public static final ConfigurationItem guiFontPreviewWidth = null; - @ConfigurationName("gui.fontPreviewWindow.height") - public static final ConfigurationItem guiFontPreviewHeight = null; - @ConfigurationName("gui.fontPreviewWindow.posX") - public static final ConfigurationItem guiFontPreviewPosX = null; - @ConfigurationName("gui.fontPreviewWindow.posY") - public static final ConfigurationItem guiFontPreviewPosY = null; - - @ConfigurationDefaultInt(3) - @ConfigurationName("formatting.indent.size") - @ConfigurationCategory("format") - public static final ConfigurationItem indentSize = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationName("formatting.indent.useTabs") - @ConfigurationCategory("format") - public static final ConfigurationItem indentUseTabs = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("format") - public static final ConfigurationItem beginBlockOnNewLine = null; - - @ConfigurationDefaultInt(1000 * 60 * 60 * 24) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.delay") - public static final ConfigurationItem checkForUpdatesDelay = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.stable") - public static final ConfigurationItem checkForUpdatesStable = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.nightly") - public static final ConfigurationItem checkForUpdatesNightly = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("update") - @ConfigurationName("check.updates.enabled") - public static final ConfigurationItem checkForUpdatesAuto = null; - - @ConfigurationDefaultString("") - @ConfigurationName("export.formats") - public static final ConfigurationItem lastSelectedExportFormats = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFile = null; - - @ConfigurationDefaultString("--- SEPARATOR ---") - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFileSeparator = null; - - @ConfigurationDefaultString("--- RECORDSEPARATOR ---") - @ConfigurationCategory("export") - public static final ConfigurationItem textExportSingleFileRecordSeparator = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationName("warning.experimental.as12edit") - @ConfigurationCategory("script") - public static final ConfigurationItem warningExperimentalAS12Edit = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationName("warning.experimental.as3edit") - @ConfigurationCategory("script") - public static final ConfigurationItem warningExperimentalAS3Edit = null; - - @ConfigurationDefaultBoolean(true) - @ConfigurationCategory("export") - public static final ConfigurationItem packJavaScripts = null; - - @ConfigurationDefaultBoolean(false) - @ConfigurationCategory("export") - public static final ConfigurationItem textExportExportFontFace = null; - - private enum OSId { - - WINDOWS, OSX, UNIX - } - - private static OSId getOSId() { - PrivilegedAction doGetOSName = new PrivilegedAction() { - @Override - public String run() { - return System.getProperty("os.name"); - } - }; - OSId id = OSId.UNIX; - String osName = AccessController.doPrivileged(doGetOSName); - if (osName != null) { - if (osName.toLowerCase().startsWith("mac os x")) { - id = OSId.OSX; - } else if (osName.contains("Windows")) { - id = OSId.WINDOWS; - } - } - return id; - } - - public static String getFFDecHome() throws IOException { - if (directory == unspecifiedFile) { - directory = null; - String userHome = null; - try { - userHome = System.getProperty("user.home"); - } catch (SecurityException ignore) { - } - if (userHome != null) { - String applicationId = ApplicationInfo.SHORT_APPLICATION_NAME; - OSId osId = getOSId(); - if (osId == OSId.WINDOWS) { - File appDataDir = null; - try { - String appDataEV = System.getenv("APPDATA"); - if ((appDataEV != null) && (appDataEV.length() > 0)) { - appDataDir = new File(appDataEV); - } - } catch (SecurityException ignore) { - } - String vendorId = ApplicationInfo.VENDOR; - if ((appDataDir != null) && appDataDir.isDirectory()) { - // ${APPDATA}\{vendorId}\${applicationId} - String path = vendorId + "\\" + applicationId + "\\"; - directory = new File(appDataDir, path); - } else { - // ${userHome}\Application Data\${vendorId}\${applicationId} - String path = "Application Data\\" + vendorId + "\\" + applicationId + "\\"; - directory = new File(userHome, path); - } - } else if (osId == OSId.OSX) { - // ${userHome}/Library/Application Support/${applicationId} - String path = "Library/Application Support/" + applicationId + "/"; - directory = new File(userHome, path); - } else { - // ${userHome}/.${applicationId}/ - String path = "." + applicationId + "/"; - directory = new File(userHome, path); - } - } - } - if (!directory.exists()) { - if (!directory.mkdirs()) { - if (!directory.exists()) { - throw new IOException("cannot create directory " + directory); - } - } - } - String ret = directory.getAbsolutePath(); - if (!ret.endsWith(File.separator)) { - ret += File.separator; - } - return ret; - } - - public static List getRecentFiles() { - String files = recentFiles.get(); - if (files == null || files.isEmpty()) { - return new ArrayList<>(); - } - return Arrays.asList(files.split("::")); - } - - public static void addRecentFile(String path) { - List recentFilesArray = new ArrayList<>(getRecentFiles()); - int idx = recentFilesArray.indexOf(path); - if (idx != -1) { - recentFilesArray.remove(idx); - } - recentFilesArray.add(path); - while (recentFilesArray.size() > maxRecentFileCount.get()) { - recentFilesArray.remove(0); - } - recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); - } - - public static void removeRecentFile(String path) { - List recentFilesArray = new ArrayList<>(getRecentFiles()); - int idx = recentFilesArray.indexOf(path); - if (idx != -1) { - recentFilesArray.remove(idx); - } - recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); - } - - public static Map getFontPairs() { - String fonts = fontPairing.get(); - if (fonts == null) { - return new HashMap<>(); - } - - Map result = new HashMap<>(); - for (String pair : fonts.split("::")) { - String[] splittedPair = pair.split("="); - result.put(splittedPair[0], splittedPair[1]); - } - return result; - } - - public static void addFontPair(String fileName, int fontId, String fontName, String systemFontName) { - String key = fileName + "_" + fontId + "_" + fontName; - Map fontPairs = getFontPairs(); - fontPairs.put(key, systemFontName); - fontPairs.put(fontName, systemFontName); - StringBuilder sb = new StringBuilder(); - int i = 0; - for (Entry pair : fontPairs.entrySet()) { - if (i != 0) { - sb.append("::"); - } - sb.append(pair.getKey()).append("=").append(pair.getValue()); - i++; - } - fontPairing.set(sb.toString()); - } - - /** - * Saves replacements to file for future use - */ - private static void saveReplacements(String replacementsFile) { - if (replacements.isEmpty()) { - File rf = new File(replacementsFile); - if (rf.exists()) { - if (!rf.delete()) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); - } - } - } else { - try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { - for (Replacement r : replacements) { - pw.println(r.urlPattern); - pw.println(r.targetFile); - } - } catch (IOException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); - } - } - } - - /** - * Load replacements from file - */ - private static void loadReplacements(String replacementsFile) { - if (!(new File(replacementsFile)).exists()) { - return; - } - replacements = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { - String s; - while ((s = br.readLine()) != null) { - Replacement r = new Replacement(s, br.readLine()); - replacements.add(r); - } - } catch (IOException e) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Error during load replacements", e); - } - } - - private static String getReplacementsFile() throws IOException { - return getFFDecHome() + REPLACEMENTS_NAME; - } - - private static String getConfigFile() throws IOException { - return getFFDecHome() + CONFIG_NAME; - } - - private static HashMap loadFromFile(String file, String replacementsFile) { - HashMap config = new HashMap<>(); - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { - - @SuppressWarnings("unchecked") - HashMap cfg = (HashMap) ois.readObject(); - config = cfg; - } catch (FileNotFoundException ex) { - } catch (ClassNotFoundException cnf) { - } catch (IOException ex) { - } - if (replacementsFile != null) { - loadReplacements(replacementsFile); - } - if (config.containsKey("paralelSpeedUp")) { - config.put("parallelSpeedUp", config.get("paralelSpeedUp")); - config.remove("paralelSpeedUp"); - } - return config; - } - - private static void saveToFile(String file, String replacementsFile) { - HashMap config = new HashMap<>(); - for (Entry entry : getConfigurationFields().entrySet()) { - try { - String name = entry.getKey(); - Field field = entry.getValue(); - ConfigurationItem item = (ConfigurationItem) field.get(null); - if (item.hasValue) { - config.put(name, item.get()); - } - } catch (IllegalArgumentException | IllegalAccessException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } - } - try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { - oos.writeObject(config); - } catch (IOException ex) { - JOptionPane.showMessageDialog(null, "Cannot save configuration.", "Error", JOptionPane.ERROR_MESSAGE); - Logger.getLogger(Configuration.class.getName()).severe("Configuration directory is read only."); - } - if (replacementsFile != null) { - saveReplacements(replacementsFile); - } - } - - public static List getReplacements() { - return replacements; - } - - public static void saveConfig() { - try { - saveToFile(getConfigFile(), getReplacementsFile()); - } catch (IOException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } - } - - static { - setConfigurationFields(); - if (useDetailedLogging.get() || debugMode.get()) { - logLevel = Level.CONFIG; - } else { - logLevel = Level.WARNING; - } - int processorCount = Runtime.getRuntime().availableProcessors(); - if (parallelThreadCount.get() > processorCount) { - parallelThreadCount.set(processorCount); - } - } - - @SuppressWarnings("unchecked") - public static void setConfigurationFields() { - try { - HashMap config = loadFromFile(getConfigFile(), getReplacementsFile()); - for (Entry entry : getConfigurationFields().entrySet()) { - String name = entry.getKey(); - Field field = entry.getValue(); - // remove final modifier from field - Field modifiersField = field.getClass().getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - - Object defaultValue = getDefaultValue(field); - Object value = null; - if (config.containsKey(name)) { - value = config.get(name); - } - - if (value != null) { - field.set(null, new ConfigurationItem(name, defaultValue, value)); - } else { - field.set(null, new ConfigurationItem(name, defaultValue)); - } - } - } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } catch (IOException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public static Object getDefaultValue(Field field) { - Object defaultValue = null; - ConfigurationDefaultBoolean aBool = field.getAnnotation(ConfigurationDefaultBoolean.class); - if (aBool != null) { - defaultValue = aBool.value(); - } - ConfigurationDefaultInt aInt = field.getAnnotation(ConfigurationDefaultInt.class); - if (aInt != null) { - defaultValue = aInt.value(); - } - ConfigurationDefaultString aString = field.getAnnotation(ConfigurationDefaultString.class); - if (aString != null) { - defaultValue = aString.value(); - } - return defaultValue; - } - - public static Map getConfigurationFields() { - Field[] fields = Configuration.class.getFields(); - Map result = new HashMap<>(); - for (Field field : fields) { - if (ConfigurationItem.class.isAssignableFrom(field.getType())) { - ConfigurationName annotation = field.getAnnotation(ConfigurationName.class); - String name = annotation == null ? field.getName() : annotation.value(); - result.put(name, field); - } - } - return result; - } - - public static CodeFormatting getCodeFormatting() { - CodeFormatting ret = new CodeFormatting(); - String indentString = ""; - for (int i = 0; i < indentSize.get(); i++) { - indentString += indentUseTabs.get() ? "\t" : " "; - } - ret.indentString = indentString; - ret.beginBlockOnNewLine = beginBlockOnNewLine.get(); - return ret; - } - - public static File getFlashLibPath() { - try { - String home = getFFDecHome(); - File libsdir = new File(home + "flashlib"); - if (!libsdir.exists()) { - libsdir.mkdirs(); - } - return libsdir; - } catch (IOException ex) { - return null; - } - - } - - public static File getPlayerSWC() { - File libsdir = getFlashLibPath(); - if (libsdir != null && libsdir.exists()) { - File libs[] = libsdir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - return name.toLowerCase().startsWith("playerglobal"); - } - }); - List libnames = new ArrayList<>(); - for (File f : libs) { - libnames.add(f.getName()); - } - Collections.sort(libnames); - if (!libnames.isEmpty()) { - return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); - } else { - return null; - } - } - - return null; - } -} +/* + * Copyright (C) 2010-2014 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.configuration; + +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.helpers.CodeFormatting; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8InputStreamReader; +import com.jpexs.helpers.utf8.Utf8OutputStreamWriter; +import com.jpexs.proxy.Replacement; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +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.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; + +public class Configuration { + + private static final String CONFIG_NAME = "config.bin"; + private static final String REPLACEMENTS_NAME = "replacements.cfg"; + private static final File unspecifiedFile = new File("unspecified"); + private static File directory = unspecifiedFile; + + /** + * List of replacements + */ + private static List replacements = new ArrayList<>(); + + public static final Level logLevel; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem openMultipleFiles = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("script") + public static final ConfigurationItem decompile = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem parallelSpeedUp = null; + + @ConfigurationDefaultInt(20) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem parallelThreadCount = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem autoDeobfuscate = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("decompilation") + public static final ConfigurationItem cacheOnDisk = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("display") + public static final ConfigurationItem internalFlashViewer = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("ui") + public static final ConfigurationItem gotoMainClassOnStartup = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem autoRenameIdentifiers = null; + + @ConfigurationDefaultBoolean(false) + public static final ConfigurationItem offeredAssociation = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem decimalAddress = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem showAllAddresses = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("display") + public static final ConfigurationItem useFrameCache = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem useRibbonInterface = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem openFolderAfterFlaExport = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem useDetailedLogging = null; + + @ConfigurationDefaultInt(65536) + @ConfigurationCategory("display") + public static final ConfigurationItem binaryDataDisplayLimit = null; + + /** + * Debug mode = throwing an error when comparing original file and + * recompiled + */ + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem debugMode = null; + /** + * Turn off resolving constants in ActionScript 2 + */ + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("script") + public static final ConfigurationItem resolveConstants = null; + /** + * Limit of code subs (for obfuscated code) + */ + @ConfigurationDefaultInt(500) + @ConfigurationCategory("limit") + public static final ConfigurationItem sublimiter = null; + /** + * Total export timeout in seconds + */ + @ConfigurationDefaultInt(30 * 60) + @ConfigurationCategory("limit") + public static final ConfigurationItem exportTimeout = null; + /** + * Decompilation timeout in seconds for a single file + */ + @ConfigurationDefaultInt(5 * 60) + @ConfigurationCategory("limit") + public static final ConfigurationItem decompilationTimeoutFile = null; + /** + * Using parameter names in decompiling may cause problems because official + * programs like Flash CS 5.5 inserts wrong parameter names indices + */ + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("script") + public static final ConfigurationItem paramNamesEnable = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("ui") + public static final ConfigurationItem displayFileName = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem debugCopy = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("debug") + public static final ConfigurationItem dumpTags = null; + + @ConfigurationDefaultInt(60) + @ConfigurationCategory("limit") + public static final ConfigurationItem decompilationTimeoutSingleMethod = null; + @ConfigurationDefaultInt(1) + public static final ConfigurationItem lastRenameType = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastSaveDir = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastOpenDir = null; + + @ConfigurationDefaultString(".") + public static final ConfigurationItem lastExportDir = null; + + @ConfigurationDefaultString("en") + @ConfigurationCategory("ui") + public static final ConfigurationItem locale = null; + + @ConfigurationDefaultString("_loc%d_") + @ConfigurationCategory("script") + public static final ConfigurationItem registerNameFormat = null; + + @ConfigurationDefaultInt(10) + public static final ConfigurationItem maxRecentFileCount = null; + + public static final ConfigurationItem recentFiles = null; + + public static final ConfigurationItem fontPairing = null; + + public static final ConfigurationItem lastUpdatesCheckDate = null; + + @ConfigurationDefaultInt(1000) + @ConfigurationName("gui.window.width") + public static final ConfigurationItem guiWindowWidth = null; + @ConfigurationDefaultInt(700) + @ConfigurationName("gui.window.height") + public static final ConfigurationItem guiWindowHeight = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationName("gui.window.maximized.horizontal") + public static final ConfigurationItem guiWindowMaximizedHorizontal = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationName("gui.window.maximized.vertical") + public static final ConfigurationItem guiWindowMaximizedVertical = null; + @ConfigurationName("gui.avm2.splitPane.dividerLocation") + public static final ConfigurationItem guiAvm2SplitPaneDividerLocation = null; + @ConfigurationName("guiActionSplitPaneDividerLocation") + public static final ConfigurationItem guiActionSplitPaneDividerLocation = null; + @ConfigurationName("guiPreviewSplitPaneDividerLocation") + public static final ConfigurationItem guiPreviewSplitPaneDividerLocation = null; + @ConfigurationName("gui.splitPane1.dividerLocation") + public static final ConfigurationItem guiSplitPane1DividerLocation = null; + @ConfigurationName("gui.splitPane2.dividerLocation") + public static final ConfigurationItem guiSplitPane2DividerLocation = null; + @ConfigurationDefaultInt(3) + @ConfigurationCategory("export") + public static final ConfigurationItem saveAsExeScaleMode = null; + + @ConfigurationDefaultInt(1024 * 100/*100KB*/) + @ConfigurationCategory("limit") + public static final ConfigurationItem syntaxHighlightLimit = null; + public static final ConfigurationItem guiFontPreviewSampleText = null; + @ConfigurationName("gui.fontPreviewWindow.width") + public static final ConfigurationItem guiFontPreviewWidth = null; + @ConfigurationName("gui.fontPreviewWindow.height") + public static final ConfigurationItem guiFontPreviewHeight = null; + @ConfigurationName("gui.fontPreviewWindow.posX") + public static final ConfigurationItem guiFontPreviewPosX = null; + @ConfigurationName("gui.fontPreviewWindow.posY") + public static final ConfigurationItem guiFontPreviewPosY = null; + + @ConfigurationDefaultInt(3) + @ConfigurationName("formatting.indent.size") + @ConfigurationCategory("format") + public static final ConfigurationItem indentSize = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationName("formatting.indent.useTabs") + @ConfigurationCategory("format") + public static final ConfigurationItem indentUseTabs = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("format") + public static final ConfigurationItem beginBlockOnNewLine = null; + + @ConfigurationDefaultInt(1000 * 60 * 60 * 24) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.delay") + public static final ConfigurationItem checkForUpdatesDelay = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.stable") + public static final ConfigurationItem checkForUpdatesStable = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.nightly") + public static final ConfigurationItem checkForUpdatesNightly = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("update") + @ConfigurationName("check.updates.enabled") + public static final ConfigurationItem checkForUpdatesAuto = null; + + @ConfigurationDefaultString("") + @ConfigurationName("export.formats") + public static final ConfigurationItem lastSelectedExportFormats = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFile = null; + + @ConfigurationDefaultString("--- SEPARATOR ---") + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFileSeparator = null; + + @ConfigurationDefaultString("--- RECORDSEPARATOR ---") + @ConfigurationCategory("export") + public static final ConfigurationItem textExportSingleFileRecordSeparator = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationName("warning.experimental.as12edit") + @ConfigurationCategory("script") + public static final ConfigurationItem warningExperimentalAS12Edit = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationName("warning.experimental.as3edit") + @ConfigurationCategory("script") + public static final ConfigurationItem warningExperimentalAS3Edit = null; + + @ConfigurationDefaultBoolean(true) + @ConfigurationCategory("export") + public static final ConfigurationItem packJavaScripts = null; + + @ConfigurationDefaultBoolean(false) + @ConfigurationCategory("export") + public static final ConfigurationItem textExportExportFontFace = null; + + private enum OSId { + + WINDOWS, OSX, UNIX + } + + private static OSId getOSId() { + PrivilegedAction doGetOSName = new PrivilegedAction() { + @Override + public String run() { + return System.getProperty("os.name"); + } + }; + OSId id = OSId.UNIX; + String osName = AccessController.doPrivileged(doGetOSName); + if (osName != null) { + if (osName.toLowerCase().startsWith("mac os x")) { + id = OSId.OSX; + } else if (osName.contains("Windows")) { + id = OSId.WINDOWS; + } + } + return id; + } + + public static String getFFDecHome() throws IOException { + if (directory == unspecifiedFile) { + directory = null; + String userHome = null; + try { + userHome = System.getProperty("user.home"); + } catch (SecurityException ignore) { + } + if (userHome != null) { + String applicationId = ApplicationInfo.SHORT_APPLICATION_NAME; + OSId osId = getOSId(); + if (osId == OSId.WINDOWS) { + File appDataDir = null; + try { + String appDataEV = System.getenv("APPDATA"); + if ((appDataEV != null) && (appDataEV.length() > 0)) { + appDataDir = new File(appDataEV); + } + } catch (SecurityException ignore) { + } + String vendorId = ApplicationInfo.VENDOR; + if ((appDataDir != null) && appDataDir.isDirectory()) { + // ${APPDATA}\{vendorId}\${applicationId} + String path = vendorId + "\\" + applicationId + "\\"; + directory = new File(appDataDir, path); + } else { + // ${userHome}\Application Data\${vendorId}\${applicationId} + String path = "Application Data\\" + vendorId + "\\" + applicationId + "\\"; + directory = new File(userHome, path); + } + } else if (osId == OSId.OSX) { + // ${userHome}/Library/Application Support/${applicationId} + String path = "Library/Application Support/" + applicationId + "/"; + directory = new File(userHome, path); + } else { + // ${userHome}/.${applicationId}/ + String path = "." + applicationId + "/"; + directory = new File(userHome, path); + } + } + } + if (!directory.exists()) { + if (!directory.mkdirs()) { + if (!directory.exists()) { + throw new IOException("cannot create directory " + directory); + } + } + } + String ret = directory.getAbsolutePath(); + if (!ret.endsWith(File.separator)) { + ret += File.separator; + } + return ret; + } + + public static List getRecentFiles() { + String files = recentFiles.get(); + if (files == null || files.isEmpty()) { + return new ArrayList<>(); + } + return Arrays.asList(files.split("::")); + } + + public static void addRecentFile(String path) { + List recentFilesArray = new ArrayList<>(getRecentFiles()); + int idx = recentFilesArray.indexOf(path); + if (idx != -1) { + recentFilesArray.remove(idx); + } + recentFilesArray.add(path); + while (recentFilesArray.size() > maxRecentFileCount.get()) { + recentFilesArray.remove(0); + } + recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); + } + + public static void removeRecentFile(String path) { + List recentFilesArray = new ArrayList<>(getRecentFiles()); + int idx = recentFilesArray.indexOf(path); + if (idx != -1) { + recentFilesArray.remove(idx); + } + recentFiles.set(Helper.joinStrings(recentFilesArray, "::")); + } + + public static Map getFontPairs() { + String fonts = fontPairing.get(); + if (fonts == null) { + return new HashMap<>(); + } + + Map result = new HashMap<>(); + for (String pair : fonts.split("::")) { + String[] splittedPair = pair.split("="); + result.put(splittedPair[0], splittedPair[1]); + } + return result; + } + + public static void addFontPair(String fileName, int fontId, String fontName, String systemFontName) { + String key = fileName + "_" + fontId + "_" + fontName; + Map fontPairs = getFontPairs(); + fontPairs.put(key, systemFontName); + fontPairs.put(fontName, systemFontName); + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Entry pair : fontPairs.entrySet()) { + if (i != 0) { + sb.append("::"); + } + sb.append(pair.getKey()).append("=").append(pair.getValue()); + i++; + } + fontPairing.set(sb.toString()); + } + + /** + * Saves replacements to file for future use + */ + private static void saveReplacements(String replacementsFile) { + if (replacements.isEmpty()) { + File rf = new File(replacementsFile); + if (rf.exists()) { + if (!rf.delete()) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Cannot delete replacements file"); + } + } + } else { + try (PrintWriter pw = new PrintWriter(new Utf8OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(replacementsFile))))) { + for (Replacement r : replacements) { + pw.println(r.urlPattern); + pw.println(r.targetFile); + } + } catch (IOException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Exception during saving replacements", ex); + } + } + } + + /** + * Load replacements from file + */ + private static void loadReplacements(String replacementsFile) { + if (!(new File(replacementsFile)).exists()) { + return; + } + replacements = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new Utf8InputStreamReader(new FileInputStream(replacementsFile)))) { + String s; + while ((s = br.readLine()) != null) { + Replacement r = new Replacement(s, br.readLine()); + replacements.add(r); + } + } catch (IOException e) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, "Error during load replacements", e); + } + } + + private static String getReplacementsFile() throws IOException { + return getFFDecHome() + REPLACEMENTS_NAME; + } + + private static String getConfigFile() throws IOException { + return getFFDecHome() + CONFIG_NAME; + } + + private static HashMap loadFromFile(String file, String replacementsFile) { + HashMap config = new HashMap<>(); + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { + + @SuppressWarnings("unchecked") + HashMap cfg = (HashMap) ois.readObject(); + config = cfg; + } catch (FileNotFoundException ex) { + } catch (ClassNotFoundException cnf) { + } catch (IOException ex) { + } + if (replacementsFile != null) { + loadReplacements(replacementsFile); + } + if (config.containsKey("paralelSpeedUp")) { + config.put("parallelSpeedUp", config.get("paralelSpeedUp")); + config.remove("paralelSpeedUp"); + } + return config; + } + + private static void saveToFile(String file, String replacementsFile) { + HashMap config = new HashMap<>(); + for (Entry entry : getConfigurationFields().entrySet()) { + try { + String name = entry.getKey(); + Field field = entry.getValue(); + ConfigurationItem item = (ConfigurationItem) field.get(null); + if (item.hasValue) { + config.put(name, item.get()); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); + } + } + try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) { + oos.writeObject(config); + } catch (IOException ex) { + JOptionPane.showMessageDialog(null, "Cannot save configuration.", "Error", JOptionPane.ERROR_MESSAGE); + Logger.getLogger(Configuration.class.getName()).severe("Configuration directory is read only."); + } + if (replacementsFile != null) { + saveReplacements(replacementsFile); + } + } + + public static List getReplacements() { + return replacements; + } + + public static void saveConfig() { + try { + saveToFile(getConfigFile(), getReplacementsFile()); + } catch (IOException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); + } + } + + static { + setConfigurationFields(); + if (useDetailedLogging.get() || debugMode.get()) { + logLevel = Level.CONFIG; + } else { + logLevel = Level.WARNING; + } + int processorCount = Runtime.getRuntime().availableProcessors(); + if (parallelThreadCount.get() > processorCount) { + parallelThreadCount.set(processorCount); + } + } + + @SuppressWarnings("unchecked") + public static void setConfigurationFields() { + try { + HashMap config = loadFromFile(getConfigFile(), getReplacementsFile()); + for (Entry entry : getConfigurationFields().entrySet()) { + String name = entry.getKey(); + Field field = entry.getValue(); + // remove final modifier from field + Field modifiersField = field.getClass().getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + + Object defaultValue = getDefaultValue(field); + Object value = null; + if (config.containsKey(name)) { + value = config.get(name); + } + + if (value != null) { + field.set(null, new ConfigurationItem(name, defaultValue, value)); + } else { + field.set(null, new ConfigurationItem(name, defaultValue)); + } + } + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } catch (IOException ex) { + Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static Object getDefaultValue(Field field) { + Object defaultValue = null; + ConfigurationDefaultBoolean aBool = field.getAnnotation(ConfigurationDefaultBoolean.class); + if (aBool != null) { + defaultValue = aBool.value(); + } + ConfigurationDefaultInt aInt = field.getAnnotation(ConfigurationDefaultInt.class); + if (aInt != null) { + defaultValue = aInt.value(); + } + ConfigurationDefaultString aString = field.getAnnotation(ConfigurationDefaultString.class); + if (aString != null) { + defaultValue = aString.value(); + } + return defaultValue; + } + + public static Map getConfigurationFields() { + Field[] fields = Configuration.class.getFields(); + Map result = new HashMap<>(); + for (Field field : fields) { + if (ConfigurationItem.class.isAssignableFrom(field.getType())) { + ConfigurationName annotation = field.getAnnotation(ConfigurationName.class); + String name = annotation == null ? field.getName() : annotation.value(); + result.put(name, field); + } + } + return result; + } + + public static CodeFormatting getCodeFormatting() { + CodeFormatting ret = new CodeFormatting(); + String indentString = ""; + for (int i = 0; i < indentSize.get(); i++) { + indentString += indentUseTabs.get() ? "\t" : " "; + } + ret.indentString = indentString; + ret.beginBlockOnNewLine = beginBlockOnNewLine.get(); + return ret; + } + + public static File getFlashLibPath() { + try { + String home = getFFDecHome(); + File libsdir = new File(home + "flashlib"); + if (!libsdir.exists()) { + libsdir.mkdirs(); + } + return libsdir; + } catch (IOException ex) { + return null; + } + + } + + public static File getPlayerSWC() { + File libsdir = getFlashLibPath(); + if (libsdir != null && libsdir.exists()) { + File libs[] = libsdir.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().startsWith("playerglobal"); + } + }); + List libnames = new ArrayList<>(); + for (File f : libs) { + libnames.add(f.getName()); + } + Collections.sort(libnames); + if (!libnames.isEmpty()) { + return new File(libsdir.getAbsolutePath() + File.separator + libnames.get(libnames.size() - 1)); + } else { + return null; + } + } + + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index 4d2b18f99..74b5a7d52 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -1,1271 +1,1268 @@ -/* - * Copyright (C) 2010-2014 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.SWF; -import static com.jpexs.decompiler.flash.SWF.frameToImageGet; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.SWFSourceInfo; -import com.jpexs.decompiler.flash.SearchMode; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -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.TextExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; -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.TextExportMode; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; -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.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.streams.SeekableInputStream; -import com.sun.jna.Platform; -import com.sun.jna.platform.win32.Kernel32; -import gnu.jpdf.PDFJob; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.awt.print.PageFormat; -import java.awt.print.Paper; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class CommandLineArgumentParser { - - private static boolean commandLineMode = false; - - @SuppressWarnings("unchecked") - private static final ConfigurationItem[] commandlineConfigBoolean = new ConfigurationItem[]{ - Configuration.decompile, - Configuration.parallelSpeedUp, - Configuration.internalFlashViewer, - Configuration.autoDeobfuscate, - Configuration.cacheOnDisk - }; - - public static boolean isCommandLineMode() { - return commandLineMode; - } - - //(as|pcode|pcodehex|hex|image|shape|movie|sound|binaryData|text|textplain|all|all_as|all_pcode|all_pcodehex|all_hex) - public static void printCmdLineUsage() { - int cnt=1; - System.out.println("Commandline arguments:"); - System.out.println(" "+(cnt++)+") -help | --help | /?"); - System.out.println(" ...shows commandline arguments (this help)"); - System.out.println(" "+(cnt++)+") "); - System.out.println(" ...opens SWF file with the decompiler GUI"); - System.out.println(" "+(cnt++)+") -proxy [-P]"); - System.out.println(" ...auto start proxy in the tray. Optional parameter -P specifies port for proxy. Defaults to 55555. "); - System.out.println(" "+(cnt++)+") -export [-selectas3class ...]"); - System.out.println(" ...export sources to "); - System.out.println(" Values for parameter:"); - System.out.println(" script - Scripts (Default format: ActionScript source)"); - System.out.println(" - For this type, optional \"-selectas3class\" parameter can be passed to export only selected classes (ActionScript 3 only)"); - System.out.println(" image - Images (Default format: PNG/JPEG)"); - System.out.println(" shape - Shapes (Default format: SVG)"); - System.out.println(" morphshape - MorphShapes (Default format: SVG)"); - System.out.println(" movie - Movies (Default format: FLV without sound)"); - System.out.println(" frame - Frames (Default format: PNG)"); - System.out.println(" sound - Sounds (Default format: MP3/WAV/FLV only sound)"); - System.out.println(" binaryData - Binary data (Default format: Raw data)"); - System.out.println(" text - Texts (Default format: Formatted text)"); - System.out.println(" all - Every resource"); - System.out.println(" fla - Everything to FLA compressed format"); - System.out.println(" xfl - Everything to uncompressed FLA format (XFL)"); - System.out.println(" You can export multiple types of items by using colon \",\""); - System.out.println(" DO NOT PUT space between comma (,) and next value."); - System.out.println(); - System.out.println(" Old DEPRECATED aliases include: (please use basic itemtypes and -format parameter instead)"); - System.out.println(" as, pcode, pcodehex, hex, all_as, all_pcode, all_pcodehex, textplain"); - System.out.println(); - System.out.println(" "+(cnt++)+") -format "); - System.out.println(" ...sets output formats for export"); - System.out.println(" Values for parameter:"); - System.out.println(" script:as - ActionScript source"); - System.out.println(" script:pcode - ActionScript P-code"); - System.out.println(" script:pcodehex - ActionScript P-code with hex"); - System.out.println(" script:hex - ActionScript Hex only"); - System.out.println(" shape:svg - SVG format for Shapes"); - System.out.println(" shape:png - PNG format for Shapes"); - System.out.println(" shape:canvas - HTML5 Canvas format for Shapes"); - System.out.println(" morphshape:svg - SVG format for MorphShapes"); - System.out.println(" morphshape:canvas - HTML5 Canvas format for MorphShapes"); - System.out.println(" frame:png - PNG format for Frames"); - System.out.println(" frame:gif - GIF format for Frames"); - System.out.println(" frame:avi - AVI format for Frames"); - System.out.println(" frame:canvas - HTML5 Canvas format for Frames"); - System.out.println(" frame:pdf - PDF format for Frames"); - System.out.println(" image:png_jpeg - PNG/JPEG format for Images"); - System.out.println(" image:png - PNG format for Images"); - System.out.println(" image:jpeg - JPEG format for Images"); - System.out.println(" text:plain - Plain text format for Texts"); - System.out.println(" text:formatted - Formatted text format for Texts"); - System.out.println(" sound:mp3_wav_flv - MP3/WAV/FLV format for Sounds"); - System.out.println(" sound:mp3_wav - MP3/WAV format for Sounds"); - System.out.println(" sound:wav - WAV format for Sounds"); - System.out.println(" sound:flv - FLV format for Sounds"); - System.out.println(" fla: or xfl: - Specify FLA format version"); - System.out.println(" - values for : cs5,cs5.5,cs6,cc"); - - System.out.println(" You can set multiple formats at once using comma (,)"); - System.out.println(" DO NOT PUT space between comma (,) and next value."); - System.out.println(" The prefix with colon (:) is neccessary."); - System.out.println(" "+(cnt++)+") -select "); - System.out.println(" ...selects frames/pages for export"); - System.out.println(" Example formats:"); - System.out.println(" 1-5"); - System.out.println(" 2,3"); - System.out.println(" 2-5,7,9-"); - System.out.println(" DO NOT PUT space between comma (,) and next ramge."); - System.out.println(" "+(cnt++)+") -selectid "); - System.out.println(" ...selects characters for export by character id"); - System.out.println(" format is same as in -select"); - System.out.println(" "+(cnt++)+") -dumpSWF "); - System.out.println(" ...dumps list of SWF tags to console"); - System.out.println(" "+(cnt++)+") -compress "); - System.out.println(" ...Compress SWF and save it to "); - System.out.println(" "+(cnt++)+") -decompress "); - System.out.println(" ...Decompress and save it to "); - System.out.println(" "+(cnt++)+") -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]"); - System.out.println(" ...Extracts SWF files from ZIP or other binary files"); - System.out.println(" ...-o parameter should contain a file path when \"biggest\" or \"first\" parameter is specified"); - System.out.println(" ...-o parameter should contain a folder path when no exctaction mode or \"all\" parameter is specified"); - System.out.println(" "+(cnt++)+") -renameInvalidIdentifiers (typeNumber|randomWord) e"); - System.out.println(" ...Renames the invalid identifiers in and save it to "); - System.out.println(" "+(cnt++)+") -config key=value[,key2=value2][,key3=value3...] [other parameters]"); - System.out.print(" ...Sets configuration values. Available keys[current setting]:"); - for (ConfigurationItem item : commandlineConfigBoolean) { - System.out.print(" " + item + "[" + item.get() + "]"); - } - System.out.println(""); - System.out.println(" Values are boolean, you can use 0/1, true/false, on/off or yes/no."); - System.out.println(" If no other parameters passed, configuration is saved. Otherwise it is used only once."); - System.out.println(" DO NOT PUT space between comma (,) and next value."); - System.out.println(" "+(cnt++)+") -onerror (abort|retryN|ignore)"); - System.out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file"); - System.out.println(" "+(cnt++)+") -timeout "); - System.out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds"); - System.out.println(" "+(cnt++)+") -exportTimeout "); - System.out.println(" ...total export timeout in seconds"); - System.out.println(" "+(cnt++)+") -exportFileTimeout "); - System.out.println(" ...export timeout for a single AS3 class in seconds"); - System.out.println(" "+(cnt++)+") -flashpaper2pdf "); - System.out.println(" ...converts FlashPaper SWF file to PDF . Use -zoom parameter to specify image quality."); - System.out.println(" "+(cnt++)+") -zoom "); - System.out.println(" ...apply zoom during export (currently for FlashPaper conversion only)"); - System.out.println(); - System.out.println("Examples:"); - System.out.println("java -jar ffdec.jar myfile.swf"); - System.out.println("java -jar ffdec.jar -proxy"); - System.out.println("java -jar ffdec.jar -proxy -P1234"); - System.out.println("java -jar ffdec.jar -export script \"C:\\decompiled\" myfile.swf"); - System.out.println("java -jar ffdec.jar -export script \"C:\\decompiled\" myfile.swf -selectas3class com.example.MyClass com.example.SecondClass"); - System.out.println("java -jar ffdec.jar -format script:pcode -export script \"C:\\decompiled\" myfile.swf"); - System.out.println("java -jar ffdec.jar -format script:pcode,text:plain -export script,text,image \"C:\\decompiled\" myfile.swf"); - System.out.println("java -jar ffdec.jar -format fla:cs5.5 -export fla \"C:\\sources\\myfile.fla\" myfile.swf"); - System.out.println("java -jar ffdec.jar -dumpSWF myfile.swf"); - System.out.println("java -jar ffdec.jar -compress myfile.swf myfiledec.swf"); - System.out.println("java -jar ffdec.jar -decompress myfiledec.swf myfile.swf"); - System.out.println("java -jar ffdec.jar -onerror ignore -export script \"C:\\decompiled\" myfile.swf"); - System.out.println("java -jar ffdec.jar -onerror retry 5 -export script \"C:\\decompiled\" myfile.swf"); - System.out.println("java -jar ffdec.jar -config autoDeobfuscate=1,parallelSpeedUp=0 -export script \"C:\\decompiled\" myfile.swf"); - System.out.println(""); - System.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 - * @return path to the file which should be opened or null - * @throws java.io.IOException - */ - public static String parseArguments(String[] arguments) throws IOException { - Level traceLevel = Level.WARNING; - Queue args = new LinkedList<>(); - for (String arg : arguments) { - if (arg.length() > 0) { - args.add(arg); - } - } - AbortRetryIgnoreHandler handler = null; - Map format = new HashMap<>(); - double zoom = 1; - Selection selection=new Selection(); - Selection selectionIds=new Selection(); - String nextParam; - OUTER: - while (true) { - nextParam = args.remove(); - if (nextParam != null) { - nextParam = nextParam.toLowerCase(); - } - switch (nextParam) { - case "-selectid": - selectionIds = parseSelect(args); - break; - case "-select": - selection = parseSelect(args); - break; - case "-zoom": - zoom = parseZoom(args); - break; - case "-format": - format = parseFormat(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 "-affinity": - parseAffinity(args); - break; - case "-priority": - parsePriority(args); - break; - case "-verbose": - traceLevel = Level.FINE; - break; - case "-debug": - Configuration.debugMode.set(true); - break; - default: - break OUTER; - } - if (args.isEmpty()) { - return null; - } - } - if (nextParam.equals("-removefromcontextmenu")) { - ContextMenuTools.addToContextMenu(false, true); - System.exit(0); - } else if (nextParam.equals("-addtocontextmenu")) { - ContextMenuTools.addToContextMenu(true, true); - System.exit(0); - } else if (nextParam.equals("-proxy")) { - parseProxy(args); - } else if (nextParam.equals("-export")) { - parseExport(selection,selectionIds,args, handler, traceLevel, format); - } else if (nextParam.equals("-compress")) { - parseCompress(args); - } else if (nextParam.equals("-decompress")) { - parseDecompress(args); - } else if (nextParam.equals("-extract")) { - parseExtract(args); - } else if (nextParam.equals("-renameinvalididentifiers")) { - parseRenameInvalidIdentifiers(args); - } else if (nextParam.equals("-dumpswf")) { - parseDumpSwf(args); - } else if (nextParam.equals("-flashpaper2pdf")) { - parseFlashPaperToPdf(selection,zoom, args); - } else if (nextParam.equals("-as3compiler")) { - ActionScriptParser.compile(null /*?*/,args.remove(), args.remove()); - } else if (nextParam.equals("-help") || nextParam.equals("--help") || nextParam.equals("/?")) { - printHeader(); - printCmdLineUsage(); - System.exit(0); - } else if (args.isEmpty()) { - return nextParam; - } 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() { - System.err.println("Error: Bad Commandline Arguments!"); - printCmdLineUsage(); - System.exit(1); - } - - private static void setConfigurations(String cfgStr) { - String[] cfgs; - if (cfgStr.contains(",")) { - cfgs = cfgStr.split(","); - } else { - cfgs = new String[]{cfgStr}; - } - - for (String c : cfgs) { - String[] cp; - if (c.contains("=")) { - cp = c.split("="); - } else { - cp = new String[]{c, "1"}; - } - String key = cp[0]; - String value = cp[1]; - if (key.toLowerCase().equals("paralelSpeedUp".toLowerCase())) { - key = "parallelSpeedUp"; - } - for (ConfigurationItem item : commandlineConfigBoolean) { - if (key.toLowerCase().equals(item.getName().toLowerCase())) { - Boolean bValue = parseBooleanConfigValue(value); - if (bValue != null) { - System.out.println("Config " + item.getName() + " set to " + bValue); - item.set(bValue); - } - } - } - } - } - - private static Boolean parseBooleanConfigValue(String value) { - if (value == null) { - return null; - } - - Boolean bValue = null; - value = value.toLowerCase(); - 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 ScriptExportMode strToExportFormat(String exportFormatStr) { - switch (exportFormatStr) { - case "pcode": - return ScriptExportMode.PCODE; - case "pcodehex": - return ScriptExportMode.PCODE_HEX; - case "hex": - return ScriptExportMode.HEX; - default: - return ScriptExportMode.AS; - } - } - - private static void parseConfig(Queue args) { - if (args.isEmpty()) { - System.err.println("Config values expected"); - badArguments(); - } - setConfigurations(args.remove()); - } - - - private static class Range { - public Integer min; - public Integer max; - - public Range(Integer min, Integer max) { - this.min = min; - this.max = max; - } - - public boolean contains(int index){ - Integer minimum = min == null? Integer.MIN_VALUE:min; - Integer 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(null, null)); - } - - public Selection(List ranges) { - this.ranges = ranges; - } - - public boolean contains(int index){ - for(Range r:ranges){ - if(r.contains(index)){ - return true; - } - } - return false; - } - - } - - - private static Selection parseSelect(Queue args) { - List ret=new ArrayList<>(); - if (args.isEmpty()) { - System.err.println("range parameter expected"); - badArguments(); - } - String range=args.remove(); - String[] ranges; - if(range.contains(",")){ - ranges = range.split(","); - }else{ - ranges=new String[]{range}; - } - for(String r:ranges){ - Integer min = null; - Integer max = null; - if(r.contains("-")){ - String ps[]=r.split("\\-"); - if(ps.length!=2){ - System.err.println("invalid range"); - badArguments(); - } - 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(); - } - }else{ - try{ - min = Integer.parseInt(r); - max = min; - }catch(NumberFormatException nfe){ - System.err.println("invalid range"); - badArguments(); - } - } - ret.add(new Range(min, max)); - } - return new Selection(ret); - } - - private static double parseZoom(Queue args) { - if (args.isEmpty()) { - System.err.println("zoom parameter expected"); - badArguments(); - } - try{ - return Double.parseDouble(args.remove()); - }catch(NumberFormatException nfe){ - System.err.println("invalid zoom"); - badArguments(); - } - return 1; - } - - private static AbortRetryIgnoreHandler parseOnError(Queue args) { - int errorMode = AbortRetryIgnoreHandler.UNDEFINED; - int retryCount = 0; - - if (args.isEmpty()) { - System.err.println("onerror parameter expected"); - badArguments(); - } - String errorModeParameter = args.remove(); - 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(); - } - - try { - retryCount = Integer.parseInt(args.remove()); - } 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(Queue args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments(); - } - try { - int timeout = Integer.parseInt(args.remove()); - Configuration.decompilationTimeoutSingleMethod.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseExportTimeout(Queue args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments(); - } - try { - int timeout = Integer.parseInt(args.remove()); - Configuration.exportTimeout.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseExportFileTimeout(Queue args) { - if (args.isEmpty()) { - System.err.println("timeout parameter expected"); - badArguments(); - } - try { - int timeout = Integer.parseInt(args.remove()); - Configuration.decompilationTimeoutFile.set(timeout); - } catch (NumberFormatException nex) { - System.err.println("Bad timeout value"); - } - } - - private static void parseAffinity(Queue args) { - if (Platform.isWindows()) { - if (args.isEmpty()) { - System.err.println("affinity parameter expected"); - badArguments(); - } - try { - int affinityMask = Integer.parseInt(args.remove()); - 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 parsePriority(Queue args) { - if (Platform.isWindows()) { - if (args.isEmpty()) { - System.err.println("priority parameter expected"); - badArguments(); - } - String priority = args.remove(); - 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 parseProxy(Queue args) { - int port = 55555; - String portStr = args.peek(); - if (portStr != null && portStr.startsWith("-P")) { - args.remove(); - try { - port = Integer.parseInt(portStr.substring(2)); - } catch (NumberFormatException nex) { - System.err.println("Bad port number"); - } - } - Main.startProxy(port); - } - - private static List parseSelectClasses(Queue args) { - List ret = new ArrayList<>(); - if (!args.isEmpty() && args.peek().equals("-selectas3class")) { - args.remove(); - while (!args.isEmpty()) { - ret.add(args.remove()); - - } - } - return ret; - - } - - private static void parseExport(Selection selection, Selection selectionIds, Queue args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats) { - if (args.size() < 3) { - badArguments(); - } - String[] validExportItems = new String[]{ - "script", - "image", - "shape", - "morphshape", - "movie", - "sound", - "binarydata", - "text", - "all", - "frame", - "fla", - "xfl", - "font" - }; - - String[] deprecatedExportFormats = new String[]{ - "as", - "pcode", - "all_as", - "all_pcode", - "all_pcodehex", - "all_hex", - "textplain" - }; - - if (handler == null) { - handler = new ConsoleAbortRetryIgnoreHandler(AbortRetryIgnoreHandler.UNDEFINED, 0); - } - String exportFormatString = args.remove().toLowerCase(); - String exportFormats[]; - if (exportFormatString.contains(",")) { - exportFormats = exportFormatString.split(","); - } else { - exportFormats = new String[]{exportFormatString}; - } - long startTime = System.currentTimeMillis(); - - File outDir = new File(args.remove()); - File inFile = new File(args.remove()); - if (!inFile.exists()) { - System.err.println("Input SWF file does not exist!"); - badArguments(); - } - printHeader(); - boolean exportOK = true; - - List as3classes = new ArrayList<>(); - - try { - SWF exfile = new SWF(new FileInputStream(inFile), Configuration.parallelSpeedUp.get()); - - List extags=new ArrayList<>(); - for(Tag t:exfile.tags){ - 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; - exfile.addEventListener(new EventListener() { - @Override - public void handleEvent(String event, Object data) { - if (level.intValue() <= Level.FINE.intValue() && event.equals("exporting")) { - System.out.println((String) data); - } - if (event.equals("exported")) { - System.out.println((String) data); - } - } - }); - - for (String exportFormat : exportFormats) { - if (!Arrays.asList(validExportItems).contains(exportFormat) && !Arrays.asList(deprecatedExportFormats).contains(exportFormat)) { - System.err.println("Invalid export item:" + exportFormat); - badArguments(); - } - if (Arrays.asList(deprecatedExportFormats).contains(exportFormat)) { - System.err.println("Warning: Using DEPRECATED export item: " + exportFormat + ". Run application with --help parameter to see available formats."); - } - - commandLineMode = true; - - switch (exportFormat) { - case "all": - case "all_as": - case "all_pcode": - case "all_pcodehex": - case "all_hex": { - ScriptExportMode allExportMode = ScriptExportMode.AS; - if (!exportFormat.equals("all")) { - allExportMode = strToExportFormat(exportFormat.substring("all_".length() - 1)); - } else if (formats.containsKey("script")) { - allExportMode = strToExportFormat(formats.get("script")); - } - System.out.println("Exporting images..."); - new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + File.separator + "images", extags, new ImageExportSettings(ImageExportMode.PNG_JPEG)); - System.out.println("Exporting shapes..."); - new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + File.separator + "shapes", extags, new ShapeExportSettings(ShapeExportMode.SVG)); - System.out.println("Exporting morphshapes..."); - new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + File.separator + "morphshapes", extags, new MorphShapeExportSettings(MorphShapeExportMode.SVG)); - System.out.println("Exporting scripts..."); - exfile.exportActionScript(handler, outDir.getAbsolutePath() + File.separator + "scripts", allExportMode, Configuration.parallelSpeedUp.get()); - System.out.println("Exporting movies..."); - new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + File.separator + "movies", extags, new MovieExportSettings(MovieExportMode.FLV)); - System.out.println("Exporting sounds..."); - new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + File.separator + "sounds", extags, new SoundExportSettings(SoundExportMode.MP3_WAV_FLV)); - System.out.println("Exporting binaryData..."); - new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + File.separator + "binaryData", extags, new BinaryDataExportSettings(BinaryDataExportMode.RAW)); - System.out.println("Exporting texts..."); - - String allTextFormat = formats.get("text"); - if (allTextFormat == null) { - allTextFormat = "formatted"; - } - Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); - if (singleTextFile == null) { - singleTextFile = Configuration.textExportSingleFile.get(); - } - new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + File.separator + "texts", extags, new TextExportSettings(allTextFormat.equals("formatted") ? TextExportMode.FORMATTED : TextExportMode.PLAIN, singleTextFile)); - } - break; - case "image": { - System.out.println("Exporting images..."); - new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "images" : ""), extags, new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class))); - } - break; - case "shape": { - System.out.println("Exporting shapes..."); - new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "shapes" : ""), extags, new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class))); - } - break; - case "morphshape": { - System.out.println("Exporting morphshapes..."); - new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "morphshapes" : ""), extags, new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class))); - } - break; - case "script": - case "as": - case "pcode": - case "pcodehex": - case "hex": { - System.out.println("Exporting scripts..."); - boolean parallel = Configuration.parallelSpeedUp.get(); - if (as3classes.isEmpty()) { - as3classes = parseSelectClasses(args); - } - if (!as3classes.isEmpty()) { - for (String as3class : as3classes) { - exportOK = exportOK && exfile.exportAS3Class(as3class, outDir.getAbsolutePath(), enumFromStr(formats.get("script"), ScriptExportMode.class), parallel); - } - } else { - exportOK = exportOK && exfile.exportActionScript(handler, outDir.getAbsolutePath(), enumFromStr(formats.get("script"), ScriptExportMode.class), parallel) != null; - } - } - break; - case "movie": { - System.out.println("Exporting movies..."); - new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "movies" : ""),extags, - new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class))); - } - break; - case "font": { - System.out.println("Exporting fonts..."); - new FontExporter().exportFonts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fonts" : ""),extags, - new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class))); - } - break; - case "frame": { - System.out.println("Exporting frames..."); - List frames=new ArrayList<>(); - for(int i=0;i 1 ? File.separator + "frames" : ""), 0, frames, - new FramesExportSettings(enumFromStr(formats.get("frame"), FramesExportMode.class))); - } - break; - case "sound": { - System.out.println("Exporting sounds..."); - new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "sounds" : ""),extags, - new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class))); - } - break; - case "binarydata": { - System.out.println("Exporting binaryData..."); - new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "binaryData" : ""),extags, - new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class))); - } - break; - case "text": { - System.out.println("Exporting texts..."); - Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); - if (singleTextFile == null) { - singleTextFile = Configuration.textExportSingleFile.get(); - } - new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""),extags, - new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile)); - } - break; - case "textplain": { - System.out.println("Exporting texts..."); - Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); - if (singleTextFile == null) { - singleTextFile = Configuration.textExportSingleFile.get(); - } - new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""),extags, - new TextExportSettings(TextExportMode.PLAIN, singleTextFile)); - } - break; - case "fla": { - System.out.println("Exporting FLA..."); - FLAVersion flaVersion = FLAVersion.fromString(formats.get("fla")); - if (flaVersion == null) { - flaVersion = FLAVersion.CS6; //Defaults to CS6 - } - exfile.exportFla(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fla" : ""), inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), flaVersion); - } - break; - case "xfl": { - System.out.println("Exporting XFL..."); - FLAVersion xflVersion = FLAVersion.fromString(formats.get("xfl")); - if (xflVersion == null) { - xflVersion = FLAVersion.CS6; //Defaults to CS6 - } - exfile.exportXfl(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "xfl" : ""), inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), xflVersion); - } - break; - default: - exportOK = false; - } - - } - } catch (OutOfMemoryError | Exception ex) { - exportOK = false; - System.err.print("FAIL: Exporting Failed on Exception - "); - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - System.exit(1); - } - long stopTime = System.currentTimeMillis(); - long time = stopTime - startTime; - System.out.println("Export finished. Total export time: " + Helper.formatTimeSec(time)); - if (exportOK) { - System.out.println("OK"); - System.exit(0); - } else { - System.err.println("FAIL"); - System.exit(1); - } - } - - private static void parseCompress(Queue args) { - if (args.size() < 2) { - badArguments(); - } - - try { - try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); - OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { - if (SWF.fws2cws(fis, fos)) { - System.out.println("OK"); - } else { - System.err.println("FAIL"); - } - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - } - } catch (IOException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - - System.exit(0); - } - - private static void parseDecompress(Queue args) { - if (args.size() < 2) { - badArguments(); - } - - try { - try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); - OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { - if (SWF.decompress(fis, fos)) { - System.out.println("OK"); - System.exit(0); - } else { - System.err.println("FAIL"); - System.exit(1); - } - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - } - } catch (IOException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - - System.exit(0); - } - - private static void parseExtract(Queue args) { - if (args.size() < 1) { - badArguments(); - } - - String fileName = args.remove(); - SearchMode mode = SearchMode.ALL; - - boolean noCheck = false; - String output = null; - - if (args.size() > 0 && args.peek().toLowerCase().equals("-o")) { - args.remove(); - if (args.size() < 1) { - badArguments(); - } - output = args.remove(); - } - - if (args.size() > 0 && args.peek().toLowerCase().equals("nocheck")) { - noCheck = true; - args.remove(); - } - - if (args.size() > 0) { - String modeStr = args.remove().toLowerCase(); - 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 { - SWFSourceInfo sourceInfo = new SWFSourceInfo(null, fileName, null); - if (!sourceInfo.isBundle()) { - System.err.println("Error: should be a bundle. (ZIP or non SWF binary file)"); - System.exit(1); - } - SWFBundle 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.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - - System.exit(0); - } - - private static void parseRenameInvalidIdentifiers(Queue args) { - if (args.size() < 3) { - badArguments(); - } - - String renameTypeStr = args.remove(); - RenameType renameType; - switch (renameTypeStr.toLowerCase()) { - case "typenumber": - renameType = RenameType.TYPENUMBER; - break; - case "randomword": - renameType = RenameType.RANDOMWORD; - break; - default: - System.err.println("Invalid rename type:" + renameTypeStr); - badArguments(); - return; - } - - try { - try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); - OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { - if (SWF.renameInvalidIdentifiers(renameType, fis, fos)) { - System.out.println("OK"); - System.exit(0); - } else { - System.err.println("FAIL"); - System.exit(1); - } - } catch (FileNotFoundException ex) { - System.err.println("File not found."); - } - } catch (IOException ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - } - - System.exit(0); - } - - private static Map parseFormat(Queue args) { - if (args.size() < 1) { - badArguments(); - } - String fmtStr = args.remove(); - String[] fmts; - if (fmtStr.contains(",")) { - fmts = fmtStr.split(","); - } else { - fmts = new String[]{fmtStr}; - } - Map ret = new HashMap<>(); - for (int i = 0; i < fmts.length; i++) { - String parts[] = fmts[i].split(":"); - ret.put(parts[0].toLowerCase(), parts[1].toLowerCase()); - } - return ret; - } - - private static void parseFlashPaperToPdf(Selection selection,double zoom,Queue args){ - if (args.size()<2) { - badArguments(); - } - File inFile = new File(args.remove()); - File outFile=new File(args.remove()); - printHeader(); - - try(FileInputStream is = new FileInputStream(inFile)) { - - PDFJob job = null; - - SWF swf = new SWF(is,Configuration.parallelSpeedUp.get()); - int totalPages = 0; - - for(Tag t:swf.tags){ - if(t instanceof DefineSpriteTag){ - DefineSpriteTag ds=(DefineSpriteTag)t; - if("page1".equals(ds.getExportName())){ - totalPages = 1; - }else{ - if(totalPages>0){ - totalPages++; - } - } - } - } - - int page = 0; - - for(Tag t:swf.tags){ - if(t instanceof DefineSpriteTag){ - DefineSpriteTag ds=(DefineSpriteTag)t; - if("page1".equals(ds.getExportName())){ - page = 1; - job=new PDFJob(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+"..."); - RECT displayRect = new RECT(ds.getTimeline().displayRect); - displayRect.Xmax*=zoom; - displayRect.Ymax*=zoom; - Matrix m=new Matrix(); - m.scale(zoom); - BufferedImage img = swf.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect,m , new ColorTransform(), Color.white).getBufferedImage(); - PageFormat pf=new PageFormat(); - pf.setOrientation(PageFormat.PORTRAIT); - Paper p = new Paper(); - p.setSize(img.getWidth(),img.getHeight()); - pf.setPaper(p); - Graphics g = job.getGraphics(pf); - g.drawImage(img, 0, 0,img.getWidth(),img.getHeight(), null); - g.dispose(); - 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); - } - System.exit(0); - } - - - private static void parseDumpSwf(Queue args) { - if (args.isEmpty()) { - badArguments(); - } - try { - Configuration.dumpTags.set(true); - Configuration.parallelSpeedUp.set(false); - SWFSourceInfo sourceInfo = new SWFSourceInfo(null, args.remove(), null); - Main.parseSWF(sourceInfo); - } catch (Exception ex) { - Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); - System.exit(1); - } - System.exit(0); - } - - 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().equals(str.toLowerCase())) { - return e; - } - } - return vals[0]; - } -} +/* + * Copyright (C) 2010-2014 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.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.SearchMode; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +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.TextExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; +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.TextExportMode; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; +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.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.streams.SeekableInputStream; +import com.sun.jna.Platform; +import com.sun.jna.platform.win32.Kernel32; +import gnu.jpdf.PDFJob; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class CommandLineArgumentParser { + + private static boolean commandLineMode = false; + + @SuppressWarnings("unchecked") + private static final ConfigurationItem[] commandlineConfigBoolean = new ConfigurationItem[]{ + Configuration.decompile, + Configuration.parallelSpeedUp, + Configuration.internalFlashViewer, + Configuration.autoDeobfuscate, + Configuration.cacheOnDisk + }; + + public static boolean isCommandLineMode() { + return commandLineMode; + } + + //(as|pcode|pcodehex|hex|image|shape|movie|sound|binaryData|text|textplain|all|all_as|all_pcode|all_pcodehex|all_hex) + public static void printCmdLineUsage() { + int cnt = 1; + System.out.println("Commandline arguments:"); + System.out.println(" " + (cnt++) + ") -help | --help | /?"); + System.out.println(" ...shows commandline arguments (this help)"); + System.out.println(" " + (cnt++) + ") "); + System.out.println(" ...opens SWF file with the decompiler GUI"); + System.out.println(" " + (cnt++) + ") -proxy [-P]"); + System.out.println(" ...auto start proxy in the tray. Optional parameter -P specifies port for proxy. Defaults to 55555. "); + System.out.println(" " + (cnt++) + ") -export [-selectas3class ...]"); + System.out.println(" ...export sources to "); + System.out.println(" Values for parameter:"); + System.out.println(" script - Scripts (Default format: ActionScript source)"); + System.out.println(" - For this type, optional \"-selectas3class\" parameter can be passed to export only selected classes (ActionScript 3 only)"); + System.out.println(" image - Images (Default format: PNG/JPEG)"); + System.out.println(" shape - Shapes (Default format: SVG)"); + System.out.println(" morphshape - MorphShapes (Default format: SVG)"); + System.out.println(" movie - Movies (Default format: FLV without sound)"); + System.out.println(" frame - Frames (Default format: PNG)"); + System.out.println(" sound - Sounds (Default format: MP3/WAV/FLV only sound)"); + System.out.println(" binaryData - Binary data (Default format: Raw data)"); + System.out.println(" text - Texts (Default format: Formatted text)"); + System.out.println(" all - Every resource"); + System.out.println(" fla - Everything to FLA compressed format"); + System.out.println(" xfl - Everything to uncompressed FLA format (XFL)"); + System.out.println(" You can export multiple types of items by using colon \",\""); + System.out.println(" DO NOT PUT space between comma (,) and next value."); + System.out.println(); + System.out.println(" Old DEPRECATED aliases include: (please use basic itemtypes and -format parameter instead)"); + System.out.println(" as, pcode, pcodehex, hex, all_as, all_pcode, all_pcodehex, textplain"); + System.out.println(); + System.out.println(" " + (cnt++) + ") -format "); + System.out.println(" ...sets output formats for export"); + System.out.println(" Values for parameter:"); + System.out.println(" script:as - ActionScript source"); + System.out.println(" script:pcode - ActionScript P-code"); + System.out.println(" script:pcodehex - ActionScript P-code with hex"); + System.out.println(" script:hex - ActionScript Hex only"); + System.out.println(" shape:svg - SVG format for Shapes"); + System.out.println(" shape:png - PNG format for Shapes"); + System.out.println(" shape:canvas - HTML5 Canvas format for Shapes"); + System.out.println(" morphshape:svg - SVG format for MorphShapes"); + System.out.println(" morphshape:canvas - HTML5 Canvas format for MorphShapes"); + System.out.println(" frame:png - PNG format for Frames"); + System.out.println(" frame:gif - GIF format for Frames"); + System.out.println(" frame:avi - AVI format for Frames"); + System.out.println(" frame:canvas - HTML5 Canvas format for Frames"); + System.out.println(" frame:pdf - PDF format for Frames"); + System.out.println(" image:png_jpeg - PNG/JPEG format for Images"); + System.out.println(" image:png - PNG format for Images"); + System.out.println(" image:jpeg - JPEG format for Images"); + System.out.println(" text:plain - Plain text format for Texts"); + System.out.println(" text:formatted - Formatted text format for Texts"); + System.out.println(" sound:mp3_wav_flv - MP3/WAV/FLV format for Sounds"); + System.out.println(" sound:mp3_wav - MP3/WAV format for Sounds"); + System.out.println(" sound:wav - WAV format for Sounds"); + System.out.println(" sound:flv - FLV format for Sounds"); + System.out.println(" fla: or xfl: - Specify FLA format version"); + System.out.println(" - values for : cs5,cs5.5,cs6,cc"); + + System.out.println(" You can set multiple formats at once using comma (,)"); + System.out.println(" DO NOT PUT space between comma (,) and next value."); + System.out.println(" The prefix with colon (:) is neccessary."); + System.out.println(" " + (cnt++) + ") -select "); + System.out.println(" ...selects frames/pages for export"); + System.out.println(" Example formats:"); + System.out.println(" 1-5"); + System.out.println(" 2,3"); + System.out.println(" 2-5,7,9-"); + System.out.println(" DO NOT PUT space between comma (,) and next ramge."); + System.out.println(" " + (cnt++) + ") -selectid "); + System.out.println(" ...selects characters for export by character id"); + System.out.println(" format is same as in -select"); + System.out.println(" " + (cnt++) + ") -dumpSWF "); + System.out.println(" ...dumps list of SWF tags to console"); + System.out.println(" " + (cnt++) + ") -compress "); + System.out.println(" ...Compress SWF and save it to "); + System.out.println(" " + (cnt++) + ") -decompress "); + System.out.println(" ...Decompress and save it to "); + System.out.println(" " + (cnt++) + ") -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]"); + System.out.println(" ...Extracts SWF files from ZIP or other binary files"); + System.out.println(" ...-o parameter should contain a file path when \"biggest\" or \"first\" parameter is specified"); + System.out.println(" ...-o parameter should contain a folder path when no exctaction mode or \"all\" parameter is specified"); + System.out.println(" " + (cnt++) + ") -renameInvalidIdentifiers (typeNumber|randomWord) e"); + System.out.println(" ...Renames the invalid identifiers in and save it to "); + System.out.println(" " + (cnt++) + ") -config key=value[,key2=value2][,key3=value3...] [other parameters]"); + System.out.print(" ...Sets configuration values. Available keys[current setting]:"); + for (ConfigurationItem item : commandlineConfigBoolean) { + System.out.print(" " + item + "[" + item.get() + "]"); + } + System.out.println(""); + System.out.println(" Values are boolean, you can use 0/1, true/false, on/off or yes/no."); + System.out.println(" If no other parameters passed, configuration is saved. Otherwise it is used only once."); + System.out.println(" DO NOT PUT space between comma (,) and next value."); + System.out.println(" " + (cnt++) + ") -onerror (abort|retryN|ignore)"); + System.out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file"); + System.out.println(" " + (cnt++) + ") -timeout "); + System.out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds"); + System.out.println(" " + (cnt++) + ") -exportTimeout "); + System.out.println(" ...total export timeout in seconds"); + System.out.println(" " + (cnt++) + ") -exportFileTimeout "); + System.out.println(" ...export timeout for a single AS3 class in seconds"); + System.out.println(" " + (cnt++) + ") -flashpaper2pdf "); + System.out.println(" ...converts FlashPaper SWF file to PDF . Use -zoom parameter to specify image quality."); + System.out.println(" " + (cnt++) + ") -zoom "); + System.out.println(" ...apply zoom during export (currently for FlashPaper conversion only)"); + System.out.println(); + System.out.println("Examples:"); + System.out.println("java -jar ffdec.jar myfile.swf"); + System.out.println("java -jar ffdec.jar -proxy"); + System.out.println("java -jar ffdec.jar -proxy -P1234"); + System.out.println("java -jar ffdec.jar -export script \"C:\\decompiled\" myfile.swf"); + System.out.println("java -jar ffdec.jar -export script \"C:\\decompiled\" myfile.swf -selectas3class com.example.MyClass com.example.SecondClass"); + System.out.println("java -jar ffdec.jar -format script:pcode -export script \"C:\\decompiled\" myfile.swf"); + System.out.println("java -jar ffdec.jar -format script:pcode,text:plain -export script,text,image \"C:\\decompiled\" myfile.swf"); + System.out.println("java -jar ffdec.jar -format fla:cs5.5 -export fla \"C:\\sources\\myfile.fla\" myfile.swf"); + System.out.println("java -jar ffdec.jar -dumpSWF myfile.swf"); + System.out.println("java -jar ffdec.jar -compress myfile.swf myfiledec.swf"); + System.out.println("java -jar ffdec.jar -decompress myfiledec.swf myfile.swf"); + System.out.println("java -jar ffdec.jar -onerror ignore -export script \"C:\\decompiled\" myfile.swf"); + System.out.println("java -jar ffdec.jar -onerror retry 5 -export script \"C:\\decompiled\" myfile.swf"); + System.out.println("java -jar ffdec.jar -config autoDeobfuscate=1,parallelSpeedUp=0 -export script \"C:\\decompiled\" myfile.swf"); + System.out.println(""); + System.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 + * @return path to the file which should be opened or null + * @throws java.io.IOException + */ + public static String parseArguments(String[] arguments) throws IOException { + Level traceLevel = Level.WARNING; + Queue args = new LinkedList<>(); + for (String arg : arguments) { + if (arg.length() > 0) { + args.add(arg); + } + } + AbortRetryIgnoreHandler handler = null; + Map format = new HashMap<>(); + double zoom = 1; + Selection selection = new Selection(); + Selection selectionIds = new Selection(); + String nextParam; + OUTER: + while (true) { + nextParam = args.remove(); + if (nextParam != null) { + nextParam = nextParam.toLowerCase(); + } + switch (nextParam) { + case "-selectid": + selectionIds = parseSelect(args); + break; + case "-select": + selection = parseSelect(args); + break; + case "-zoom": + zoom = parseZoom(args); + break; + case "-format": + format = parseFormat(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 "-affinity": + parseAffinity(args); + break; + case "-priority": + parsePriority(args); + break; + case "-verbose": + traceLevel = Level.FINE; + break; + case "-debug": + Configuration.debugMode.set(true); + break; + default: + break OUTER; + } + if (args.isEmpty()) { + return null; + } + } + if (nextParam.equals("-removefromcontextmenu")) { + ContextMenuTools.addToContextMenu(false, true); + System.exit(0); + } else if (nextParam.equals("-addtocontextmenu")) { + ContextMenuTools.addToContextMenu(true, true); + System.exit(0); + } else if (nextParam.equals("-proxy")) { + parseProxy(args); + } else if (nextParam.equals("-export")) { + parseExport(selection, selectionIds, args, handler, traceLevel, format); + } else if (nextParam.equals("-compress")) { + parseCompress(args); + } else if (nextParam.equals("-decompress")) { + parseDecompress(args); + } else if (nextParam.equals("-extract")) { + parseExtract(args); + } else if (nextParam.equals("-renameinvalididentifiers")) { + parseRenameInvalidIdentifiers(args); + } else if (nextParam.equals("-dumpswf")) { + parseDumpSwf(args); + } else if (nextParam.equals("-flashpaper2pdf")) { + parseFlashPaperToPdf(selection, zoom, args); + } else if (nextParam.equals("-as3compiler")) { + ActionScriptParser.compile(null /*?*/, args.remove(), args.remove()); + } else if (nextParam.equals("-help") || nextParam.equals("--help") || nextParam.equals("/?")) { + printHeader(); + printCmdLineUsage(); + System.exit(0); + } else if (args.isEmpty()) { + return nextParam; + } 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() { + System.err.println("Error: Bad Commandline Arguments!"); + printCmdLineUsage(); + System.exit(1); + } + + private static void setConfigurations(String cfgStr) { + String[] cfgs; + if (cfgStr.contains(",")) { + cfgs = cfgStr.split(","); + } else { + cfgs = new String[]{cfgStr}; + } + + for (String c : cfgs) { + String[] cp; + if (c.contains("=")) { + cp = c.split("="); + } else { + cp = new String[]{c, "1"}; + } + String key = cp[0]; + String value = cp[1]; + if (key.toLowerCase().equals("paralelSpeedUp".toLowerCase())) { + key = "parallelSpeedUp"; + } + for (ConfigurationItem item : commandlineConfigBoolean) { + if (key.toLowerCase().equals(item.getName().toLowerCase())) { + Boolean bValue = parseBooleanConfigValue(value); + if (bValue != null) { + System.out.println("Config " + item.getName() + " set to " + bValue); + item.set(bValue); + } + } + } + } + } + + private static Boolean parseBooleanConfigValue(String value) { + if (value == null) { + return null; + } + + Boolean bValue = null; + value = value.toLowerCase(); + 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 ScriptExportMode strToExportFormat(String exportFormatStr) { + switch (exportFormatStr) { + case "pcode": + return ScriptExportMode.PCODE; + case "pcodehex": + return ScriptExportMode.PCODE_HEX; + case "hex": + return ScriptExportMode.HEX; + default: + return ScriptExportMode.AS; + } + } + + private static void parseConfig(Queue args) { + if (args.isEmpty()) { + System.err.println("Config values expected"); + badArguments(); + } + setConfigurations(args.remove()); + } + + private static class Range { + + public Integer min; + public Integer max; + + public Range(Integer min, Integer max) { + this.min = min; + this.max = max; + } + + public boolean contains(int index) { + Integer minimum = min == null ? Integer.MIN_VALUE : min; + Integer 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(null, null)); + } + + public Selection(List ranges) { + this.ranges = ranges; + } + + public boolean contains(int index) { + for (Range r : ranges) { + if (r.contains(index)) { + return true; + } + } + return false; + } + + } + + private static Selection parseSelect(Queue args) { + List ret = new ArrayList<>(); + if (args.isEmpty()) { + System.err.println("range parameter expected"); + badArguments(); + } + String range = args.remove(); + String[] ranges; + if (range.contains(",")) { + ranges = range.split(","); + } else { + ranges = new String[]{range}; + } + for (String r : ranges) { + Integer min = null; + Integer max = null; + if (r.contains("-")) { + String ps[] = r.split("\\-"); + if (ps.length != 2) { + System.err.println("invalid range"); + badArguments(); + } + 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(); + } + } else { + try { + min = Integer.parseInt(r); + max = min; + } catch (NumberFormatException nfe) { + System.err.println("invalid range"); + badArguments(); + } + } + ret.add(new Range(min, max)); + } + return new Selection(ret); + } + + private static double parseZoom(Queue args) { + if (args.isEmpty()) { + System.err.println("zoom parameter expected"); + badArguments(); + } + try { + return Double.parseDouble(args.remove()); + } catch (NumberFormatException nfe) { + System.err.println("invalid zoom"); + badArguments(); + } + return 1; + } + + private static AbortRetryIgnoreHandler parseOnError(Queue args) { + int errorMode = AbortRetryIgnoreHandler.UNDEFINED; + int retryCount = 0; + + if (args.isEmpty()) { + System.err.println("onerror parameter expected"); + badArguments(); + } + String errorModeParameter = args.remove(); + 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(); + } + + try { + retryCount = Integer.parseInt(args.remove()); + } 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(Queue args) { + if (args.isEmpty()) { + System.err.println("timeout parameter expected"); + badArguments(); + } + try { + int timeout = Integer.parseInt(args.remove()); + Configuration.decompilationTimeoutSingleMethod.set(timeout); + } catch (NumberFormatException nex) { + System.err.println("Bad timeout value"); + } + } + + private static void parseExportTimeout(Queue args) { + if (args.isEmpty()) { + System.err.println("timeout parameter expected"); + badArguments(); + } + try { + int timeout = Integer.parseInt(args.remove()); + Configuration.exportTimeout.set(timeout); + } catch (NumberFormatException nex) { + System.err.println("Bad timeout value"); + } + } + + private static void parseExportFileTimeout(Queue args) { + if (args.isEmpty()) { + System.err.println("timeout parameter expected"); + badArguments(); + } + try { + int timeout = Integer.parseInt(args.remove()); + Configuration.decompilationTimeoutFile.set(timeout); + } catch (NumberFormatException nex) { + System.err.println("Bad timeout value"); + } + } + + private static void parseAffinity(Queue args) { + if (Platform.isWindows()) { + if (args.isEmpty()) { + System.err.println("affinity parameter expected"); + badArguments(); + } + try { + int affinityMask = Integer.parseInt(args.remove()); + 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 parsePriority(Queue args) { + if (Platform.isWindows()) { + if (args.isEmpty()) { + System.err.println("priority parameter expected"); + badArguments(); + } + String priority = args.remove(); + 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 parseProxy(Queue args) { + int port = 55555; + String portStr = args.peek(); + if (portStr != null && portStr.startsWith("-P")) { + args.remove(); + try { + port = Integer.parseInt(portStr.substring(2)); + } catch (NumberFormatException nex) { + System.err.println("Bad port number"); + } + } + Main.startProxy(port); + } + + private static List parseSelectClasses(Queue args) { + List ret = new ArrayList<>(); + if (!args.isEmpty() && args.peek().equals("-selectas3class")) { + args.remove(); + while (!args.isEmpty()) { + ret.add(args.remove()); + + } + } + return ret; + + } + + private static void parseExport(Selection selection, Selection selectionIds, Queue args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats) { + if (args.size() < 3) { + badArguments(); + } + String[] validExportItems = new String[]{ + "script", + "image", + "shape", + "morphshape", + "movie", + "sound", + "binarydata", + "text", + "all", + "frame", + "fla", + "xfl", + "font" + }; + + String[] deprecatedExportFormats = new String[]{ + "as", + "pcode", + "all_as", + "all_pcode", + "all_pcodehex", + "all_hex", + "textplain" + }; + + if (handler == null) { + handler = new ConsoleAbortRetryIgnoreHandler(AbortRetryIgnoreHandler.UNDEFINED, 0); + } + String exportFormatString = args.remove().toLowerCase(); + String exportFormats[]; + if (exportFormatString.contains(",")) { + exportFormats = exportFormatString.split(","); + } else { + exportFormats = new String[]{exportFormatString}; + } + long startTime = System.currentTimeMillis(); + + File outDir = new File(args.remove()); + File inFile = new File(args.remove()); + if (!inFile.exists()) { + System.err.println("Input SWF file does not exist!"); + badArguments(); + } + printHeader(); + boolean exportOK = true; + + List as3classes = new ArrayList<>(); + + try { + SWF exfile = new SWF(new FileInputStream(inFile), Configuration.parallelSpeedUp.get()); + + List extags = new ArrayList<>(); + for (Tag t : exfile.tags) { + 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; + exfile.addEventListener(new EventListener() { + @Override + public void handleEvent(String event, Object data) { + if (level.intValue() <= Level.FINE.intValue() && event.equals("exporting")) { + System.out.println((String) data); + } + if (event.equals("exported")) { + System.out.println((String) data); + } + } + }); + + for (String exportFormat : exportFormats) { + if (!Arrays.asList(validExportItems).contains(exportFormat) && !Arrays.asList(deprecatedExportFormats).contains(exportFormat)) { + System.err.println("Invalid export item:" + exportFormat); + badArguments(); + } + if (Arrays.asList(deprecatedExportFormats).contains(exportFormat)) { + System.err.println("Warning: Using DEPRECATED export item: " + exportFormat + ". Run application with --help parameter to see available formats."); + } + + commandLineMode = true; + + switch (exportFormat) { + case "all": + case "all_as": + case "all_pcode": + case "all_pcodehex": + case "all_hex": { + ScriptExportMode allExportMode = ScriptExportMode.AS; + if (!exportFormat.equals("all")) { + allExportMode = strToExportFormat(exportFormat.substring("all_".length() - 1)); + } else if (formats.containsKey("script")) { + allExportMode = strToExportFormat(formats.get("script")); + } + System.out.println("Exporting images..."); + new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + File.separator + "images", extags, new ImageExportSettings(ImageExportMode.PNG_JPEG)); + System.out.println("Exporting shapes..."); + new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + File.separator + "shapes", extags, new ShapeExportSettings(ShapeExportMode.SVG)); + System.out.println("Exporting morphshapes..."); + new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + File.separator + "morphshapes", extags, new MorphShapeExportSettings(MorphShapeExportMode.SVG)); + System.out.println("Exporting scripts..."); + exfile.exportActionScript(handler, outDir.getAbsolutePath() + File.separator + "scripts", allExportMode, Configuration.parallelSpeedUp.get()); + System.out.println("Exporting movies..."); + new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + File.separator + "movies", extags, new MovieExportSettings(MovieExportMode.FLV)); + System.out.println("Exporting sounds..."); + new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + File.separator + "sounds", extags, new SoundExportSettings(SoundExportMode.MP3_WAV_FLV)); + System.out.println("Exporting binaryData..."); + new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + File.separator + "binaryData", extags, new BinaryDataExportSettings(BinaryDataExportMode.RAW)); + System.out.println("Exporting texts..."); + + String allTextFormat = formats.get("text"); + if (allTextFormat == null) { + allTextFormat = "formatted"; + } + Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); + if (singleTextFile == null) { + singleTextFile = Configuration.textExportSingleFile.get(); + } + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + File.separator + "texts", extags, new TextExportSettings(allTextFormat.equals("formatted") ? TextExportMode.FORMATTED : TextExportMode.PLAIN, singleTextFile)); + } + break; + case "image": { + System.out.println("Exporting images..."); + new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "images" : ""), extags, new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class))); + } + break; + case "shape": { + System.out.println("Exporting shapes..."); + new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "shapes" : ""), extags, new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class))); + } + break; + case "morphshape": { + System.out.println("Exporting morphshapes..."); + new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "morphshapes" : ""), extags, new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class))); + } + break; + case "script": + case "as": + case "pcode": + case "pcodehex": + case "hex": { + System.out.println("Exporting scripts..."); + boolean parallel = Configuration.parallelSpeedUp.get(); + if (as3classes.isEmpty()) { + as3classes = parseSelectClasses(args); + } + if (!as3classes.isEmpty()) { + for (String as3class : as3classes) { + exportOK = exportOK && exfile.exportAS3Class(as3class, outDir.getAbsolutePath(), enumFromStr(formats.get("script"), ScriptExportMode.class), parallel); + } + } else { + exportOK = exportOK && exfile.exportActionScript(handler, outDir.getAbsolutePath(), enumFromStr(formats.get("script"), ScriptExportMode.class), parallel) != null; + } + } + break; + case "movie": { + System.out.println("Exporting movies..."); + new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "movies" : ""), extags, + new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class))); + } + break; + case "font": { + System.out.println("Exporting fonts..."); + new FontExporter().exportFonts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fonts" : ""), extags, + new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class))); + } + break; + case "frame": { + System.out.println("Exporting frames..."); + List frames = new ArrayList<>(); + for (int i = 0; i < exfile.frameCount; i++) { + if (selection.contains(i + 1)) { + frames.add(i); + } + } + exfile.exportFrames(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "frames" : ""), 0, frames, + new FramesExportSettings(enumFromStr(formats.get("frame"), FramesExportMode.class))); + } + break; + case "sound": { + System.out.println("Exporting sounds..."); + new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "sounds" : ""), extags, + new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class))); + } + break; + case "binarydata": { + System.out.println("Exporting binaryData..."); + new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "binaryData" : ""), extags, + new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class))); + } + break; + case "text": { + System.out.println("Exporting texts..."); + Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); + if (singleTextFile == null) { + singleTextFile = Configuration.textExportSingleFile.get(); + } + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""), extags, + new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile)); + } + break; + case "textplain": { + System.out.println("Exporting texts..."); + Boolean singleTextFile = parseBooleanConfigValue(formats.get("singletext")); + if (singleTextFile == null) { + singleTextFile = Configuration.textExportSingleFile.get(); + } + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""), extags, + new TextExportSettings(TextExportMode.PLAIN, singleTextFile)); + } + break; + case "fla": { + System.out.println("Exporting FLA..."); + FLAVersion flaVersion = FLAVersion.fromString(formats.get("fla")); + if (flaVersion == null) { + flaVersion = FLAVersion.CS6; //Defaults to CS6 + } + exfile.exportFla(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fla" : ""), inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), flaVersion); + } + break; + case "xfl": { + System.out.println("Exporting XFL..."); + FLAVersion xflVersion = FLAVersion.fromString(formats.get("xfl")); + if (xflVersion == null) { + xflVersion = FLAVersion.CS6; //Defaults to CS6 + } + exfile.exportXfl(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "xfl" : ""), inFile.getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), xflVersion); + } + break; + default: + exportOK = false; + } + + } + } catch (OutOfMemoryError | Exception ex) { + exportOK = false; + System.err.print("FAIL: Exporting Failed on Exception - "); + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + System.exit(1); + } + long stopTime = System.currentTimeMillis(); + long time = stopTime - startTime; + System.out.println("Export finished. Total export time: " + Helper.formatTimeSec(time)); + if (exportOK) { + System.out.println("OK"); + System.exit(0); + } else { + System.err.println("FAIL"); + System.exit(1); + } + } + + private static void parseCompress(Queue args) { + if (args.size() < 2) { + badArguments(); + } + + try { + try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); + OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { + if (SWF.fws2cws(fis, fos)) { + System.out.println("OK"); + } else { + System.err.println("FAIL"); + } + } catch (FileNotFoundException ex) { + System.err.println("File not found."); + } + } catch (IOException ex) { + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + } + + System.exit(0); + } + + private static void parseDecompress(Queue args) { + if (args.size() < 2) { + badArguments(); + } + + try { + try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); + OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { + if (SWF.decompress(fis, fos)) { + System.out.println("OK"); + System.exit(0); + } else { + System.err.println("FAIL"); + System.exit(1); + } + } catch (FileNotFoundException ex) { + System.err.println("File not found."); + } + } catch (IOException ex) { + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + } + + System.exit(0); + } + + private static void parseExtract(Queue args) { + if (args.size() < 1) { + badArguments(); + } + + String fileName = args.remove(); + SearchMode mode = SearchMode.ALL; + + boolean noCheck = false; + String output = null; + + if (args.size() > 0 && args.peek().toLowerCase().equals("-o")) { + args.remove(); + if (args.size() < 1) { + badArguments(); + } + output = args.remove(); + } + + if (args.size() > 0 && args.peek().toLowerCase().equals("nocheck")) { + noCheck = true; + args.remove(); + } + + if (args.size() > 0) { + String modeStr = args.remove().toLowerCase(); + 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 { + SWFSourceInfo sourceInfo = new SWFSourceInfo(null, fileName, null); + if (!sourceInfo.isBundle()) { + System.err.println("Error: should be a bundle. (ZIP or non SWF binary file)"); + System.exit(1); + } + SWFBundle 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.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + } + + System.exit(0); + } + + private static void parseRenameInvalidIdentifiers(Queue args) { + if (args.size() < 3) { + badArguments(); + } + + String renameTypeStr = args.remove(); + RenameType renameType; + switch (renameTypeStr.toLowerCase()) { + case "typenumber": + renameType = RenameType.TYPENUMBER; + break; + case "randomword": + renameType = RenameType.RANDOMWORD; + break; + default: + System.err.println("Invalid rename type:" + renameTypeStr); + badArguments(); + return; + } + + try { + try (InputStream fis = new BufferedInputStream(new FileInputStream(args.remove())); + OutputStream fos = new BufferedOutputStream(new FileOutputStream(args.remove()))) { + if (SWF.renameInvalidIdentifiers(renameType, fis, fos)) { + System.out.println("OK"); + System.exit(0); + } else { + System.err.println("FAIL"); + System.exit(1); + } + } catch (FileNotFoundException ex) { + System.err.println("File not found."); + } + } catch (IOException ex) { + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + } + + System.exit(0); + } + + private static Map parseFormat(Queue args) { + if (args.size() < 1) { + badArguments(); + } + String fmtStr = args.remove(); + String[] fmts; + if (fmtStr.contains(",")) { + fmts = fmtStr.split(","); + } else { + fmts = new String[]{fmtStr}; + } + Map ret = new HashMap<>(); + for (int i = 0; i < fmts.length; i++) { + String parts[] = fmts[i].split(":"); + ret.put(parts[0].toLowerCase(), parts[1].toLowerCase()); + } + return ret; + } + + private static void parseFlashPaperToPdf(Selection selection, double zoom, Queue args) { + if (args.size() < 2) { + badArguments(); + } + File inFile = new File(args.remove()); + File outFile = new File(args.remove()); + printHeader(); + + try (FileInputStream is = new FileInputStream(inFile)) { + + PDFJob job = null; + + SWF swf = new SWF(is, Configuration.parallelSpeedUp.get()); + int totalPages = 0; + + for (Tag t : swf.tags) { + if (t instanceof DefineSpriteTag) { + DefineSpriteTag ds = (DefineSpriteTag) t; + if ("page1".equals(ds.getExportName())) { + totalPages = 1; + } else { + if (totalPages > 0) { + totalPages++; + } + } + } + } + + int page = 0; + + for (Tag t : swf.tags) { + if (t instanceof DefineSpriteTag) { + DefineSpriteTag ds = (DefineSpriteTag) t; + if ("page1".equals(ds.getExportName())) { + page = 1; + job = new PDFJob(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 + "..."); + RECT displayRect = new RECT(ds.getTimeline().displayRect); + displayRect.Xmax *= zoom; + displayRect.Ymax *= zoom; + Matrix m = new Matrix(); + m.scale(zoom); + BufferedImage img = SWF.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect, m, new ColorTransform(), Color.white).getBufferedImage(); + PageFormat pf = new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + p.setSize(img.getWidth(), img.getHeight()); + pf.setPaper(p); + Graphics g = job.getGraphics(pf); + g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null); + g.dispose(); + 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); + } + System.exit(0); + } + + private static void parseDumpSwf(Queue args) { + if (args.isEmpty()) { + badArguments(); + } + try { + Configuration.dumpTags.set(true); + Configuration.parallelSpeedUp.set(false); + SWFSourceInfo sourceInfo = new SWFSourceInfo(null, args.remove(), null); + Main.parseSWF(sourceInfo); + } catch (Exception ex) { + Logger.getLogger(CommandLineArgumentParser.class.getName()).log(Level.SEVERE, null, ex); + System.exit(1); + } + System.exit(0); + } + + 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().equals(str.toLowerCase())) { + return e; + } + } + return vals[0]; + } +} diff --git a/src/com/jpexs/decompiler/flash/exporters/FontExporter.java b/src/com/jpexs/decompiler/flash/exporters/FontExporter.java index 86229a388..3801d411a 100644 --- a/src/com/jpexs/decompiler/flash/exporters/FontExporter.java +++ b/src/com/jpexs/decompiler/flash/exporters/FontExporter.java @@ -1,221 +1,220 @@ -/* - * Copyright (C) 2014 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.exporters; - -import com.google.typography.font.sfntly.Font; -import com.google.typography.font.sfntly.FontFactory; -import com.google.typography.font.sfntly.data.WritableFontData; -import com.google.typography.font.tools.conversion.woff.WoffWriter; -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.RunnableIOEx; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.PathExporter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.helpers.Helper; -import fontastic.FGlyph; -import fontastic.FPoint; -import fontastic.Fontastic; -import fontastic.PVector; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class FontExporter { - - public List exportFonts(AbortRetryIgnoreHandler handler, String outdir, List tags, final FontExportSettings settings) throws IOException { - List ret = new ArrayList<>(); - if (tags.isEmpty()) { - return ret; - } - File foutdir = new File(outdir); - if (!foutdir.exists()) { - if (!foutdir.mkdirs()) { - if (!foutdir.exists()) { - throw new IOException("Cannot create directory " + outdir); - } - } - } - for (Tag t : tags) { - File newfile = null; - if (t instanceof FontTag) { - final FontTag st = (FontTag) t; - String ext = ".ttf"; - if(settings.mode == FontExportMode.WOFF){ - ext = ".woff"; - } - final File file = new File(outdir + File.separator + st.getCharacterExportFileName() + ext); - newfile = file; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - exportFont(st, settings.mode, file); - } - }, handler).run(); - - ret.add(newfile); - - } - - } - return ret; - } - - public byte[] exportFont(final FontTag t, FontExportMode mode) { - try { - String ext = null; - switch (mode) { - case TTF: - ext = ".ttf"; - break; - case WOFF: - ext = ".woff"; - } - File f = File.createTempFile("temp", ext); - exportFont(t, mode, f); - return Helper.readFile(f.getPath()); - } catch (IOException ex) { - Logger.getLogger(FontExporter.class.getName()).log(Level.SEVERE, null, ex); - } - return new byte[0]; - } - - public void exportFont(final FontTag t, FontExportMode mode, File file) throws IOException { - List shapes = t.getGlyphShapeTable(); - - File ttfFile = file; - - if(mode == FontExportMode.WOFF){ - ttfFile = File.createTempFile("ffdec_export", ".ttf"); - } - - - Fontastic f = new Fontastic(t.getFontName(), ttfFile); - String cop = t.getCopyright(); - - f.getEngine().setCopyrightYear(cop == null ? "" : cop); - f.setAuthor(ApplicationInfo.shortApplicationVerName); - f.setVersion("1.0"); - f.setAscender(t.getAscent() / t.getDivider()); - f.setDescender(t.getDescent() / t.getDivider()); - - for (int i = 0; i < shapes.size(); i++) { - SHAPE s = shapes.get(i); - final List contours = new ArrayList<>(); - PathExporter seb = new PathExporter(s, new ColorTransform()) { - - private double transformX(double x) { - return Math.ceil((double) (x / t.getDivider())); - } - - private double transformY(double y) { - return -Math.ceil((double) (y / t.getDivider())); - } - - List path = new ArrayList<>(); - - @Override - protected void finalizePath() { - FPoint[] points = path.toArray(new FPoint[path.size()]); - if (points.length > 0) { - contours.add(points); - } - path.clear(); - } - - @Override - public void moveTo(double x, double y) { - finalizePath(); - path.add(new FPoint(new PVector(transformX(x), transformY(y)))); - } - - @Override - public void lineTo(double x, double y) { - path.add(new FPoint(new PVector(transformX(x), transformY(y)))); - } - - @Override - public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { - path.add(new FPoint( - new PVector(transformX(anchorX), transformY(anchorY)), - new PVector(transformX(controlX), transformY(controlY)) - )); - - } - }; - seb.export(); - char c = t.glyphToChar(i); - if (contours.isEmpty()) { - continue; - } - if (c == '.') { - continue; - } - final FGlyph g = f.addGlyph(c); - double adv = t.getGlyphAdvance(i); - if (adv != -1) { - g.setAdvanceWidth((int) adv); - } else { - g.setAdvanceWidth(t.getGlyphWidth(i) / t.getDivider() + 100); - } - for (FPoint[] cnt : contours) { - if (cnt.length == 0) { - continue; - } - g.addContour(cnt); - } - - } - f.buildFont(); - - if(mode == FontExportMode.WOFF){ - FontFactory fontFactory = FontFactory.getInstance(); - byte[] fontBytes = new byte[0]; - try(FileInputStream fis = new FileInputStream(ttfFile)){ - fontBytes = new byte[(int) ttfFile.length()]; - fis.read(fontBytes); - } - - Font[] fontArray = null; - fontArray = fontFactory.loadFonts(fontBytes); - - Font font = fontArray[0]; - - try(FileOutputStream fos = new FileOutputStream(file)){ - WoffWriter w = new WoffWriter(); - WritableFontData woffData = w.convert(font); - woffData.copyTo(fos); - } - ttfFile.delete(); - - } - } -} +/* + * Copyright (C) 2014 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.exporters; + +import com.google.typography.font.sfntly.Font; +import com.google.typography.font.sfntly.FontFactory; +import com.google.typography.font.sfntly.data.WritableFontData; +import com.google.typography.font.tools.conversion.woff.WoffWriter; +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.RunnableIOEx; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.PathExporter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.helpers.Helper; +import fontastic.FGlyph; +import fontastic.FPoint; +import fontastic.Fontastic; +import fontastic.PVector; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class FontExporter { + + public List exportFonts(AbortRetryIgnoreHandler handler, String outdir, List tags, final FontExportSettings settings) throws IOException { + List ret = new ArrayList<>(); + if (tags.isEmpty()) { + return ret; + } + File foutdir = new File(outdir); + if (!foutdir.exists()) { + if (!foutdir.mkdirs()) { + if (!foutdir.exists()) { + throw new IOException("Cannot create directory " + outdir); + } + } + } + for (Tag t : tags) { + File newfile = null; + if (t instanceof FontTag) { + final FontTag st = (FontTag) t; + String ext = ".ttf"; + if (settings.mode == FontExportMode.WOFF) { + ext = ".woff"; + } + final File file = new File(outdir + File.separator + st.getCharacterExportFileName() + ext); + newfile = file; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + exportFont(st, settings.mode, file); + } + }, handler).run(); + + ret.add(newfile); + + } + + } + return ret; + } + + public byte[] exportFont(final FontTag t, FontExportMode mode) { + try { + String ext = null; + switch (mode) { + case TTF: + ext = ".ttf"; + break; + case WOFF: + ext = ".woff"; + } + File f = File.createTempFile("temp", ext); + exportFont(t, mode, f); + return Helper.readFile(f.getPath()); + } catch (IOException ex) { + Logger.getLogger(FontExporter.class.getName()).log(Level.SEVERE, null, ex); + } + return new byte[0]; + } + + public void exportFont(final FontTag t, FontExportMode mode, File file) throws IOException { + List shapes = t.getGlyphShapeTable(); + + File ttfFile = file; + + if (mode == FontExportMode.WOFF) { + ttfFile = File.createTempFile("ffdec_export", ".ttf"); + } + + Fontastic f = new Fontastic(t.getFontName(), ttfFile); + String cop = t.getCopyright(); + + f.getEngine().setCopyrightYear(cop == null ? "" : cop); + f.setAuthor(ApplicationInfo.shortApplicationVerName); + f.setVersion("1.0"); + f.setAscender(t.getAscent() / t.getDivider()); + f.setDescender(t.getDescent() / t.getDivider()); + + for (int i = 0; i < shapes.size(); i++) { + SHAPE s = shapes.get(i); + final List contours = new ArrayList<>(); + PathExporter seb = new PathExporter(s, new ColorTransform()) { + + private double transformX(double x) { + return Math.ceil((double) (x / t.getDivider())); + } + + private double transformY(double y) { + return -Math.ceil((double) (y / t.getDivider())); + } + + List path = new ArrayList<>(); + + @Override + protected void finalizePath() { + FPoint[] points = path.toArray(new FPoint[path.size()]); + if (points.length > 0) { + contours.add(points); + } + path.clear(); + } + + @Override + public void moveTo(double x, double y) { + finalizePath(); + path.add(new FPoint(new PVector(transformX(x), transformY(y)))); + } + + @Override + public void lineTo(double x, double y) { + path.add(new FPoint(new PVector(transformX(x), transformY(y)))); + } + + @Override + public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { + path.add(new FPoint( + new PVector(transformX(anchorX), transformY(anchorY)), + new PVector(transformX(controlX), transformY(controlY)) + )); + + } + }; + seb.export(); + char c = t.glyphToChar(i); + if (contours.isEmpty()) { + continue; + } + if (c == '.') { + continue; + } + final FGlyph g = f.addGlyph(c); + double adv = t.getGlyphAdvance(i); + if (adv != -1) { + g.setAdvanceWidth((int) adv); + } else { + g.setAdvanceWidth(t.getGlyphWidth(i) / t.getDivider() + 100); + } + for (FPoint[] cnt : contours) { + if (cnt.length == 0) { + continue; + } + g.addContour(cnt); + } + + } + f.buildFont(); + + if (mode == FontExportMode.WOFF) { + FontFactory fontFactory = FontFactory.getInstance(); + byte[] fontBytes = new byte[0]; + try (FileInputStream fis = new FileInputStream(ttfFile)) { + fontBytes = new byte[(int) ttfFile.length()]; + fis.read(fontBytes); + } + + Font[] fontArray = null; + fontArray = fontFactory.loadFonts(fontBytes); + + Font font = fontArray[0]; + + try (FileOutputStream fos = new FileOutputStream(file)) { + WoffWriter w = new WoffWriter(); + WritableFontData woffData = w.convert(font); + woffData.copyTo(fos); + } + ttfFile.delete(); + + } + } +} diff --git a/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java b/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java index 2c626ecbc..e7231ecff 100644 --- a/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java +++ b/src/com/jpexs/decompiler/flash/exporters/MorphShapeExporter.java @@ -1,116 +1,118 @@ -/* - * Copyright (C) 2014 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.exporters; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.RunnableIOEx; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; -import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class MorphShapeExporter { - - //TODO: implement morphshape export. How to handle 65536 frames? - public List exportMorphShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final MorphShapeExportSettings settings) throws IOException { - List ret = new ArrayList<>(); - if (tags.isEmpty()) { - return ret; - } - File foutdir = new File(outdir); - if (!foutdir.exists()) { - if (!foutdir.mkdirs()) { - if (!foutdir.exists()) { - throw new IOException("Cannot create directory " + outdir); - } - } - } - - for (final Tag t : tags) { - if (t instanceof MorphShapeTag) { - int characterID = 0; - if (t instanceof CharacterTag) { - characterID = ((CharacterTag) t).getCharacterId(); - } - String ext = settings.mode == MorphShapeExportMode.CANVAS ? "html" : "svg"; - - final File file = new File(outdir + File.separator + characterID + "." + ext); - final int fcharacterID = characterID; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - MorphShapeTag mst = (MorphShapeTag) t; - switch (settings.mode) { - case SVG: - try (FileOutputStream fos = new FileOutputStream(file)) { - ExportRectangle rect = new ExportRectangle(mst.getRect()); - SVGExporter exporter = new SVGExporter(rect); - mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); - fos.write(Utf8Helper.getBytes(exporter.getSVG())); - } - break; - case CANVAS: - try (FileOutputStream fos = new FileOutputStream(file)) { - int deltaX = -Math.min(mst.getStartBounds().Xmin, mst.getEndBounds().Xmin); - int deltaY = -Math.min(mst.getStartBounds().Ymin, mst.getEndBounds().Ymin); - CanvasMorphShapeExporter cse = new CanvasMorphShapeExporter(((Tag) mst).getSwf(), mst.getShapeAtRatio(0), mst.getShapeAtRatio(DefineMorphShapeTag.MAX_RATIO), new CXFORMWITHALPHA(), SWF.unitDivisor, deltaX, deltaY); - cse.export(); - Set needed=new HashSet<>(); - SWF.getNeededCharacters(((Tag)mst).getSwf(), ((CharacterTag)mst).getCharacterId(), needed); - ByteArrayOutputStream baos=new ByteArrayOutputStream(); - SWF.writeLibrary(((Tag)mst).getSwf(), needed, baos); - fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(),"UTF-8")))); - } - break; - } - - } - }, handler).run(); - ret.add(file); - } - } - - if(settings.mode == MorphShapeExportMode.CANVAS){ - File fcanvas = new File(foutdir + File.separator + "canvas.js"); - Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); - ret.add(fcanvas); - } - return ret; - } -} +/* + * Copyright (C) 2014 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.exporters; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.RunnableIOEx; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; +import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class MorphShapeExporter { + + //TODO: implement morphshape export. How to handle 65536 frames? + public List exportMorphShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final MorphShapeExportSettings settings) throws IOException { + List ret = new ArrayList<>(); + if (tags.isEmpty()) { + return ret; + } + File foutdir = new File(outdir); + if (!foutdir.exists()) { + if (!foutdir.mkdirs()) { + if (!foutdir.exists()) { + throw new IOException("Cannot create directory " + outdir); + } + } + } + + for (final Tag t : tags) { + if (t instanceof MorphShapeTag) { + int characterID = 0; + if (t instanceof CharacterTag) { + characterID = ((CharacterTag) t).getCharacterId(); + } + String ext = settings.mode == MorphShapeExportMode.CANVAS ? "html" : "svg"; + + final File file = new File(outdir + File.separator + characterID + "." + ext); + final int fcharacterID = characterID; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + MorphShapeTag mst = (MorphShapeTag) t; + switch (settings.mode) { + case SVG: + try (FileOutputStream fos = new FileOutputStream(file)) { + ExportRectangle rect = new ExportRectangle(mst.getRect()); + SVGExporter exporter = new SVGExporter(rect); + mst.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + break; + case CANVAS: + try (FileOutputStream fos = new FileOutputStream(file)) { + int deltaX = -Math.min(mst.getStartBounds().Xmin, mst.getEndBounds().Xmin); + int deltaY = -Math.min(mst.getStartBounds().Ymin, mst.getEndBounds().Ymin); + CanvasMorphShapeExporter cse = new CanvasMorphShapeExporter(((Tag) mst).getSwf(), mst.getShapeAtRatio(0), mst.getShapeAtRatio(DefineMorphShapeTag.MAX_RATIO), new CXFORMWITHALPHA(), SWF.unitDivisor, deltaX, deltaY); + cse.export(); + Set needed = new HashSet<>(); + CharacterTag ct = ((CharacterTag) mst); + needed.add(ct.getCharacterId()); + ct.getNeededCharactersDeep(needed); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWF.writeLibrary(ct.getSwf(), needed, baos); + fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), "UTF-8")))); + } + break; + } + + } + }, handler).run(); + ret.add(file); + } + } + + if (settings.mode == MorphShapeExportMode.CANVAS) { + File fcanvas = new File(foutdir + File.separator + "canvas.js"); + Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); + ret.add(fcanvas); + } + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java index 481faeff2..a6d9c7455 100644 --- a/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java +++ b/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java @@ -1,136 +1,137 @@ -/* - * Copyright (C) 2014 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.exporters; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.RunnableIOEx; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.imageio.ImageIO; - -/** - * - * @author JPEXS - */ -public class ShapeExporter { - - public List exportShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final ShapeExportSettings settings) throws IOException { - List ret = new ArrayList<>(); - if (tags.isEmpty()) { - return ret; - } - File foutdir = new File(outdir); - if (!foutdir.exists()) { - if (!foutdir.mkdirs()) { - if (!foutdir.exists()) { - throw new IOException("Cannot create directory " + outdir); - } - } - } - - for (final Tag t : tags) { - if (t instanceof ShapeTag) { - int characterID = 0; - if (t instanceof CharacterTag) { - characterID = ((CharacterTag) t).getCharacterId(); - } - String ext = "svg"; - if (settings.mode == ShapeExportMode.PNG) { - ext = "png"; - } - if (settings.mode == ShapeExportMode.CANVAS) { - ext = "html"; - } - - final File file = new File(outdir + File.separator + characterID + "." + ext); - final int fcharacterID = characterID; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - ShapeTag st = (ShapeTag) t; - switch (settings.mode) { - case SVG: - try (FileOutputStream fos = new FileOutputStream(file)) { - ExportRectangle rect = new ExportRectangle(st.getRect()); - SVGExporter exporter = new SVGExporter(rect); - st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); - fos.write(Utf8Helper.getBytes(exporter.getSVG())); - } - break; - case PNG: - RECT rect = st.getRect(); - int newWidth = (int) (rect.getWidth() / SWF.unitDivisor); - int newHeight = (int) (rect.getHeight() / SWF.unitDivisor); - SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); - img.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - st.toImage(0, 0, 0, null, 0, img, m, new CXFORMWITHALPHA()); - ImageIO.write(img.getBufferedImage(), "PNG", new FileOutputStream(file)); - break; - case CANVAS: - try (FileOutputStream fos = new FileOutputStream(file)) { - SHAPE shp = st.getShapes(); - int deltaX = -shp.getBounds().Xmin; - int deltaY = -shp.getBounds().Ymin; - CanvasShapeExporter cse = new CanvasShapeExporter(null, SWF.unitDivisor, ((Tag) st).getSwf(), shp, new CXFORMWITHALPHA(), deltaX, deltaY); - cse.export(); - Set needed=new HashSet<>(); - SWF.getNeededCharacters(st.getSwf(), st.getCharacterId(), needed); - ByteArrayOutputStream baos=new ByteArrayOutputStream(); - SWF.writeLibrary(st.getSwf(), needed, baos); - fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(),"UTF-8")))); - } - break; - } - - } - }, handler).run(); - ret.add(file); - } - } - if(settings.mode == ShapeExportMode.CANVAS){ - File fcanvas = new File(foutdir + File.separator + "canvas.js"); - Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); - ret.add(fcanvas); - } - return ret; - } -} +/* + * Copyright (C) 2014 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.exporters; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.RunnableIOEx; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; +import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.imageio.ImageIO; + +/** + * + * @author JPEXS + */ +public class ShapeExporter { + + public List exportShapes(AbortRetryIgnoreHandler handler, final String outdir, List tags, final ShapeExportSettings settings) throws IOException { + List ret = new ArrayList<>(); + if (tags.isEmpty()) { + return ret; + } + File foutdir = new File(outdir); + if (!foutdir.exists()) { + if (!foutdir.mkdirs()) { + if (!foutdir.exists()) { + throw new IOException("Cannot create directory " + outdir); + } + } + } + + for (final Tag t : tags) { + if (t instanceof ShapeTag) { + int characterID = 0; + if (t instanceof CharacterTag) { + characterID = ((CharacterTag) t).getCharacterId(); + } + String ext = "svg"; + if (settings.mode == ShapeExportMode.PNG) { + ext = "png"; + } + if (settings.mode == ShapeExportMode.CANVAS) { + ext = "html"; + } + + final File file = new File(outdir + File.separator + characterID + "." + ext); + final int fcharacterID = characterID; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + ShapeTag st = (ShapeTag) t; + switch (settings.mode) { + case SVG: + try (FileOutputStream fos = new FileOutputStream(file)) { + ExportRectangle rect = new ExportRectangle(st.getRect()); + SVGExporter exporter = new SVGExporter(rect); + st.toSVG(exporter, -2, new CXFORMWITHALPHA(), 0); + fos.write(Utf8Helper.getBytes(exporter.getSVG())); + } + break; + case PNG: + RECT rect = st.getRect(); + int newWidth = (int) (rect.getWidth() / SWF.unitDivisor); + int newHeight = (int) (rect.getHeight() / SWF.unitDivisor); + SerializableImage img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB); + img.fillTransparent(); + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + st.toImage(0, 0, 0, null, 0, img, m, new CXFORMWITHALPHA()); + ImageIO.write(img.getBufferedImage(), "PNG", new FileOutputStream(file)); + break; + case CANVAS: + try (FileOutputStream fos = new FileOutputStream(file)) { + SHAPE shp = st.getShapes(); + int deltaX = -shp.getBounds().Xmin; + int deltaY = -shp.getBounds().Ymin; + CanvasShapeExporter cse = new CanvasShapeExporter(null, SWF.unitDivisor, ((Tag) st).getSwf(), shp, new CXFORMWITHALPHA(), deltaX, deltaY); + cse.export(); + Set needed = new HashSet<>(); + needed.add(st.getCharacterId()); + st.getNeededCharactersDeep(needed); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWF.writeLibrary(st.getSwf(), needed, baos); + fos.write(Utf8Helper.getBytes(cse.getHtml(new String(baos.toByteArray(), "UTF-8")))); + } + break; + } + + } + }, handler).run(); + ret.add(file); + } + } + if (settings.mode == ShapeExportMode.CANVAS) { + File fcanvas = new File(foutdir + File.separator + "canvas.js"); + Helper.saveStream(SWF.class.getClassLoader().getResourceAsStream("com/jpexs/helpers/resource/canvas.js"), fcanvas); + ret.add(fcanvas); + } + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java b/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java index e87770bf2..aa9ededb5 100644 --- a/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java +++ b/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java @@ -1,30 +1,31 @@ -/* - * Copyright (C) 2014 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.exporters.modes; - -/** - * - * @author JPEXS - */ -public enum FramesExportMode { - PNG, - GIF, - AVI, - SVG, - CANVAS, - PDF -} +/* + * Copyright (C) 2014 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.exporters.modes; + +/** + * + * @author JPEXS + */ +public enum FramesExportMode { + + PNG, + GIF, + AVI, + SVG, + CANVAS, + PDF +} diff --git a/src/com/jpexs/decompiler/flash/exporters/morphshape/CanvasMorphShapeExporter.java b/src/com/jpexs/decompiler/flash/exporters/morphshape/CanvasMorphShapeExporter.java index ec65bcec1..192280df9 100644 --- a/src/com/jpexs/decompiler/flash/exporters/morphshape/CanvasMorphShapeExporter.java +++ b/src/com/jpexs/decompiler/flash/exporters/morphshape/CanvasMorphShapeExporter.java @@ -1,532 +1,532 @@ -/* - * Copyright (C) 2010-2014 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.exporters.morphshape; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.Point; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.GRADIENT; -import com.jpexs.decompiler.flash.types.GRADRECORD; -import com.jpexs.decompiler.flash.types.LINESTYLE2; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; - -/** - * - * @author JPEXS - */ -public class CanvasMorphShapeExporter extends MorphShapeExporterBase { - - protected double deltaX = 0; - protected double deltaY = 0; - protected String pathData = ""; - protected String fillData = ""; - protected double unitDivisor; - protected String shapeData = ""; - protected String strokeData = ""; - protected Matrix fillMatrix = null; - protected Matrix fillMatrixEnd = null; - protected String lastRadColor = null; - protected int repeatCnt = 0; - protected SWF swf; - - protected String lineFillData = null; - protected String lineLastRadColor = null; - protected Matrix lineFillMatrix = null; - protected Matrix lineFillMatrixEnd = null; - protected int lineRepeatCnt = 0; - - protected int fillWidth; - protected int fillHeight; - - public CanvasMorphShapeExporter(SWF swf, SHAPE shape, SHAPE endShape, ColorTransform colorTransform, double unitDivisor, int deltaX, int deltaY) { - super(shape, endShape, colorTransform); - this.deltaX = deltaX; - this.deltaY = deltaY; - this.unitDivisor = unitDivisor; - this.swf = swf; - } - - public String getHtml(String needed) { - int width = (int) (Math.max(shape.getBounds().getWidth(), shapeEnd.getBounds().getWidth()) / unitDivisor); - int height = (int) (Math.max(shape.getBounds().getHeight(), shapeEnd.getBounds().getHeight()) / unitDivisor); - - return CanvasShapeExporter.getHtmlPrefix(width, height) + getJsPrefix() + needed + CanvasShapeExporter.getDrawJs(width,height,shapeData) + getJsSuffix(width, height) + CanvasShapeExporter.getHtmlSuffix(); - } - - public static String getJsSuffix(int width, int height) { - String ret = ""; - ret += "}\r\n"; - int step = Math.round(65535 / 100); - int rate = 10; - ret += "var step = " + step + ";\r\n"; - ret += "var ratio = -1;\r\n"; - ret += "function nextFrame(ctx){\r\n"; - ret += "\tctx.clearRect(0,0," + width + "," + height + ");\r\n"; - ret += "\tratio = (ratio+step)%65535;\r\n"; - ret += "\tmorphshape(ctx,ratio);\r\n"; - ret += "}\r\n"; - ret +="window.setInterval(function(){nextFrame(ctx)},"+rate+");\r\n"; - ret += "\r\n"; - return ret; - } - - public static String getJsPrefix(){ - String ret = "" - + "" - + "" - + "\r\n" - + "\r\n" - + "
\r\n" - + "Your browser does not support the HTML5 canvas tag.\r\n" - + "
 
 
" - //+ "
 
" - + "
\r\n" - + "\r\n"; - - } - - public static String getJsSuffix(){ - return "\r\n"; - } - - public static String getHtmlSuffix() { - return "\r\n" - + ""; - } - - public static String getDrawJs(int width,int height,String data){ - return "var originalWidth="+width+";\r\nvar originalHeight="+height+";\r\n function drawFrame(){\r\n" - + "\tctx.save();\r\n\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n"+data +"\tctx.restore();\r\n}\r\n\tdrawFrame();\r\n"; - } - - public String getHtml(String needed) { - RECT r = shape.getBounds(); - int width = (int) (r.getWidth() / unitDivisor); - int height = (int) (r.getHeight() / unitDivisor); - - return getHtmlPrefix(width, height) + getJsPrefix() + needed +getDrawJs(width,height,shapeData) + getJsSuffix() + getHtmlSuffix(); - } - - public String getShapeData() { - return shapeData; - } - - public CanvasShapeExporter(RGB basicFill, double unitDivisor, SWF swf, SHAPE shape, ColorTransform colorTransform, int deltaX, int deltaY) { - super(shape, colorTransform); - this.swf = swf; - this.unitDivisor = unitDivisor; - this.basicFill = basicFill; - this.deltaX = deltaX; - this.deltaY = deltaY; - } - - @Override - public void beginShape() { - shapeData = ""; - } - - @Override - public void endShape() { - } - - @Override - public void beginFills() { - } - - @Override - public void endFills() { - } - - @Override - public void beginLines() { - } - - @Override - public void endLines() { - finalizePath(); - } - - @Override - public void beginFill(RGB color) { - finalizePath(); - if (color == null) { - fillData += "\tctx.fillStyle=defaultFill;\r\n"; - }else{ - fillData += "\tctx.fillStyle=" + color(color) + ";\r\n"; - } - } - - @Override - public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { - finalizePath(); - - //TODO: How many repeats is ideal? - final int REPEAT_CNT = 5; - - repeatCnt = spreadMethod == GRADIENT.SPREAD_PAD_MODE ? 0 : REPEAT_CNT; - - if (type == FILLSTYLE.LINEAR_GRADIENT) { - Point start = matrix.transform(new Point(-16384 - 32768 * repeatCnt, 0)); - Point end = matrix.transform(new Point(16384 + 32768 * repeatCnt, 0)); - start.x += deltaX; - start.y += deltaY; - end.x += deltaX; - end.y += deltaY; - fillData += "\tvar grd=ctx.createLinearGradient(" + Double.toString(start.x / unitDivisor) + "," + Double.toString(start.y / unitDivisor) + "," + Double.toString(end.x / unitDivisor) + "," + Double.toString(end.y / unitDivisor) + ");\r\n"; - } else { - matrix.translateX /= unitDivisor; - matrix.translateY /= unitDivisor; - matrix.scaleX /= unitDivisor; - matrix.scaleY /= unitDivisor; - matrix.rotateSkew0 /= unitDivisor; - matrix.rotateSkew1 /= unitDivisor; - fillMatrix = matrix; - - matrix.translateX += deltaX / unitDivisor; - matrix.translateY += deltaY / unitDivisor; - - fillData += "\tvar grd=ctx.createRadialGradient(" + focalPointRatio * 16384 + ",0,0,0,0," + (16384 + 32768 * repeatCnt) + ");\r\n"; - } - int repeatTotal = repeatCnt * 2 + 1; - double oneHeight = 1.0 / repeatTotal; - double pos = 0; - boolean revert = false; - if (type != FILLSTYLE.LINEAR_GRADIENT && spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { - revert = true; - } - for (int i = 0; i < repeatTotal; i++) { - if (spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { - revert = !revert; - } - for (GRADRECORD r : gradientRecords) { - fillData += "\tgrd.addColorStop(" + Double.toString(pos + (oneHeight * (revert ? 255 - r.ratio : r.ratio) / 255.0)) + "," + color(r.color) + ");\r\n"; - lastRadColor = color(r.color); - } - pos += oneHeight; - } - fillData += "\tctx.fillStyle = grd;\r\n"; - } - - public static String color(Color color) { - return color(new RGBA(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); - } - - public static String color(RGB rgb) { - if ((rgb instanceof RGBA) && (((RGBA) rgb).alpha < 255)) { - RGBA rgba = (RGBA) rgb; - return "tocolor(ctrans.apply([" + rgba.red + "," + rgba.green + "," + rgba.blue + "," + rgba.getAlphaFloat() + "]))"; - } else { - return "tocolor(ctrans.apply([" + rgb.red + "," + rgb.green + "," + rgb.blue + ",1]))"; - } - - } - - @Override - public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) { - finalizePath(); - ImageTag image = null; - for (Tag t : swf.tags) { - if (t instanceof ImageTag) { - ImageTag i = (ImageTag) t; - if (i.getCharacterId() == bitmapId) { - image = i; - SerializableImage im=i.getImage(); - fillWidth = im.getWidth(); - fillHeight = im.getHeight(); - break; - } - } - } - if (image != null) { - SerializableImage img = image.getImage(); - if (img != null) { - colorTransform.apply(img); - if (matrix != null) { - matrix.translateX /= unitDivisor; - matrix.translateY /= unitDivisor; - matrix.scaleX /= unitDivisor; - matrix.scaleY /= unitDivisor; - matrix.rotateSkew0 /= unitDivisor; - matrix.rotateSkew1 /= unitDivisor; - matrix.translateX += deltaX / unitDivisor; - matrix.translateY += deltaY / unitDivisor; - fillMatrix = matrix; - } - - fillData += "\tvar fimg = ctrans.applyToImage(image" + bitmapId + ");\r\n"; - fillData += "\tvar pat=ctx.createPattern(fimg,\"repeat\");\r\n"; - fillData += "\tctx.fillStyle = pat;\r\n"; - } - } - } - - @Override - public void endFill() { - finalizePath(); - } - - @Override - public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { - finalizePath(); - thickness /= unitDivisor; - - if (color != null) { //gradient lines have no color - strokeData += "\tctx.strokeStyle=" + color(color) + ";\r\n"; - } - strokeData += "\tctx.lineWidth=" + Double.toString(thickness == 0 ? 1 : thickness) + ";\r\n"; - switch (startCaps) { - case LINESTYLE2.NO_CAP: - strokeData += "\tctx.lineCap=\"butt\";\r\n"; - break; - case LINESTYLE2.SQUARE_CAP: - strokeData += "\tctx.lineCap=\"square\";\r\n"; - break; - default: - strokeData += "\tctx.lineCap=\"round\";\r\n"; - break; - } - switch (joints) { - case LINESTYLE2.BEVEL_JOIN: - strokeData += "\tctx.lineJoin=\"bevel\";\r\n"; - break; - case LINESTYLE2.ROUND_JOIN: - strokeData += "\tctx.lineJoin=\"round\";\r\n"; - break; - default: - strokeData += "\tctx.lineJoin=\"miter\";\r\n"; - strokeData += "\tctx.miterLimit=" + Integer.toString(miterLimit) + ";\r\n"; - break; - } - } - - @Override - public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { - lineFillData = ""; - - //TODO: How many repeats is ideal? - final int REPEAT_CNT = 5; - - lineRepeatCnt = spreadMethod == GRADIENT.SPREAD_PAD_MODE ? 0 : REPEAT_CNT; - - if (type == FILLSTYLE.LINEAR_GRADIENT) { - Point start = matrix.transform(new Point(-16384 - 32768 * lineRepeatCnt, 0)); - Point end = matrix.transform(new Point(16384 + 32768 * lineRepeatCnt, 0)); - start.x += deltaX; - start.y += deltaY; - end.x += deltaX; - end.y += deltaY; - lineFillData += "\tvar grd=ctx.createLinearGradient(" + Double.toString(start.x / unitDivisor) + "," + Double.toString(start.y / unitDivisor) + "," + Double.toString(end.x / unitDivisor) + "," + Double.toString(end.y / unitDivisor) + ");\r\n"; - } else { - matrix.translateX /= unitDivisor; - matrix.translateY /= unitDivisor; - matrix.scaleX /= unitDivisor; - matrix.scaleY /= unitDivisor; - matrix.rotateSkew0 /= unitDivisor; - matrix.rotateSkew1 /= unitDivisor; - lineFillMatrix = matrix; - - matrix.translateX += deltaX / unitDivisor; - matrix.translateY += deltaY / unitDivisor; - - lineFillData += "\tvar grd=ctx.createRadialGradient(" + focalPointRatio * 16384 + ",0,0,0,0," + (16384 + 32768 * lineRepeatCnt) + ");\r\n"; - } - int repeatTotal = lineRepeatCnt * 2 + 1; - double oneHeight = 1.0 / repeatTotal; - double pos = 0; - boolean revert = false; - if (type != FILLSTYLE.LINEAR_GRADIENT && spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { - revert = true; - } - for (int i = 0; i < repeatTotal; i++) { - if (spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { - revert = !revert; - } - for (GRADRECORD r : gradientRecords) { - lineFillData += "\tgrd.addColorStop(" + Double.toString(pos + (oneHeight * (revert ? 255 - r.ratio : r.ratio) / 255.0)) + "," + color(r.color) + ");\r\n"; - lineLastRadColor = color(r.color); - } - pos += oneHeight; - } - lineFillData += "\tctx.fillStyle = grd;\r\n"; - - String preStrokeData = ""; - - preStrokeData += "\tvar lcanvas = document.createElement(\"canvas\");\r\n"; - preStrokeData += "\tlcanvas.width = canvas.width;\r\n"; - preStrokeData += "\tlcanvas.height=canvas.height;\r\n"; - preStrokeData += "\tvar lctx = lcanvas.getContext(\"2d\");\r\n"; - preStrokeData += "\tenhanceContext(lctx);\r\n"; - preStrokeData += "\tlctx.applyTransforms(ctx._matrices);\r\n"; - preStrokeData += "\tctx = lctx;\r\n"; - strokeData = preStrokeData + strokeData; - } - - @Override - public void moveTo(double x, double y) { - currentDrawCommand = DRAW_COMMAND_M; - pathData += currentDrawCommand + " "; - x += deltaX; - y += deltaY; - pathData += Helper.doubleStr(x / unitDivisor) + " " - + Helper.doubleStr(y / unitDivisor) + " "; - } - - @Override - public void lineTo(double x, double y) { - if (!currentDrawCommand.equals(DRAW_COMMAND_L)) { - currentDrawCommand = DRAW_COMMAND_L; - pathData += currentDrawCommand + " "; - } - x += deltaX; - y += deltaY; - pathData += Helper.doubleStr(x / unitDivisor) + " " - + Helper.doubleStr(y / unitDivisor) + " "; - } - - @Override - public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { - if (!currentDrawCommand.equals(DRAW_COMMAND_Q)) { - currentDrawCommand = DRAW_COMMAND_Q; - pathData += currentDrawCommand + " "; - } - controlX += deltaX; - anchorX += deltaX; - controlY += deltaY; - anchorY += deltaY; - pathData += Helper.doubleStr(controlX / unitDivisor) + " " - + Helper.doubleStr(controlY / unitDivisor) + " " - + Helper.doubleStr(anchorX / unitDivisor) + " " - + Helper.doubleStr(anchorY / unitDivisor) + " "; - } - - protected void finalizePath() { - if (!"".equals(pathData)) { - pathData = "\tdrawPath(ctx,\"" + pathData + "\");\r\n"; - - if (lineFillData != null) { - String preLineFillData = ""; - preLineFillData += "\tvar oldctx = ctx;\r\n"; - preLineFillData += "\tctx.save();\r\n"; - preLineFillData += strokeData; - preLineFillData += pathData; - preLineFillData += "\tctx.stroke();\r\n"; - preLineFillData += "\tvar lfcanvas = document.createElement(\"canvas\");\r\n"; - preLineFillData += "\tlfcanvas.width = canvas.width;\r\n"; - preLineFillData += "\tlfcanvas.height=canvas.height;\r\n"; - preLineFillData += "\tvar lfctx = lfcanvas.getContext(\"2d\");\r\n"; - preLineFillData += "\tenhanceContext(lfctx);\r\n"; - preLineFillData += "\tlfctx.applyTransforms(ctx._matrices);\r\n"; - preLineFillData += "\tctx = lfctx;"; - if (lineLastRadColor != null) { - preLineFillData += "\tctx.fillStyle=" + lineLastRadColor + ";\r\n\tctx.fill(\"evenodd\");\r\n"; - } - - preLineFillData += "\tctx.transform(" + Helper.doubleStr(lineFillMatrix.scaleX) + "," + Helper.doubleStr(lineFillMatrix.rotateSkew0) + "," + Helper.doubleStr(lineFillMatrix.rotateSkew1) + "," + Helper.doubleStr(lineFillMatrix.scaleY) + "," + Helper.doubleStr(lineFillMatrix.translateX) + "," + Helper.doubleStr(lineFillMatrix.translateY) + ");\r\n"; - lineFillData = preLineFillData + lineFillData; - lineFillData += "\tctx.fillRect(" + (-16384 - 32768 * lineRepeatCnt) + "," + (-16384 - 32768 * lineRepeatCnt) + "," + (2 * 16384 + 32768 * 2 * lineRepeatCnt) + "," + (2 * 16384 + 32768 * 2 * lineRepeatCnt) + ");\r\n"; - - lineFillData += "\tctx = oldctx;\r\n"; - - //lcanvas - stroke - //lfcanvas - stroke background - lineFillData += "\tvar limgd = lctx.getImageData(0, 0, lcanvas.width, lcanvas.height);\r\n" - + "\tvar lpix = limgd.data;\r\n" - + "\tvar lfimgd = lfctx.getImageData(0, 0, lfcanvas.width, lfcanvas.height);\r\n" - + "\tvar lfpix = lfimgd.data;\r\n" - + "\tvar imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n" - + "\tvar pix = imgd.data;\r\n" - + "\tfor (var i = 0; i < lpix.length; i += 4) {\r\n" - + "\t\tif(lpix[i+3]>0){ pix[i] = lfpix[i]; pix[i+1] = lfpix[i+1]; pix[i+2] = lfpix[i+2]; pix[i+3] = lfpix[i+3];}\r\n" - + "\t}\r\n" - + "\tctx.putImageData(imgd, 0, 0);\r\n"; - lineFillData += "\tctx.restore();\r\n"; - strokeData = ""; - } else { - pathData += strokeData; - } - if (fillMatrix != null) { - if (lastRadColor != null) { - pathData += "\tctx.fillStyle=" + lastRadColor + ";\r\n\tctx.fill(\"evenodd\");\r\n"; - } - pathData += "\tctx.save();\r\n"; - pathData += "\tctx.clip();\r\n"; - pathData += "\tctx.transform(" + Helper.doubleStr(fillMatrix.scaleX) + "," + Helper.doubleStr(fillMatrix.rotateSkew0) + "," + Helper.doubleStr(fillMatrix.rotateSkew1) + "," + Helper.doubleStr(fillMatrix.scaleY) + "," + Helper.doubleStr(fillMatrix.translateX) + "," + Helper.doubleStr(fillMatrix.translateY) + ");\r\n"; - if(fillWidth>0){//repeating bitmap glitch fix - //make bitmap 1px wider - double s_w = (fillWidth+1)/(double)fillWidth; - double s_h = (fillHeight+1)/(double)fillHeight; - - pathData += "\tctx.transform("+(s_w)+",0,0,"+s_h+",-0.5,-0.5);\r\n"; - } - pathData += fillData; - pathData += "\tctx.fillRect(" + (-16384 - 32768 * repeatCnt) + "," + (-16384 - 32768 * repeatCnt) + "," + (2 * 16384 + 32768 * 2 * repeatCnt) + "," + (2 * 16384 + 32768 * 2 * repeatCnt) + ");\r\n"; - pathData += "\tctx.restore();\r\n"; - shapeData += pathData; - } else { - if (!"".equals(fillData)) { - pathData += "\tctx.fill(\"evenodd\");\r\n"; - } - shapeData += fillData + pathData; - } - if (!"".equals(strokeData)) { - shapeData += "\tctx.stroke();\r\n"; - } else if (lineFillData != null) { - shapeData += lineFillData; - } - } - - repeatCnt = 0; - - pathData = ""; - fillData = ""; - strokeData = ""; - fillMatrix = null; - lastRadColor = null; - - lineRepeatCnt = 0; - lineFillData = null; - lineLastRadColor = null; - lineFillMatrix = null; - - fillWidth = 0; - fillHeight = 0; - } - - -} +/* + * Copyright (C) 2010-2014 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.exporters.shape; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.Point; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; + +/** + * + * @author JPEXS + */ +public class CanvasShapeExporter extends ShapeExporterBase { + + protected static final String DRAW_COMMAND_M = "M"; + protected static final String DRAW_COMMAND_L = "L"; + protected static final String DRAW_COMMAND_Q = "Q"; + protected String currentDrawCommand = ""; + + protected String pathData = ""; + protected String shapeData = ""; + protected String html = ""; + protected String strokeData = ""; + protected String fillData = ""; + protected double deltaX = 0; + protected double deltaY = 0; + protected Matrix fillMatrix = null; + protected String lastRadColor = null; + protected SWF swf; + protected int repeatCnt = 0; + protected double unitDivisor; + protected RGB basicFill; + protected String lineFillData = null; + protected String lineLastRadColor = null; + protected Matrix lineFillMatrix = null; + protected int lineRepeatCnt = 0; + protected int fillWidth = 0; + protected int fillHeight = 0; + + public static String getJsPrefix() { + return "" + + "" + + "" + + "\r\n" + + "\r\n" + + "
\r\n" + + "Your browser does not support the HTML5 canvas tag.\r\n" + + "
 
 
" + //+ "
 
" + + "
\r\n" + + "\r\n"; + + } + + public static String getJsSuffix() { + return "\r\n"; + } + + public static String getHtmlSuffix() { + return "\r\n" + + ""; + } + + public static String getDrawJs(int width, int height, String data) { + return "var originalWidth=" + width + ";\r\nvar originalHeight=" + height + ";\r\n function drawFrame(){\r\n" + + "\tctx.save();\r\n\tctx.transform(canvas.width/originalWidth,0,0,canvas.height/originalHeight,0,0);\r\n" + data + "\tctx.restore();\r\n}\r\n\tdrawFrame();\r\n"; + } + + public String getHtml(String needed) { + RECT r = shape.getBounds(); + int width = (int) (r.getWidth() / unitDivisor); + int height = (int) (r.getHeight() / unitDivisor); + + return getHtmlPrefix(width, height) + getJsPrefix() + needed + getDrawJs(width, height, shapeData) + getJsSuffix() + getHtmlSuffix(); + } + + public String getShapeData() { + return shapeData; + } + + public CanvasShapeExporter(RGB basicFill, double unitDivisor, SWF swf, SHAPE shape, ColorTransform colorTransform, int deltaX, int deltaY) { + super(shape, colorTransform); + this.swf = swf; + this.unitDivisor = unitDivisor; + this.basicFill = basicFill; + this.deltaX = deltaX; + this.deltaY = deltaY; + } + + @Override + public void beginShape() { + shapeData = ""; + } + + @Override + public void endShape() { + } + + @Override + public void beginFills() { + } + + @Override + public void endFills() { + } + + @Override + public void beginLines() { + } + + @Override + public void endLines() { + finalizePath(); + } + + @Override + public void beginFill(RGB color) { + finalizePath(); + if (color == null) { + fillData += "\tctx.fillStyle=defaultFill;\r\n"; + } else { + fillData += "\tctx.fillStyle=" + color(color) + ";\r\n"; + } + } + + @Override + public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + finalizePath(); + + //TODO: How many repeats is ideal? + final int REPEAT_CNT = 5; + + repeatCnt = spreadMethod == GRADIENT.SPREAD_PAD_MODE ? 0 : REPEAT_CNT; + + if (type == FILLSTYLE.LINEAR_GRADIENT) { + Point start = matrix.transform(new Point(-16384 - 32768 * repeatCnt, 0)); + Point end = matrix.transform(new Point(16384 + 32768 * repeatCnt, 0)); + start.x += deltaX; + start.y += deltaY; + end.x += deltaX; + end.y += deltaY; + fillData += "\tvar grd=ctx.createLinearGradient(" + Double.toString(start.x / unitDivisor) + "," + Double.toString(start.y / unitDivisor) + "," + Double.toString(end.x / unitDivisor) + "," + Double.toString(end.y / unitDivisor) + ");\r\n"; + } else { + matrix.translateX /= unitDivisor; + matrix.translateY /= unitDivisor; + matrix.scaleX /= unitDivisor; + matrix.scaleY /= unitDivisor; + matrix.rotateSkew0 /= unitDivisor; + matrix.rotateSkew1 /= unitDivisor; + fillMatrix = matrix; + + matrix.translateX += deltaX / unitDivisor; + matrix.translateY += deltaY / unitDivisor; + + fillData += "\tvar grd=ctx.createRadialGradient(" + focalPointRatio * 16384 + ",0,0,0,0," + (16384 + 32768 * repeatCnt) + ");\r\n"; + } + int repeatTotal = repeatCnt * 2 + 1; + double oneHeight = 1.0 / repeatTotal; + double pos = 0; + boolean revert = false; + if (type != FILLSTYLE.LINEAR_GRADIENT && spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { + revert = true; + } + for (int i = 0; i < repeatTotal; i++) { + if (spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { + revert = !revert; + } + for (GRADRECORD r : gradientRecords) { + fillData += "\tgrd.addColorStop(" + Double.toString(pos + (oneHeight * (revert ? 255 - r.ratio : r.ratio) / 255.0)) + "," + color(r.color) + ");\r\n"; + lastRadColor = color(r.color); + } + pos += oneHeight; + } + fillData += "\tctx.fillStyle = grd;\r\n"; + } + + public static String color(Color color) { + return color(new RGBA(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); + } + + public static String color(RGB rgb) { + if ((rgb instanceof RGBA) && (((RGBA) rgb).alpha < 255)) { + RGBA rgba = (RGBA) rgb; + return "tocolor(ctrans.apply([" + rgba.red + "," + rgba.green + "," + rgba.blue + "," + rgba.getAlphaFloat() + "]))"; + } else { + return "tocolor(ctrans.apply([" + rgb.red + "," + rgb.green + "," + rgb.blue + ",1]))"; + } + + } + + @Override + public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) { + finalizePath(); + ImageTag image = null; + for (Tag t : swf.tags) { + if (t instanceof ImageTag) { + ImageTag i = (ImageTag) t; + if (i.getCharacterId() == bitmapId) { + image = i; + SerializableImage im = i.getImage(); + fillWidth = im.getWidth(); + fillHeight = im.getHeight(); + break; + } + } + } + if (image != null) { + SerializableImage img = image.getImage(); + if (img != null) { + colorTransform.apply(img); + if (matrix != null) { + matrix.translateX /= unitDivisor; + matrix.translateY /= unitDivisor; + matrix.scaleX /= unitDivisor; + matrix.scaleY /= unitDivisor; + matrix.rotateSkew0 /= unitDivisor; + matrix.rotateSkew1 /= unitDivisor; + matrix.translateX += deltaX / unitDivisor; + matrix.translateY += deltaY / unitDivisor; + fillMatrix = matrix; + } + + fillData += "\tvar fimg = ctrans.applyToImage(image" + bitmapId + ");\r\n"; + fillData += "\tvar pat=ctx.createPattern(fimg,\"repeat\");\r\n"; + fillData += "\tctx.fillStyle = pat;\r\n"; + } + } + } + + @Override + public void endFill() { + finalizePath(); + } + + @Override + public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) { + finalizePath(); + thickness /= unitDivisor; + + if (color != null) { //gradient lines have no color + strokeData += "\tctx.strokeStyle=" + color(color) + ";\r\n"; + } + strokeData += "\tctx.lineWidth=" + Double.toString(thickness == 0 ? 1 : thickness) + ";\r\n"; + switch (startCaps) { + case LINESTYLE2.NO_CAP: + strokeData += "\tctx.lineCap=\"butt\";\r\n"; + break; + case LINESTYLE2.SQUARE_CAP: + strokeData += "\tctx.lineCap=\"square\";\r\n"; + break; + default: + strokeData += "\tctx.lineCap=\"round\";\r\n"; + break; + } + switch (joints) { + case LINESTYLE2.BEVEL_JOIN: + strokeData += "\tctx.lineJoin=\"bevel\";\r\n"; + break; + case LINESTYLE2.ROUND_JOIN: + strokeData += "\tctx.lineJoin=\"round\";\r\n"; + break; + default: + strokeData += "\tctx.lineJoin=\"miter\";\r\n"; + strokeData += "\tctx.miterLimit=" + Integer.toString(miterLimit) + ";\r\n"; + break; + } + } + + @Override + public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) { + lineFillData = ""; + + //TODO: How many repeats is ideal? + final int REPEAT_CNT = 5; + + lineRepeatCnt = spreadMethod == GRADIENT.SPREAD_PAD_MODE ? 0 : REPEAT_CNT; + + if (type == FILLSTYLE.LINEAR_GRADIENT) { + Point start = matrix.transform(new Point(-16384 - 32768 * lineRepeatCnt, 0)); + Point end = matrix.transform(new Point(16384 + 32768 * lineRepeatCnt, 0)); + start.x += deltaX; + start.y += deltaY; + end.x += deltaX; + end.y += deltaY; + lineFillData += "\tvar grd=ctx.createLinearGradient(" + Double.toString(start.x / unitDivisor) + "," + Double.toString(start.y / unitDivisor) + "," + Double.toString(end.x / unitDivisor) + "," + Double.toString(end.y / unitDivisor) + ");\r\n"; + } else { + matrix.translateX /= unitDivisor; + matrix.translateY /= unitDivisor; + matrix.scaleX /= unitDivisor; + matrix.scaleY /= unitDivisor; + matrix.rotateSkew0 /= unitDivisor; + matrix.rotateSkew1 /= unitDivisor; + lineFillMatrix = matrix; + + matrix.translateX += deltaX / unitDivisor; + matrix.translateY += deltaY / unitDivisor; + + lineFillData += "\tvar grd=ctx.createRadialGradient(" + focalPointRatio * 16384 + ",0,0,0,0," + (16384 + 32768 * lineRepeatCnt) + ");\r\n"; + } + int repeatTotal = lineRepeatCnt * 2 + 1; + double oneHeight = 1.0 / repeatTotal; + double pos = 0; + boolean revert = false; + if (type != FILLSTYLE.LINEAR_GRADIENT && spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { + revert = true; + } + for (int i = 0; i < repeatTotal; i++) { + if (spreadMethod == GRADIENT.SPREAD_REFLECT_MODE) { + revert = !revert; + } + for (GRADRECORD r : gradientRecords) { + lineFillData += "\tgrd.addColorStop(" + Double.toString(pos + (oneHeight * (revert ? 255 - r.ratio : r.ratio) / 255.0)) + "," + color(r.color) + ");\r\n"; + lineLastRadColor = color(r.color); + } + pos += oneHeight; + } + lineFillData += "\tctx.fillStyle = grd;\r\n"; + + String preStrokeData = ""; + + preStrokeData += "\tvar lcanvas = document.createElement(\"canvas\");\r\n"; + preStrokeData += "\tlcanvas.width = canvas.width;\r\n"; + preStrokeData += "\tlcanvas.height=canvas.height;\r\n"; + preStrokeData += "\tvar lctx = lcanvas.getContext(\"2d\");\r\n"; + preStrokeData += "\tenhanceContext(lctx);\r\n"; + preStrokeData += "\tlctx.applyTransforms(ctx._matrices);\r\n"; + preStrokeData += "\tctx = lctx;\r\n"; + strokeData = preStrokeData + strokeData; + } + + @Override + public void moveTo(double x, double y) { + currentDrawCommand = DRAW_COMMAND_M; + pathData += currentDrawCommand + " "; + x += deltaX; + y += deltaY; + pathData += Helper.doubleStr(x / unitDivisor) + " " + + Helper.doubleStr(y / unitDivisor) + " "; + } + + @Override + public void lineTo(double x, double y) { + if (!currentDrawCommand.equals(DRAW_COMMAND_L)) { + currentDrawCommand = DRAW_COMMAND_L; + pathData += currentDrawCommand + " "; + } + x += deltaX; + y += deltaY; + pathData += Helper.doubleStr(x / unitDivisor) + " " + + Helper.doubleStr(y / unitDivisor) + " "; + } + + @Override + public void curveTo(double controlX, double controlY, double anchorX, double anchorY) { + if (!currentDrawCommand.equals(DRAW_COMMAND_Q)) { + currentDrawCommand = DRAW_COMMAND_Q; + pathData += currentDrawCommand + " "; + } + controlX += deltaX; + anchorX += deltaX; + controlY += deltaY; + anchorY += deltaY; + pathData += Helper.doubleStr(controlX / unitDivisor) + " " + + Helper.doubleStr(controlY / unitDivisor) + " " + + Helper.doubleStr(anchorX / unitDivisor) + " " + + Helper.doubleStr(anchorY / unitDivisor) + " "; + } + + protected void finalizePath() { + if (!"".equals(pathData)) { + pathData = "\tdrawPath(ctx,\"" + pathData + "\");\r\n"; + + if (lineFillData != null) { + String preLineFillData = ""; + preLineFillData += "\tvar oldctx = ctx;\r\n"; + preLineFillData += "\tctx.save();\r\n"; + preLineFillData += strokeData; + preLineFillData += pathData; + preLineFillData += "\tctx.stroke();\r\n"; + preLineFillData += "\tvar lfcanvas = document.createElement(\"canvas\");\r\n"; + preLineFillData += "\tlfcanvas.width = canvas.width;\r\n"; + preLineFillData += "\tlfcanvas.height=canvas.height;\r\n"; + preLineFillData += "\tvar lfctx = lfcanvas.getContext(\"2d\");\r\n"; + preLineFillData += "\tenhanceContext(lfctx);\r\n"; + preLineFillData += "\tlfctx.applyTransforms(ctx._matrices);\r\n"; + preLineFillData += "\tctx = lfctx;"; + if (lineLastRadColor != null) { + preLineFillData += "\tctx.fillStyle=" + lineLastRadColor + ";\r\n\tctx.fill(\"evenodd\");\r\n"; + } + + preLineFillData += "\tctx.transform(" + Helper.doubleStr(lineFillMatrix.scaleX) + "," + Helper.doubleStr(lineFillMatrix.rotateSkew0) + "," + Helper.doubleStr(lineFillMatrix.rotateSkew1) + "," + Helper.doubleStr(lineFillMatrix.scaleY) + "," + Helper.doubleStr(lineFillMatrix.translateX) + "," + Helper.doubleStr(lineFillMatrix.translateY) + ");\r\n"; + lineFillData = preLineFillData + lineFillData; + lineFillData += "\tctx.fillRect(" + (-16384 - 32768 * lineRepeatCnt) + "," + (-16384 - 32768 * lineRepeatCnt) + "," + (2 * 16384 + 32768 * 2 * lineRepeatCnt) + "," + (2 * 16384 + 32768 * 2 * lineRepeatCnt) + ");\r\n"; + + lineFillData += "\tctx = oldctx;\r\n"; + + //lcanvas - stroke + //lfcanvas - stroke background + lineFillData += "\tvar limgd = lctx.getImageData(0, 0, lcanvas.width, lcanvas.height);\r\n" + + "\tvar lpix = limgd.data;\r\n" + + "\tvar lfimgd = lfctx.getImageData(0, 0, lfcanvas.width, lfcanvas.height);\r\n" + + "\tvar lfpix = lfimgd.data;\r\n" + + "\tvar imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);\r\n" + + "\tvar pix = imgd.data;\r\n" + + "\tfor (var i = 0; i < lpix.length; i += 4) {\r\n" + + "\t\tif(lpix[i+3]>0){ pix[i] = lfpix[i]; pix[i+1] = lfpix[i+1]; pix[i+2] = lfpix[i+2]; pix[i+3] = lfpix[i+3];}\r\n" + + "\t}\r\n" + + "\tctx.putImageData(imgd, 0, 0);\r\n"; + lineFillData += "\tctx.restore();\r\n"; + strokeData = ""; + } else { + pathData += strokeData; + } + if (fillMatrix != null) { + if (lastRadColor != null) { + pathData += "\tctx.fillStyle=" + lastRadColor + ";\r\n\tctx.fill(\"evenodd\");\r\n"; + } + pathData += "\tctx.save();\r\n"; + pathData += "\tctx.clip();\r\n"; + pathData += "\tctx.transform(" + Helper.doubleStr(fillMatrix.scaleX) + "," + Helper.doubleStr(fillMatrix.rotateSkew0) + "," + Helper.doubleStr(fillMatrix.rotateSkew1) + "," + Helper.doubleStr(fillMatrix.scaleY) + "," + Helper.doubleStr(fillMatrix.translateX) + "," + Helper.doubleStr(fillMatrix.translateY) + ");\r\n"; + if (fillWidth > 0) {//repeating bitmap glitch fix + //make bitmap 1px wider + double s_w = (fillWidth + 1) / (double) fillWidth; + double s_h = (fillHeight + 1) / (double) fillHeight; + + pathData += "\tctx.transform(" + (s_w) + ",0,0," + s_h + ",-0.5,-0.5);\r\n"; + } + pathData += fillData; + pathData += "\tctx.fillRect(" + (-16384 - 32768 * repeatCnt) + "," + (-16384 - 32768 * repeatCnt) + "," + (2 * 16384 + 32768 * 2 * repeatCnt) + "," + (2 * 16384 + 32768 * 2 * repeatCnt) + ");\r\n"; + pathData += "\tctx.restore();\r\n"; + shapeData += pathData; + } else { + if (!"".equals(fillData)) { + pathData += "\tctx.fill(\"evenodd\");\r\n"; + } + shapeData += fillData + pathData; + } + if (!"".equals(strokeData)) { + shapeData += "\tctx.stroke();\r\n"; + } else if (lineFillData != null) { + shapeData += lineFillData; + } + } + + repeatCnt = 0; + + pathData = ""; + fillData = ""; + strokeData = ""; + fillMatrix = null; + lastRadColor = null; + + lineRepeatCnt = 0; + lineFillData = null; + lineLastRadColor = null; + lineFillMatrix = null; + + fillWidth = 0; + fillHeight = 0; + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java b/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java index 9447b16dd..0dfa9d507 100644 --- a/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java @@ -1,367 +1,364 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationCategory; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.SpringLayout; -import javax.swing.table.DefaultTableModel; - -/** - * - * @author JPEXS - */ -public class AdvancedSettingsDialog extends AppDialog implements ActionListener { - - private Map componentsMap = new HashMap<>(); - - /** - * Creates new form AdvancedSettingsDialog - */ - public AdvancedSettingsDialog() { - initComponents(); - View.centerScreen(this); - View.setWindowIcon(this); - - //configurationTable.setCellEditor(configurationTable.getDefaultEditor(null)); - pack(); - } - - private DefaultTableModel getModel() { - return new javax.swing.table.DefaultTableModel( - new Object[][]{}, - new String[]{ - translate("advancedSettings.columns.name"), - translate("advancedSettings.columns.value"), - translate("advancedSettings.columns.description") - } - ) { - Class[] types = new Class[]{ - String.class, Object.class, String.class - }; - boolean[] canEdit = new boolean[]{ - false, true, false - }; - - @Override - public Class getColumnClass(int columnIndex) { - return types[columnIndex]; - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit[columnIndex]; - } - }; - } - - private void initComponents() { - okButton = new JButton(); - cancelButton = new JButton(); - resetButton = new JButton(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setTitle(translate("advancedSettings.dialog.title")); - setModal(true); - setPreferredSize(new java.awt.Dimension(800, 500)); - - okButton.setText(AppStrings.translate("button.ok")); - okButton.addActionListener(this); - okButton.setActionCommand("OK"); - - cancelButton.setText(AppStrings.translate("button.cancel")); - cancelButton.addActionListener(this); - cancelButton.setActionCommand("CANCEL"); - - resetButton.setText(AppStrings.translate("button.reset")); - resetButton.addActionListener(this); - resetButton.setActionCommand("RESET"); - - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - //cnt.add(new JScrollPane(configurationTable),BorderLayout.CENTER); - - JPanel buttonsPanel = new JPanel(new BorderLayout()); - - JPanel buttonsLeftPanel = new JPanel(new FlowLayout()); - buttonsLeftPanel.add(resetButton, BorderLayout.WEST); - - buttonsPanel.add(buttonsLeftPanel, BorderLayout.WEST); - - JPanel buttonsRightPanel = new JPanel(new FlowLayout()); - buttonsRightPanel.add(cancelButton); - buttonsRightPanel.add(okButton); - buttonsPanel.add(buttonsRightPanel, BorderLayout.EAST); - - cnt.add(buttonsPanel, BorderLayout.SOUTH); - - Map fields = Configuration.getConfigurationFields(); - String[] keys = new String[fields.size()]; - keys = fields.keySet().toArray(keys); - Arrays.sort(keys); - - Map> categorized = new HashMap<>(); - - for (String name : keys) { - Field field = fields.get(name); - ConfigurationCategory cat = field.getAnnotation(ConfigurationCategory.class); - String scat = cat == null?"other":cat.value(); - if (!categorized.containsKey(scat)) { - categorized.put(scat, new HashMap()); - } - categorized.get(scat).put(name, field); - } - - JTabbedPane tabPane = new JTabbedPane(); - Map tabs=new HashMap<>(); - for (String cat : categorized.keySet()) { - JPanel configPanel = new JPanel(new SpringLayout()); - for (String name : categorized.get(cat).keySet()) { - Field field = categorized.get(cat).get(name); - - String locName = translate("config.name." + name); - - try { - - ConfigurationItem item = (ConfigurationItem) field.get(null); - - ParameterizedType listType = (ParameterizedType) field.getGenericType(); - Class itemType = (Class) listType.getActualTypeArguments()[0]; - /*String description = Configuration.getDescription(field); - if (description == null) { - description = ""; - }*/ - String description = translate("config.description." + name); - - Object defaultValue = Configuration.getDefaultValue(field); - if (defaultValue != null) { - description += " (" + translate("default") + ": " + defaultValue + ")"; - } - //model.addRow(new Object[]{locName, item.get(), description}); - - JLabel l = new JLabel(locName, JLabel.TRAILING); - l.setToolTipText(description); - configPanel.add(l); - Component c = null; - if ((itemType == String.class) || (itemType == Integer.class) || (itemType == Long.class) || (itemType == Double.class) || (itemType == Float.class) || (itemType == Calendar.class)) { - JTextField tf = new JTextField(); - Object val = item.get(); - if (val == null) { - val = ""; - } - if (itemType == Calendar.class) { - - tf.setText(new SimpleDateFormat().format(((Calendar) item.get()).getTime())); - } else { - tf.setText(val.toString()); - } - tf.setToolTipText(description); - tf.setMaximumSize(new Dimension(Integer.MAX_VALUE,tf.getPreferredSize().height)); - c = tf; - } - if (itemType == Boolean.class) { - JCheckBox cb = new JCheckBox(); - cb.setSelected((Boolean) item.get()); - cb.setToolTipText(description); - c = cb; - } - componentsMap.put(name, c); - l.setLabelFor(c); - configPanel.add(c); - - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - } - SpringUtilities.makeCompactGrid(configPanel, - categorized.get(cat).size(), 2, //rows, cols - 6, 6, //initX, initY - 6, 6); //xPad, yPad - tabs.put(cat,new JScrollPane(configPanel)); - } - - - String catOrder[] = new String[]{"ui","display","decompilation","script","format","export","limit","update","debug","other"}; - - for(String cat:catOrder){ - if(!tabs.containsKey(cat)){ - continue; - } - tabPane.add(translate("config.group.name."+cat),tabs.get(cat)); - tabPane.setToolTipTextAt(tabPane.getTabCount()-1, translate("config.group.description."+cat)); - } - - - - cnt.add(tabPane, BorderLayout.CENTER); - pack(); - } - - private void showRestartConfirmDialod() { - if (View.showConfirmDialog(this, translate("advancedSettings.restartConfirmation"), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - View.execInEventDispatchLater(new Runnable() { - - @Override - public void run() { - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); - } - SelectLanguageDialog.reloadUi(); - } - }); - - } - } - - private JButton cancelButton; - private JButton okButton; - private JButton resetButton; - //private EachRowRendererEditor configurationTable; - - @Override - @SuppressWarnings("unchecked") - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case "OK": - boolean modified = false; - Map fields = Configuration.getConfigurationFields(); - Map values = new HashMap<>(); - for (String name : fields.keySet()) { - Component c = componentsMap.get(name); - Object value = null; - - ParameterizedType listType = (ParameterizedType) fields.get(name).getGenericType(); - Class itemType = (Class) listType.getActualTypeArguments()[0]; - if (itemType == String.class) { - value = ((JTextField) c).getText(); - } - if (itemType == Boolean.class) { - value = ((JCheckBox) c).isSelected(); - } - - if (itemType == Calendar.class) { - Calendar cal = Calendar.getInstance(); - try { - cal.setTime(new SimpleDateFormat().parse(((JTextField) c).getText())); - } catch (ParseException ex) { - c.requestFocusInWindow(); - return; - } - value = cal; - } - - try { - if (itemType == Integer.class) { - value = Integer.parseInt(((JTextField) c).getText()); - } - if (itemType == Long.class) { - value = Long.parseLong(((JTextField) c).getText()); - } - if (itemType == Double.class) { - value = Double.parseDouble(((JTextField) c).getText()); - } - if (itemType == Float.class) { - value = Float.parseFloat(((JTextField) c).getText()); - } - } catch (NumberFormatException nfe) { - if (!((JTextField) c).getText().equals("")) { - c.requestFocusInWindow(); - return; - }//else null - } - values.put(name, value); - } - - for (String name : fields.keySet()) { - Component c = componentsMap.get(name); - Object value = values.get(name); - - Field field = fields.get(name); - ConfigurationItem item = null; - try { - item = (ConfigurationItem) field.get(null); - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - if (item.get() != null && !item.get().equals(value)) { - item.set(value); - modified = true; - } - } - Configuration.saveConfig(); - setVisible(false); - if (modified) { - showRestartConfirmDialod(); - } - break; - case "CANCEL": - setVisible(false); - break; - case "RESET": - - Map rfields = Configuration.getConfigurationFields(); - for (Entry entry : rfields.entrySet()) { - String name = entry.getKey(); - Field field = entry.getValue(); - try { - ConfigurationItem item = (ConfigurationItem) field.get(null); - item.unset(); - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - } - Configuration.saveConfig(); - setVisible(false); - showRestartConfirmDialod(); - break; - } - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationCategory; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SpringLayout; +import javax.swing.table.DefaultTableModel; + +/** + * + * @author JPEXS + */ +public class AdvancedSettingsDialog extends AppDialog implements ActionListener { + + private Map componentsMap = new HashMap<>(); + + /** + * Creates new form AdvancedSettingsDialog + */ + public AdvancedSettingsDialog() { + initComponents(); + View.centerScreen(this); + View.setWindowIcon(this); + + //configurationTable.setCellEditor(configurationTable.getDefaultEditor(null)); + pack(); + } + + private DefaultTableModel getModel() { + return new javax.swing.table.DefaultTableModel( + new Object[][]{}, + new String[]{ + translate("advancedSettings.columns.name"), + translate("advancedSettings.columns.value"), + translate("advancedSettings.columns.description") + } + ) { + Class[] types = new Class[]{ + String.class, Object.class, String.class + }; + boolean[] canEdit = new boolean[]{ + false, true, false + }; + + @Override + public Class getColumnClass(int columnIndex) { + return types[columnIndex]; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit[columnIndex]; + } + }; + } + + private void initComponents() { + okButton = new JButton(); + cancelButton = new JButton(); + resetButton = new JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(translate("advancedSettings.dialog.title")); + setModal(true); + setPreferredSize(new java.awt.Dimension(800, 500)); + + okButton.setText(AppStrings.translate("button.ok")); + okButton.addActionListener(this); + okButton.setActionCommand("OK"); + + cancelButton.setText(AppStrings.translate("button.cancel")); + cancelButton.addActionListener(this); + cancelButton.setActionCommand("CANCEL"); + + resetButton.setText(AppStrings.translate("button.reset")); + resetButton.addActionListener(this); + resetButton.setActionCommand("RESET"); + + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + //cnt.add(new JScrollPane(configurationTable),BorderLayout.CENTER); + + JPanel buttonsPanel = new JPanel(new BorderLayout()); + + JPanel buttonsLeftPanel = new JPanel(new FlowLayout()); + buttonsLeftPanel.add(resetButton, BorderLayout.WEST); + + buttonsPanel.add(buttonsLeftPanel, BorderLayout.WEST); + + JPanel buttonsRightPanel = new JPanel(new FlowLayout()); + buttonsRightPanel.add(cancelButton); + buttonsRightPanel.add(okButton); + buttonsPanel.add(buttonsRightPanel, BorderLayout.EAST); + + cnt.add(buttonsPanel, BorderLayout.SOUTH); + + Map fields = Configuration.getConfigurationFields(); + String[] keys = new String[fields.size()]; + keys = fields.keySet().toArray(keys); + Arrays.sort(keys); + + Map> categorized = new HashMap<>(); + + for (String name : keys) { + Field field = fields.get(name); + ConfigurationCategory cat = field.getAnnotation(ConfigurationCategory.class); + String scat = cat == null ? "other" : cat.value(); + if (!categorized.containsKey(scat)) { + categorized.put(scat, new HashMap()); + } + categorized.get(scat).put(name, field); + } + + JTabbedPane tabPane = new JTabbedPane(); + Map tabs = new HashMap<>(); + for (String cat : categorized.keySet()) { + JPanel configPanel = new JPanel(new SpringLayout()); + for (String name : categorized.get(cat).keySet()) { + Field field = categorized.get(cat).get(name); + + String locName = translate("config.name." + name); + + try { + + ConfigurationItem item = (ConfigurationItem) field.get(null); + + ParameterizedType listType = (ParameterizedType) field.getGenericType(); + Class itemType = (Class) listType.getActualTypeArguments()[0]; + /*String description = Configuration.getDescription(field); + if (description == null) { + description = ""; + }*/ + String description = translate("config.description." + name); + + Object defaultValue = Configuration.getDefaultValue(field); + if (defaultValue != null) { + description += " (" + translate("default") + ": " + defaultValue + ")"; + } + //model.addRow(new Object[]{locName, item.get(), description}); + + JLabel l = new JLabel(locName, JLabel.TRAILING); + l.setToolTipText(description); + configPanel.add(l); + Component c = null; + if ((itemType == String.class) || (itemType == Integer.class) || (itemType == Long.class) || (itemType == Double.class) || (itemType == Float.class) || (itemType == Calendar.class)) { + JTextField tf = new JTextField(); + Object val = item.get(); + if (val == null) { + val = ""; + } + if (itemType == Calendar.class) { + + tf.setText(new SimpleDateFormat().format(((Calendar) item.get()).getTime())); + } else { + tf.setText(val.toString()); + } + tf.setToolTipText(description); + tf.setMaximumSize(new Dimension(Integer.MAX_VALUE, tf.getPreferredSize().height)); + c = tf; + } + if (itemType == Boolean.class) { + JCheckBox cb = new JCheckBox(); + cb.setSelected((Boolean) item.get()); + cb.setToolTipText(description); + c = cb; + } + componentsMap.put(name, c); + l.setLabelFor(c); + configPanel.add(c); + + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + } + SpringUtilities.makeCompactGrid(configPanel, + categorized.get(cat).size(), 2, //rows, cols + 6, 6, //initX, initY + 6, 6); //xPad, yPad + tabs.put(cat, new JScrollPane(configPanel)); + } + + String catOrder[] = new String[]{"ui", "display", "decompilation", "script", "format", "export", "limit", "update", "debug", "other"}; + + for (String cat : catOrder) { + if (!tabs.containsKey(cat)) { + continue; + } + tabPane.add(translate("config.group.name." + cat), tabs.get(cat)); + tabPane.setToolTipTextAt(tabPane.getTabCount() - 1, translate("config.group.description." + cat)); + } + + cnt.add(tabPane, BorderLayout.CENTER); + pack(); + } + + private void showRestartConfirmDialod() { + if (View.showConfirmDialog(this, translate("advancedSettings.restartConfirmation"), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + View.execInEventDispatchLater(new Runnable() { + + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + SelectLanguageDialog.reloadUi(); + } + }); + + } + } + + private JButton cancelButton; + private JButton okButton; + private JButton resetButton; + //private EachRowRendererEditor configurationTable; + + @Override + @SuppressWarnings("unchecked") + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case "OK": + boolean modified = false; + Map fields = Configuration.getConfigurationFields(); + Map values = new HashMap<>(); + for (String name : fields.keySet()) { + Component c = componentsMap.get(name); + Object value = null; + + ParameterizedType listType = (ParameterizedType) fields.get(name).getGenericType(); + Class itemType = (Class) listType.getActualTypeArguments()[0]; + if (itemType == String.class) { + value = ((JTextField) c).getText(); + } + if (itemType == Boolean.class) { + value = ((JCheckBox) c).isSelected(); + } + + if (itemType == Calendar.class) { + Calendar cal = Calendar.getInstance(); + try { + cal.setTime(new SimpleDateFormat().parse(((JTextField) c).getText())); + } catch (ParseException ex) { + c.requestFocusInWindow(); + return; + } + value = cal; + } + + try { + if (itemType == Integer.class) { + value = Integer.parseInt(((JTextField) c).getText()); + } + if (itemType == Long.class) { + value = Long.parseLong(((JTextField) c).getText()); + } + if (itemType == Double.class) { + value = Double.parseDouble(((JTextField) c).getText()); + } + if (itemType == Float.class) { + value = Float.parseFloat(((JTextField) c).getText()); + } + } catch (NumberFormatException nfe) { + if (!((JTextField) c).getText().isEmpty()) { + c.requestFocusInWindow(); + return; + }//else null + } + values.put(name, value); + } + + for (String name : fields.keySet()) { + Component c = componentsMap.get(name); + Object value = values.get(name); + + Field field = fields.get(name); + ConfigurationItem item = null; + try { + item = (ConfigurationItem) field.get(null); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + if (item.get() != null && !item.get().equals(value)) { + item.set(value); + modified = true; + } + } + Configuration.saveConfig(); + setVisible(false); + if (modified) { + showRestartConfirmDialod(); + } + break; + case "CANCEL": + setVisible(false); + break; + case "RESET": + + Map rfields = Configuration.getConfigurationFields(); + for (Entry entry : rfields.entrySet()) { + String name = entry.getKey(); + Field field = entry.getValue(); + try { + ConfigurationItem item = (ConfigurationItem) field.get(null); + item.unset(); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + } + Configuration.saveConfig(); + setVisible(false); + showRestartConfirmDialod(); + break; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index ceed3e0cd..06baf0f70 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -1,679 +1,679 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.gui.player.MediaDisplay; -import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.SerializableImage; -import java.awt.AlphaComposite; -import java.awt.BasicStroke; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.FlowLayout; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionAdapter; -import java.awt.event.MouseMotionListener; -import java.awt.geom.AffineTransform; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.UnsupportedAudioFileException; -import javax.swing.JButton; -import javax.swing.JColorChooser; -import javax.swing.JLabel; -import javax.swing.JPanel; - -public final class ImagePanel extends JPanel implements ActionListener, MediaDisplay { - - static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; - - //private JLabel label = new JLabel(); - private Timelined timelined; - private boolean stillFrame = false; - private Timer timer; - private int frame = -1; - private SWF swf; - private boolean loaded; - private int mouseButton; - private JLabel debugLabel = new JLabel("-"); - private DepthState stateUnderCursor = null; - private MouseEvent lastMouseEvent = null; - private final List soundPlayers = new ArrayList<>(); - private final IconPanel iconPanel; - private int time = 0; - - private class IconPanel extends JPanel { - - private SerializableImage img; - - private Rectangle rect = null; - private List dss; - private List outlines; - - public synchronized void setOutlines(List dss, List outlines) { - this.outlines = outlines; - this.dss = dss; - } - - public void setImg(SerializableImage img) { - this.img = img; - calcRect(); - repaint(); - } - - public synchronized List getObjectsUnderPoint(Point p) { - List ret = new ArrayList<>(); - for (int i = 0; i < outlines.size(); i++) { - if (outlines.get(i).contains(p)) { - ret.add(dss.get(i)); - } - } - return ret; - } - - public Rectangle getRect() { - return rect; - } - - public Point toImagePoint(Point p) { - if (img == null) { - return null; - } - return new Point((p.x - rect.x) * img.getWidth() / rect.width, (p.y - rect.y) * img.getHeight() / rect.height); - } - - private void calcRect() { - if (img != null) { - int w1 = img.getWidth(); - int h1 = img.getHeight(); - - int w2 = getWidth(); - int h2 = getHeight(); - - int w; - int h; - if (w1 <= w2 && h1 <= h2) { - w = w1; - h = h1; - } else { - - h = h1 * w2 / w1; - if (h > h2) { - w = w1 * h2 / h1; - h = h2; - } else { - w = w2; - } - } - - rect = new Rectangle(getWidth() / 2 - w / 2, getHeight() / 2 - h / 2, w, h); - } else { - rect = null; - } - } - - @Override - protected void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setPaint(View.transparentPaint); - g2d.fill(new Rectangle(0, 0, getWidth(), getHeight())); - g2d.setComposite(AlphaComposite.SrcOver); - g2d.setPaint(View.swfBackgroundColor); - g2d.fill(new Rectangle(0, 0, getWidth(), getHeight())); - if (img != null) { - calcRect(); - g2d.setComposite(AlphaComposite.SrcOver); - g2d.drawImage(img.getBufferedImage(), rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, 0, 0, img.getWidth(), img.getHeight(), null); - } - - } - } - - @Override - public void setBackground(Color bg) { - if (iconPanel != null) { - iconPanel.setBackground(bg); - } - super.setBackground(bg); - } - - @Override - public synchronized void addMouseListener(MouseListener l) { - iconPanel.addMouseListener(l); - } - - @Override - public synchronized void removeMouseListener(MouseListener l) { - iconPanel.removeMouseListener(l); - } - - @Override - public synchronized void addMouseMotionListener(MouseMotionListener l) { - iconPanel.addMouseMotionListener(l); - } - - @Override - public synchronized void removeMouseMotionListener(MouseMotionListener l) { - iconPanel.removeMouseMotionListener(l); - } - - private void updatePos(MouseEvent e, boolean draw) { - if (e == null) { - return; - } - synchronized (iconPanel) { - lastMouseEvent = e; - boolean handCursor = false; - DepthState newStateUnderCursor = null; - if (timelined != null) { - - Timeline tim = ((Timelined) timelined).getTimeline(); - BoundedTag bounded = (BoundedTag) timelined; - RECT rect = bounded.getRect(); - int width = rect.getWidth(); - double scale = 1.0; - /*if (width > swf.displayRect.getWidth()) { - scale = (double) swf.displayRect.getWidth() / (double) width; - }*/ - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - m.scale(scale); - Point p = e.getPoint(); - p = iconPanel.toImagePoint(p); - List objs = new ArrayList<>(); - String ret = ""; - if (p != null) { - int x = p.x; - int y = p.y; - objs = iconPanel.getObjectsUnderPoint(p); - - ret += " [" + x + "," + y + "] : "; - } - - boolean first = true; - for (int i = 0; i < objs.size(); i++) { - DepthState ds = objs.get(i); - if (!first) { - ret += ", "; - } - first = false; - CharacterTag c = tim.swf.characters.get(ds.characterId); - if (c instanceof ButtonTag) { - newStateUnderCursor = ds; - handCursor = true; - } - ret += c.toString(); - if (timelined instanceof ButtonTag) { - handCursor = true; - } - } - if (first) { - ret += " - "; - } - debugLabel.setText(ret); - - if (handCursor) { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - if (newStateUnderCursor != stateUnderCursor) { - stateUnderCursor = newStateUnderCursor; - if (draw) { - drawFrame(); - } - } - } - } - } - - public ImagePanel() { - super(new BorderLayout()); - //iconPanel.setHorizontalAlignment(JLabel.CENTER); - setOpaque(true); - setBackground(View.DEFAULT_BACKGROUND_COLOR); - - iconPanel = new IconPanel(); - //labelPan.add(label, new GridBagConstraints()); - add(iconPanel, BorderLayout.CENTER); - - JPanel bottomPanel = new JPanel(new BorderLayout()); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton selectColorButton = new JButton(View.getIcon("color16")); - selectColorButton.addActionListener(this); - selectColorButton.setActionCommand(ACTION_SELECT_BKCOLOR); - selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); - buttonsPanel.add(selectColorButton); - bottomPanel.add(buttonsPanel, BorderLayout.EAST); - add(bottomPanel, BorderLayout.SOUTH); - add(debugLabel, BorderLayout.NORTH); - iconPanel.addMouseListener(new MouseAdapter() { - - @Override - public void mouseEntered(MouseEvent e) { - drawFrame(); - } - - @Override - public void mouseExited(MouseEvent e) { - stateUnderCursor = null; - drawFrame(); - debugLabel.setText(" - "); - } - - @Override - public void mousePressed(MouseEvent e) { - mouseButton = e.getButton(); - updatePos(e, true); - drawFrame(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar2)); - } - } - } - - @Override - public void mouseReleased(MouseEvent e) { - mouseButton = 0; - updatePos(e, true); - drawFrame(); - if (stateUnderCursor != null) { - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar3)); - } - } - } - - }); - iconPanel.addMouseMotionListener(new MouseMotionAdapter() { - @Override - public void mouseMoved(MouseEvent e) { - DepthState lastUnderCur = stateUnderCursor; - updatePos(e, true); - if (stateUnderCursor != null) { - if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { - //New mouse entered - ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar1)); - } - } - } - if (lastUnderCur != null) { - if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { - //Old mouse leave - ButtonTag b = (ButtonTag) swf.characters.get(lastUnderCur.characterId); - DefineButtonSoundTag sounds = b.getSounds(); - if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle - playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar0)); - } - } - } - } - - @Override - public void mouseDragged(MouseEvent e) { - updatePos(e, true); - } - - }); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals(ACTION_SELECT_BKCOLOR)) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.swfBackgroundColor); - if (newColor != null) { - View.swfBackgroundColor = newColor; - setBackground(newColor); - repaint(); - } - } - }); - - } - } - - public void setImage(byte[] data) { - setBackground(View.swfBackgroundColor); - if (timer != null) { - timer.cancel(); - } - timelined = null; - loaded = true; - try { - iconPanel.setImg(new SerializableImage(ImageIO.read(new ByteArrayInputStream(data)))); - iconPanel.setOutlines(new ArrayList(), new ArrayList()); - } catch (IOException ex) { - Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public synchronized void setTimelined(final Timelined drawable, final SWF swf, int frame) { - pause(); - if (drawable instanceof ButtonTag) { - frame = ButtonTag.FRAME_UP; - } - this.timelined = drawable; - this.swf = swf; - if (frame > -1) { - this.frame = frame; - this.stillFrame = true; - } else { - this.frame = 0; - this.stillFrame = false; - } - loaded = true; - - if (drawable.getTimeline().frames.isEmpty()) { - iconPanel.setImg(null); - iconPanel.setOutlines(new ArrayList(), new ArrayList()); - return; - } - time = 0; - play(); - } - - public void setImage(SerializableImage image) { - setBackground(View.swfBackgroundColor); - if (timer != null) { - timer.cancel(); - } - timelined = null; - loaded = true; - stillFrame = true; - iconPanel.setImg(image); - iconPanel.setOutlines(new ArrayList(), new ArrayList()); - } - - @Override - public int getCurrentFrame() { - return frame; - } - - @Override - public int getTotalFrames() { - if (timelined == null) { - return 0; - } - if (stillFrame) { - return 0; - } - return timelined.getTimeline().frames.size(); - } - - @Override - public void pause() { - if (timer != null) { - timer.cancel(); - timer = null; - } - stopAllSounds(); - } - - private void stopAllSounds() { - for (int i = soundPlayers.size() - 1; i >= 0; i--) { - SoundTagPlayer pl = soundPlayers.get(i); - pl.pause(); - } - soundPlayers.clear(); - } - - private void nextFrame() { - drawFrame(); - int newframe = (frame + 1) % timelined.getTimeline().frames.size(); - if (stillFrame) { - newframe = frame; - } - if (newframe != frame) { - if (newframe == 0) { - stopAllSounds(); - } - frame = newframe; - time = 0; - } else { - time++; - } - } - - private static SerializableImage getFrame(SWF swf, int frame, int time, Timelined drawable, DepthState stateUnderCursor, int mouseButton) { - String key = "drawable_" + frame + "_" + drawable.hashCode() + "_" + mouseButton + "_" + (stateUnderCursor == null ? "out" : stateUnderCursor.hashCode()); - SerializableImage img = SWF.getFromCache(key); - if (img == null) { - if (drawable instanceof BoundedTag) { - BoundedTag bounded = (BoundedTag) drawable; - RECT rect = bounded.getRect(); - if (rect == null) { //??? Why? - rect = new RECT(0, 0, 1, 1); - } - int width = rect.getWidth(); - int height = rect.getHeight(); - SerializableImage image = new SerializableImage((int) (width / SWF.unitDivisor) + 1, - (int) (height / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); - image.fillTransparent(); - Matrix m = new Matrix(); - m.translate(-rect.Xmin, -rect.Ymin); - drawable.getTimeline().toImage(frame, time, frame, stateUnderCursor, mouseButton, image, m, new ColorTransform()); - - Graphics2D gg = (Graphics2D) image.getGraphics(); - gg.setStroke(new BasicStroke(3)); - gg.setPaint(Color.green); - gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - List dss = new ArrayList<>(); - List os = new ArrayList<>(); - /*drawable.getTimeline().getObjectsOutlines(frame, frame, stateUnderCursor, mouseButton, m, dss, os); - - //gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); - for(Shape s:os){ - gg.draw(SHAPERECORD.twipToPixelShape(s)); - }*/ - - img = image; - } - if (drawable.getTimeline().isSingleFrame()) { - SWF.putToCache(key, img); - } - } - return img; - } - - private synchronized void drawFrame() { - if (timelined == null) { - return; - } - Timeline timeline = timelined.getTimeline(); - if (frame>=timeline.frames.size()) { - return; - } - - getOutlines(); - Matrix mat = new Matrix(); - mat.translateX = swf.displayRect.Xmin; - mat.translateY = swf.displayRect.Ymin; - updatePos(lastMouseEvent, false); - SerializableImage img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton); - List sounds = new ArrayList<>(); - List soundClasses = new ArrayList<>(); - timeline.getSounds(frame, time, stateUnderCursor, mouseButton, sounds, soundClasses); - for (int cid : swf.characters.keySet()) { - CharacterTag c = swf.characters.get(cid); - for (String cls : soundClasses) { - if (cls.equals(c.getClassName())) { - sounds.add(cid); - } - } - } - for (int sndId : sounds) { - CharacterTag c = swf.characters.get(sndId); - if (c instanceof SoundTag) { - SoundTag st = (SoundTag) c; - playSound(st); - } - } - - iconPanel.setImg(img); - } - - private void playSound(SoundTag st) { - final SoundTagPlayer sp; - try { - sp = new SoundTagPlayer(st, 1); - - synchronized (ImagePanel.class) { - soundPlayers.add(sp); - } - sp.addListener(new PlayerListener() { - - @Override - public void playingFinished() { - synchronized (ImagePanel.class) { - soundPlayers.remove(sp); - } - } - }); - sp.play(); - } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { - Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, "Error during playing sound", ex); - } - } - - private void getOutlines() { - List objs = new ArrayList<>(); - List outlines = new ArrayList<>(); - Matrix m = new Matrix(); - RECT rect = timelined.getTimeline().displayRect; - m.translate(-rect.Xmin, -rect.Ymin); - m.scale(1); - - timelined.getTimeline().getObjectsOutlines(frame, time, frame, stateUnderCursor, mouseButton, m, objs, outlines); - for (int i = 0; i < outlines.size(); i++) { - outlines.set(i, SHAPERECORD.twipToPixelShape(outlines.get(i))); - } - iconPanel.setOutlines(objs, outlines); - } - - public void stop() { - if (timer != null) { - timer.cancel(); - timer = null; - } - } - - @Override - public void play() { - pause(); - if (timelined != null) { - timer = new Timer(); - timer.schedule(new TimerTask() { - boolean first = true; - - @Override - public void run() { - Timeline timeline = timelined.getTimeline(); - if (timeline.frames.size() <= 1 && timeline.isSingleFrame()) { - if (first) { - drawFrame(); - first = false; - } - } else { - nextFrame(); - } - } - }, 0, 1000 / timelined.getTimeline().frameRate); - } else { - drawFrame(); - } - } - - @Override - public void rewind() { - frame = 0; - drawFrame(); - } - - @Override - public boolean isPlaying() { - if (timelined == null) { - return false; - } - if (stillFrame) { - return false; - } - return (timelined.getTimeline().frames.size() <= 1) || (timer != null); - } - - @Override - public void gotoFrame(int frame) { - this.frame = frame; - drawFrame(); - } - - @Override - public int getFrameRate() { - if (timelined == null) { - return 1; - } - if (stillFrame) { - return 1; - } - return timelined.getTimeline().frameRate; - } - - @Override - public boolean isLoaded() { - return loaded; - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.gui.player.MediaDisplay; +import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.SerializableImage; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; +import java.awt.geom.AffineTransform; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public final class ImagePanel extends JPanel implements ActionListener, MediaDisplay { + + static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; + + //private JLabel label = new JLabel(); + private Timelined timelined; + private boolean stillFrame = false; + private Timer timer; + private int frame = -1; + private SWF swf; + private boolean loaded; + private int mouseButton; + private JLabel debugLabel = new JLabel("-"); + private DepthState stateUnderCursor = null; + private MouseEvent lastMouseEvent = null; + private final List soundPlayers = new ArrayList<>(); + private final IconPanel iconPanel; + private int time = 0; + + private class IconPanel extends JPanel { + + private SerializableImage img; + + private Rectangle rect = null; + private List dss; + private List outlines; + + public synchronized void setOutlines(List dss, List outlines) { + this.outlines = outlines; + this.dss = dss; + } + + public void setImg(SerializableImage img) { + this.img = img; + calcRect(); + repaint(); + } + + public synchronized List getObjectsUnderPoint(Point p) { + List ret = new ArrayList<>(); + for (int i = 0; i < outlines.size(); i++) { + if (outlines.get(i).contains(p)) { + ret.add(dss.get(i)); + } + } + return ret; + } + + public Rectangle getRect() { + return rect; + } + + public Point toImagePoint(Point p) { + if (img == null) { + return null; + } + return new Point((p.x - rect.x) * img.getWidth() / rect.width, (p.y - rect.y) * img.getHeight() / rect.height); + } + + private void calcRect() { + if (img != null) { + int w1 = img.getWidth(); + int h1 = img.getHeight(); + + int w2 = getWidth(); + int h2 = getHeight(); + + int w; + int h; + if (w1 <= w2 && h1 <= h2) { + w = w1; + h = h1; + } else { + + h = h1 * w2 / w1; + if (h > h2) { + w = w1 * h2 / h1; + h = h2; + } else { + w = w2; + } + } + + rect = new Rectangle(getWidth() / 2 - w / 2, getHeight() / 2 - h / 2, w, h); + } else { + rect = null; + } + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setPaint(View.transparentPaint); + g2d.fill(new Rectangle(0, 0, getWidth(), getHeight())); + g2d.setComposite(AlphaComposite.SrcOver); + g2d.setPaint(View.swfBackgroundColor); + g2d.fill(new Rectangle(0, 0, getWidth(), getHeight())); + if (img != null) { + calcRect(); + g2d.setComposite(AlphaComposite.SrcOver); + g2d.drawImage(img.getBufferedImage(), rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, 0, 0, img.getWidth(), img.getHeight(), null); + } + + } + } + + @Override + public void setBackground(Color bg) { + if (iconPanel != null) { + iconPanel.setBackground(bg); + } + super.setBackground(bg); + } + + @Override + public synchronized void addMouseListener(MouseListener l) { + iconPanel.addMouseListener(l); + } + + @Override + public synchronized void removeMouseListener(MouseListener l) { + iconPanel.removeMouseListener(l); + } + + @Override + public synchronized void addMouseMotionListener(MouseMotionListener l) { + iconPanel.addMouseMotionListener(l); + } + + @Override + public synchronized void removeMouseMotionListener(MouseMotionListener l) { + iconPanel.removeMouseMotionListener(l); + } + + private void updatePos(MouseEvent e, boolean draw) { + if (e == null) { + return; + } + synchronized (iconPanel) { + lastMouseEvent = e; + boolean handCursor = false; + DepthState newStateUnderCursor = null; + if (timelined != null) { + + Timeline tim = ((Timelined) timelined).getTimeline(); + BoundedTag bounded = (BoundedTag) timelined; + RECT rect = bounded.getRect(); + int width = rect.getWidth(); + double scale = 1.0; + /*if (width > swf.displayRect.getWidth()) { + scale = (double) swf.displayRect.getWidth() / (double) width; + }*/ + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + m.scale(scale); + Point p = e.getPoint(); + p = iconPanel.toImagePoint(p); + List objs = new ArrayList<>(); + String ret = ""; + if (p != null) { + int x = p.x; + int y = p.y; + objs = iconPanel.getObjectsUnderPoint(p); + + ret += " [" + x + "," + y + "] : "; + } + + boolean first = true; + for (int i = 0; i < objs.size(); i++) { + DepthState ds = objs.get(i); + if (!first) { + ret += ", "; + } + first = false; + CharacterTag c = tim.swf.characters.get(ds.characterId); + if (c instanceof ButtonTag) { + newStateUnderCursor = ds; + handCursor = true; + } + ret += c.toString(); + if (timelined instanceof ButtonTag) { + handCursor = true; + } + } + if (first) { + ret += " - "; + } + debugLabel.setText(ret); + + if (handCursor) { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + if (newStateUnderCursor != stateUnderCursor) { + stateUnderCursor = newStateUnderCursor; + if (draw) { + drawFrame(); + } + } + } + } + } + + public ImagePanel() { + super(new BorderLayout()); + //iconPanel.setHorizontalAlignment(JLabel.CENTER); + setOpaque(true); + setBackground(View.DEFAULT_BACKGROUND_COLOR); + + iconPanel = new IconPanel(); + //labelPan.add(label, new GridBagConstraints()); + add(iconPanel, BorderLayout.CENTER); + + JPanel bottomPanel = new JPanel(new BorderLayout()); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton selectColorButton = new JButton(View.getIcon("color16")); + selectColorButton.addActionListener(this); + selectColorButton.setActionCommand(ACTION_SELECT_BKCOLOR); + selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); + buttonsPanel.add(selectColorButton); + bottomPanel.add(buttonsPanel, BorderLayout.EAST); + add(bottomPanel, BorderLayout.SOUTH); + add(debugLabel, BorderLayout.NORTH); + iconPanel.addMouseListener(new MouseAdapter() { + + @Override + public void mouseEntered(MouseEvent e) { + drawFrame(); + } + + @Override + public void mouseExited(MouseEvent e) { + stateUnderCursor = null; + drawFrame(); + debugLabel.setText(" - "); + } + + @Override + public void mousePressed(MouseEvent e) { + mouseButton = e.getButton(); + updatePos(e, true); + drawFrame(); + if (stateUnderCursor != null) { + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar2 != 0) { //OverUpToOverDown + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar2)); + } + } + } + + @Override + public void mouseReleased(MouseEvent e) { + mouseButton = 0; + updatePos(e, true); + drawFrame(); + if (stateUnderCursor != null) { + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar3 != 0) { //OverDownToOverUp + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar3)); + } + } + } + + }); + iconPanel.addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + DepthState lastUnderCur = stateUnderCursor; + updatePos(e, true); + if (stateUnderCursor != null) { + if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { + //New mouse entered + ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar1)); + } + } + } + if (lastUnderCur != null) { + if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { + //Old mouse leave + ButtonTag b = (ButtonTag) swf.characters.get(lastUnderCur.characterId); + DefineButtonSoundTag sounds = b.getSounds(); + if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle + playSound((SoundTag) swf.characters.get(sounds.buttonSoundChar0)); + } + } + } + } + + @Override + public void mouseDragged(MouseEvent e) { + updatePos(e, true); + } + + }); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals(ACTION_SELECT_BKCOLOR)) { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.swfBackgroundColor); + if (newColor != null) { + View.swfBackgroundColor = newColor; + setBackground(newColor); + repaint(); + } + } + }); + + } + } + + public void setImage(byte[] data) { + setBackground(View.swfBackgroundColor); + if (timer != null) { + timer.cancel(); + } + timelined = null; + loaded = true; + try { + iconPanel.setImg(new SerializableImage(ImageIO.read(new ByteArrayInputStream(data)))); + iconPanel.setOutlines(new ArrayList(), new ArrayList()); + } catch (IOException ex) { + Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public synchronized void setTimelined(final Timelined drawable, final SWF swf, int frame) { + pause(); + if (drawable instanceof ButtonTag) { + frame = ButtonTag.FRAME_UP; + } + this.timelined = drawable; + this.swf = swf; + if (frame > -1) { + this.frame = frame; + this.stillFrame = true; + } else { + this.frame = 0; + this.stillFrame = false; + } + loaded = true; + + if (drawable.getTimeline().frames.isEmpty()) { + iconPanel.setImg(null); + iconPanel.setOutlines(new ArrayList(), new ArrayList()); + return; + } + time = 0; + play(); + } + + public void setImage(SerializableImage image) { + setBackground(View.swfBackgroundColor); + if (timer != null) { + timer.cancel(); + } + timelined = null; + loaded = true; + stillFrame = true; + iconPanel.setImg(image); + iconPanel.setOutlines(new ArrayList(), new ArrayList()); + } + + @Override + public int getCurrentFrame() { + return frame; + } + + @Override + public int getTotalFrames() { + if (timelined == null) { + return 0; + } + if (stillFrame) { + return 0; + } + return timelined.getTimeline().frames.size(); + } + + @Override + public void pause() { + if (timer != null) { + timer.cancel(); + timer = null; + } + stopAllSounds(); + } + + private void stopAllSounds() { + for (int i = soundPlayers.size() - 1; i >= 0; i--) { + SoundTagPlayer pl = soundPlayers.get(i); + pl.pause(); + } + soundPlayers.clear(); + } + + private void nextFrame() { + drawFrame(); + int newframe = (frame + 1) % timelined.getTimeline().frames.size(); + if (stillFrame) { + newframe = frame; + } + if (newframe != frame) { + if (newframe == 0) { + stopAllSounds(); + } + frame = newframe; + time = 0; + } else { + time++; + } + } + + private static SerializableImage getFrame(SWF swf, int frame, int time, Timelined drawable, DepthState stateUnderCursor, int mouseButton) { + String key = "drawable_" + frame + "_" + drawable.hashCode() + "_" + mouseButton + "_" + (stateUnderCursor == null ? "out" : stateUnderCursor.hashCode()); + SerializableImage img = SWF.getFromCache(key); + if (img == null) { + if (drawable instanceof BoundedTag) { + BoundedTag bounded = (BoundedTag) drawable; + RECT rect = bounded.getRect(); + if (rect == null) { //??? Why? + rect = new RECT(0, 0, 1, 1); + } + int width = rect.getWidth(); + int height = rect.getHeight(); + SerializableImage image = new SerializableImage((int) (width / SWF.unitDivisor) + 1, + (int) (height / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB); + image.fillTransparent(); + Matrix m = new Matrix(); + m.translate(-rect.Xmin, -rect.Ymin); + drawable.getTimeline().toImage(frame, time, frame, stateUnderCursor, mouseButton, image, m, new ColorTransform()); + + Graphics2D gg = (Graphics2D) image.getGraphics(); + gg.setStroke(new BasicStroke(3)); + gg.setPaint(Color.green); + gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); + List dss = new ArrayList<>(); + List os = new ArrayList<>(); + /*drawable.getTimeline().getObjectsOutlines(frame, frame, stateUnderCursor, mouseButton, m, dss, os); + + //gg.setTransform(AffineTransform.getTranslateInstance(0, 0)); + for(Shape s:os){ + gg.draw(SHAPERECORD.twipToPixelShape(s)); + }*/ + + img = image; + } + if (drawable.getTimeline().isSingleFrame()) { + SWF.putToCache(key, img); + } + } + return img; + } + + private synchronized void drawFrame() { + if (timelined == null) { + return; + } + Timeline timeline = timelined.getTimeline(); + if (frame >= timeline.frames.size()) { + return; + } + + getOutlines(); + Matrix mat = new Matrix(); + mat.translateX = swf.displayRect.Xmin; + mat.translateY = swf.displayRect.Ymin; + updatePos(lastMouseEvent, false); + SerializableImage img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton); + List sounds = new ArrayList<>(); + List soundClasses = new ArrayList<>(); + timeline.getSounds(frame, time, stateUnderCursor, mouseButton, sounds, soundClasses); + for (int cid : swf.characters.keySet()) { + CharacterTag c = swf.characters.get(cid); + for (String cls : soundClasses) { + if (cls.equals(c.getClassName())) { + sounds.add(cid); + } + } + } + for (int sndId : sounds) { + CharacterTag c = swf.characters.get(sndId); + if (c instanceof SoundTag) { + SoundTag st = (SoundTag) c; + playSound(st); + } + } + + iconPanel.setImg(img); + } + + private void playSound(SoundTag st) { + final SoundTagPlayer sp; + try { + sp = new SoundTagPlayer(st, 1); + + synchronized (ImagePanel.class) { + soundPlayers.add(sp); + } + sp.addListener(new PlayerListener() { + + @Override + public void playingFinished() { + synchronized (ImagePanel.class) { + soundPlayers.remove(sp); + } + } + }); + sp.play(); + } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { + Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, "Error during playing sound", ex); + } + } + + private void getOutlines() { + List objs = new ArrayList<>(); + List outlines = new ArrayList<>(); + Matrix m = new Matrix(); + RECT rect = timelined.getTimeline().displayRect; + m.translate(-rect.Xmin, -rect.Ymin); + m.scale(1); + + timelined.getTimeline().getObjectsOutlines(frame, time, frame, stateUnderCursor, mouseButton, m, objs, outlines); + for (int i = 0; i < outlines.size(); i++) { + outlines.set(i, SHAPERECORD.twipToPixelShape(outlines.get(i))); + } + iconPanel.setOutlines(objs, outlines); + } + + public void stop() { + if (timer != null) { + timer.cancel(); + timer = null; + } + } + + @Override + public void play() { + pause(); + if (timelined != null) { + timer = new Timer(); + timer.schedule(new TimerTask() { + boolean first = true; + + @Override + public void run() { + Timeline timeline = timelined.getTimeline(); + if (timeline.frames.size() <= 1 && timeline.isSingleFrame()) { + if (first) { + drawFrame(); + first = false; + } + } else { + nextFrame(); + } + } + }, 0, 1000 / timelined.getTimeline().frameRate); + } else { + drawFrame(); + } + } + + @Override + public void rewind() { + frame = 0; + drawFrame(); + } + + @Override + public boolean isPlaying() { + if (timelined == null) { + return false; + } + if (stillFrame) { + return false; + } + return (timelined.getTimeline().frames.size() <= 1) || (timer != null); + } + + @Override + public void gotoFrame(int frame) { + this.frame = frame; + drawFrame(); + } + + @Override + public int getFrameRate() { + if (timelined == null) { + return 1; + } + if (stillFrame) { + return 1; + } + return timelined.getTimeline().frameRate; + } + + @Override + public boolean isLoaded() { + return loaded; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/LoadingPanel.java b/src/com/jpexs/decompiler/flash/gui/LoadingPanel.java index 832a76e11..84a19f988 100644 --- a/src/com/jpexs/decompiler/flash/gui/LoadingPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/LoadingPanel.java @@ -1,146 +1,138 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.util.Timer; -import java.util.TimerTask; -import javax.swing.JPanel; - -/** - * Panel with loading animation - * - * @author JPEXS - */ -public class LoadingPanel extends JPanel { - - BufferedImage lastImage; - int lastSize = 0; - Color col; - double rotation = 0; - Timer drawTimer; - - public LoadingPanel(int width,int height){ - this.col = new Color(0,0,255); - setPreferredSize(new Dimension(width,height)); - } - - private synchronized void setRotation(double rotation) { - this.rotation = rotation; - } - - private synchronized double getRotation() { - return rotation; - } - - private synchronized int getLastSize() { - return lastSize; - } - - - - - - - private synchronized void redrawImage(int size){ - if(drawTimer!=null){ - drawTimer.cancel();; - drawTimer = null; - } - BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); - Graphics2D big = bi.createGraphics(); - big.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - big.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - big.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - int br = size/16; - if(br<2){ - br = 2; - } - int border = 2; - int r = size / 2 - br - border; - - int o = (int) Math.round(Math.PI * 2 * r); - double max = Math.PI * 2; - double skip = max / o; - - - big.setComposite(AlphaComposite.Src); - for (int i = 0; i < o; i++) { - int c = i * 256 / o; - double alfa = skip * i; - double x = border + br + r + Math.round(Math.sin(alfa) * r); - double y = border + br + r + Math.round(Math.cos(alfa) * r); - big.setColor(new Color(col.getRed(), col.getGreen(), col.getBlue(), c)); - big.fill(new Ellipse2D.Double(x - br, y - br, 2 * br, 2 * br)); - } - lastImage = bi; - lastSize = size; - drawTimer = new Timer(); - int timeSpin = 1000; - double delay = 0; - delay = timeSpin/o; - while(delay<10){ - o--; - delay = timeSpin/o; - } - final int segments = o; - int idelay = (int)Math.round(delay); - drawTimer.schedule(new TimerTask() { - - @Override - public void run() { - double rot2 = rotation - Math.PI*2/segments; - if(rot2<0){ - rot2+=Math.PI*2; - } - setRotation(rot2); - repaint(); - } - }, idelay,idelay); - } - - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - - Graphics2D g2 = (Graphics2D) g; - - int size = Math.min(getWidth(), getHeight()); - if (lastImage == null || getLastSize()!=size) { - redrawImage(size); - } - - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - AffineTransform t = AffineTransform.getRotateInstance(getRotation(), size/2, size/2); - g2.setTransform(t); - g2.drawImage(lastImage, 0, 0, this); - } - -} +/* + * Copyright (C) 2010-2014 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.gui; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.image.BufferedImage; +import java.util.Timer; +import java.util.TimerTask; +import javax.swing.JPanel; + +/** + * Panel with loading animation + * + * @author JPEXS + */ +public class LoadingPanel extends JPanel { + + BufferedImage lastImage; + int lastSize = 0; + Color col; + double rotation = 0; + Timer drawTimer; + + public LoadingPanel(int width, int height) { + this.col = new Color(0, 0, 255); + setPreferredSize(new Dimension(width, height)); + } + + private synchronized void setRotation(double rotation) { + this.rotation = rotation; + } + + private synchronized double getRotation() { + return rotation; + } + + private synchronized int getLastSize() { + return lastSize; + } + + private synchronized void redrawImage(int size) { + if (drawTimer != null) { + drawTimer.cancel();; + drawTimer = null; + } + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + Graphics2D big = bi.createGraphics(); + big.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + big.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + big.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + int br = size / 16; + if (br < 2) { + br = 2; + } + int border = 2; + int r = size / 2 - br - border; + + int o = (int) Math.round(Math.PI * 2 * r); + double max = Math.PI * 2; + double skip = max / o; + + big.setComposite(AlphaComposite.Src); + for (int i = 0; i < o; i++) { + int c = i * 256 / o; + double alfa = skip * i; + double x = border + br + r + Math.round(Math.sin(alfa) * r); + double y = border + br + r + Math.round(Math.cos(alfa) * r); + big.setColor(new Color(col.getRed(), col.getGreen(), col.getBlue(), c)); + big.fill(new Ellipse2D.Double(x - br, y - br, 2 * br, 2 * br)); + } + lastImage = bi; + lastSize = size; + drawTimer = new Timer(); + int timeSpin = 1000; + double delay = 0; + delay = timeSpin / o; + while (delay < 10) { + o--; + delay = timeSpin / o; + } + final int segments = o; + int idelay = (int) Math.round(delay); + drawTimer.schedule(new TimerTask() { + + @Override + public void run() { + double rot2 = rotation - Math.PI * 2 / segments; + if (rot2 < 0) { + rot2 += Math.PI * 2; + } + setRotation(rot2); + repaint(); + } + }, idelay, idelay); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + Graphics2D g2 = (Graphics2D) g; + + int size = Math.min(getWidth(), getHeight()); + if (lastImage == null || getLastSize() != size) { + redrawImage(size); + } + + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + AffineTransform t = AffineTransform.getRotateInstance(getRotation(), size / 2, size / 2); + g2.setTransform(t); + g2.drawImage(lastImage, 0, 0, this); + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java index 592361b33..602bf0a98 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameClassicMenu.java @@ -1,579 +1,578 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.console.ContextMenuTools; -import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.helpers.Cache; -import com.sun.jna.Platform; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; - -/** - * - * @author JPEXS - */ -public class MainFrameClassicMenu implements MainFrameMenu, ActionListener { - - static final String ACTION_RELOAD = "RELOAD"; - static final String ACTION_ADVANCED_SETTINGS = "ADVANCEDSETTINGS"; - static final String ACTION_LOAD_MEMORY = "LOADMEMORY"; - static final String ACTION_LOAD_CACHE = "LOADCACHE"; - static final String ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP = "GOTODOCUMENTCLASSONSTARTUP"; - static final String ACTION_AUTO_RENAME_IDENTIFIERS = "AUTORENAMEIDENTIFIERS"; - static final String ACTION_CACHE_ON_DISK = "CACHEONDISK"; - static final String ACTION_SET_LANGUAGE = "SETLANGUAGE"; - static final String ACTION_DISABLE_DECOMPILATION = "DISABLEDECOMPILATION"; - static final String ACTION_ASSOCIATE = "ASSOCIATE"; - static final String ACTION_GOTO_DOCUMENT_CLASS = "GOTODOCUMENTCLASS"; - static final String ACTION_PARALLEL_SPEED_UP = "PARALLELSPEEDUP"; - static final String ACTION_INTERNAL_VIEWER_SWITCH = "INTERNALVIEWERSWITCH"; - static final String ACTION_SEARCH_AS = "SEARCHAS"; - static final String ACTION_AUTO_DEOBFUSCATE = "AUTODEOBFUSCATE"; - static final String ACTION_EXIT = "EXIT"; - - static final String ACTION_RENAME_ONE_IDENTIFIER = "RENAMEONEIDENTIFIER"; - static final String ACTION_ABOUT = "ABOUT"; - static final String ACTION_SHOW_PROXY = "SHOWPROXY"; - static final String ACTION_SUB_LIMITER = "SUBLIMITER"; - static final String ACTION_SAVE = "SAVE"; - static final String ACTION_SAVE_AS = "SAVEAS"; - static final String ACTION_SAVE_AS_EXE = "SAVEASEXE"; - static final String ACTION_OPEN = "OPEN"; - static final String ACTION_EXPORT_FLA = "EXPORTFLA"; - public static final String ACTION_EXPORT_SEL = "EXPORTSEL"; - static final String ACTION_EXPORT = "EXPORT"; - static final String ACTION_CHECK_UPDATES = "CHECKUPDATES"; - static final String ACTION_HELP_US = "HELPUS"; - static final String ACTION_HOMEPAGE = "HOMEPAGE"; - static final String ACTION_RESTORE_CONTROL_FLOW = "RESTORECONTROLFLOW"; - static final String ACTION_RESTORE_CONTROL_FLOW_ALL = "RESTORECONTROLFLOWALL"; - static final String ACTION_RENAME_IDENTIFIERS = "RENAMEIDENTIFIERS"; - static final String ACTION_DEOBFUSCATE = "DEOBFUSCATE"; - static final String ACTION_DEOBFUSCATE_ALL = "DEOBFUSCATEALL"; - static final String ACTION_REMOVE_NON_SCRIPTS = "REMOVENONSCRIPTS"; - static final String ACTION_REFRESH_DECOMPILED = "REFRESHDECOMPILED"; - - private final MainFrameClassic mainFrame; - - private JCheckBoxMenuItem miAutoDeobfuscation; - private JCheckBoxMenuItem miInternalViewer; - private JCheckBoxMenuItem miParallelSpeedUp; - private JCheckBoxMenuItem miAssociate; - private JCheckBoxMenuItem miDecompile; - private JCheckBoxMenuItem miCacheDisk; - private JCheckBoxMenuItem miGotoMainClassOnStartup; - private JCheckBoxMenuItem miAutoRenameIdentifiers; - private JMenuItem saveCommandButton; - private JMenuItem saveasCommandButton; - private JMenuItem saveasexeCommandButton; - private JMenuItem exportAllCommandButton; - private JMenuItem exportFlaCommandButton; - private JMenuItem exportSelectionCommandButton; - - private JMenuItem reloadCommandButton; - private JMenuItem renameinvalidCommandButton; - private JMenuItem globalrenameCommandButton; - private JMenuItem deobfuscationCommandButton; - private JMenuItem searchCommandButton; - private JMenuItem gotoDocumentClassCommandButton; - - public MainFrameClassicMenu(MainFrameClassic mainFrame, boolean externalFlashPlayerUnavailable) { - this.mainFrame = mainFrame; - - createMenuBar(externalFlashPlayerUnavailable); - } - - @Override - public boolean isInternalFlashViewerSelected() { - return miInternalViewer.isSelected(); - } - - private String translate(String key) { - return mainFrame.translate(key); - } - - private void assignListener(JMenuItem b, final String command) { - final MainFrameClassicMenu t = this; - b.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - t.actionPerformed(new ActionEvent(e.getSource(), 0, command)); - } - }); - } - - private String fixCommandTitle(String title) { - if (title.length() > 2) { - if (title.charAt(1) == ' ') { - title = title.charAt(0) + "\u00A0" + title.substring(2); - } - } - return title; - } - - private void createMenuBar(boolean externalFlashPlayerUnavailable) { - JMenuBar menuBar = new JMenuBar(); - - JMenu menuFile = new JMenu(translate("menu.file")); - JMenuItem miOpen = new JMenuItem(translate("menu.file.open")); - miOpen.setIcon(View.getIcon("open16")); - miOpen.setActionCommand(ACTION_OPEN); - miOpen.addActionListener(this); - JMenuItem miSave = new JMenuItem(translate("menu.file.save")); - miSave.setIcon(View.getIcon("save16")); - miSave.setActionCommand(ACTION_SAVE); - miSave.addActionListener(this); - JMenuItem miSaveAs = new JMenuItem(translate("menu.file.saveas")); - miSaveAs.setIcon(View.getIcon("saveas16")); - miSaveAs.setActionCommand(ACTION_SAVE_AS); - miSaveAs.addActionListener(this); - JMenuItem miSaveAsExe = new JMenuItem(translate("menu.file.saveasexe")); - miSaveAsExe.setIcon(View.getIcon("saveas16")); - miSaveAsExe.setActionCommand(ACTION_SAVE_AS_EXE); - miSaveAsExe.addActionListener(this); - - JMenuItem menuExportFla = new JMenuItem(translate("menu.file.export.fla")); - menuExportFla.setActionCommand(ACTION_EXPORT_FLA); - menuExportFla.addActionListener(this); - menuExportFla.setIcon(View.getIcon("flash16")); - - JMenuItem menuExportAll = new JMenuItem(translate("menu.file.export.all")); - menuExportAll.setActionCommand(ACTION_EXPORT); - menuExportAll.addActionListener(this); - JMenuItem menuExportSel = new JMenuItem(translate("menu.file.export.selection")); - menuExportSel.setActionCommand(ACTION_EXPORT_SEL); - menuExportSel.addActionListener(this); - menuExportAll.setIcon(View.getIcon("export16")); - menuExportSel.setIcon(View.getIcon("exportsel16")); - - menuFile.add(miOpen); - menuFile.add(miSave); - menuFile.add(miSaveAs); - menuFile.add(miSaveAsExe); - menuFile.add(menuExportFla); - menuFile.add(menuExportAll); - menuFile.add(menuExportSel); - menuFile.addSeparator(); - JMenuItem miClose = new JMenuItem(translate("menu.file.exit")); - miClose.setIcon(View.getIcon("exit16")); - miClose.setActionCommand(ACTION_EXIT); - miClose.addActionListener(this); - menuFile.add(miClose); - menuBar.add(menuFile); - JMenu menuDeobfuscation = new JMenu(translate("menu.tools.deobfuscation")); - menuDeobfuscation.setIcon(View.getIcon("deobfuscate16")); - - JMenuItem miDeobfuscation = new JMenuItem(translate("menu.tools.deobfuscation.pcode")); - miDeobfuscation.setActionCommand(ACTION_DEOBFUSCATE); - miDeobfuscation.addActionListener(this); - - miAutoDeobfuscation = new JCheckBoxMenuItem(translate("menu.settings.autodeobfuscation")); - miAutoDeobfuscation.setSelected(Configuration.autoDeobfuscate.get()); - miAutoDeobfuscation.addActionListener(this); - miAutoDeobfuscation.setActionCommand(ACTION_AUTO_DEOBFUSCATE); - - JMenuItem miRenameOneIdentifier = new JMenuItem(translate("menu.tools.deobfuscation.globalrename")); - miRenameOneIdentifier.setActionCommand(ACTION_RENAME_ONE_IDENTIFIER); - miRenameOneIdentifier.addActionListener(this); - - JMenuItem miRenameIdentifiers = new JMenuItem(translate("menu.tools.deobfuscation.renameinvalid")); - miRenameIdentifiers.setActionCommand(ACTION_RENAME_IDENTIFIERS); - miRenameIdentifiers.addActionListener(this); - - menuDeobfuscation.add(miRenameOneIdentifier); - menuDeobfuscation.add(miRenameIdentifiers); - menuDeobfuscation.add(miDeobfuscation); - JMenu menuTools = new JMenu(translate("menu.tools")); - JMenuItem miProxy = new JMenuItem(translate("menu.tools.proxy")); - miProxy.setActionCommand(ACTION_SHOW_PROXY); - miProxy.setIcon(View.getIcon("proxy16")); - miProxy.addActionListener(this); - - JMenuItem miSearchScript = new JMenuItem(translate("menu.tools.searchas")); - miSearchScript.addActionListener(this); - miSearchScript.setActionCommand(ACTION_SEARCH_AS); - miSearchScript.setIcon(View.getIcon("search16")); - - menuTools.add(miSearchScript); - - miInternalViewer = new JCheckBoxMenuItem(translate("menu.settings.internalflashviewer")); - miInternalViewer.setSelected(Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); - if (externalFlashPlayerUnavailable) { - miInternalViewer.setEnabled(false); - } - miInternalViewer.setActionCommand(ACTION_INTERNAL_VIEWER_SWITCH); - miInternalViewer.addActionListener(this); - - miParallelSpeedUp = new JCheckBoxMenuItem(translate("menu.settings.parallelspeedup")); - miParallelSpeedUp.setSelected(Configuration.parallelSpeedUp.get()); - miParallelSpeedUp.setActionCommand(ACTION_PARALLEL_SPEED_UP); - miParallelSpeedUp.addActionListener(this); - - menuTools.add(miProxy); - - menuTools.add(menuDeobfuscation); - - JMenuItem miGotoDocumentClass = new JMenuItem(translate("menu.tools.gotodocumentclass")); - miGotoDocumentClass.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS); - miGotoDocumentClass.addActionListener(this); - menuBar.add(menuTools); - - miDecompile = new JCheckBoxMenuItem(translate("menu.settings.disabledecompilation")); - miDecompile.setSelected(!Configuration.decompile.get()); - miDecompile.setActionCommand(ACTION_DISABLE_DECOMPILATION); - miDecompile.addActionListener(this); - - miCacheDisk = new JCheckBoxMenuItem(translate("menu.settings.cacheOnDisk")); - miCacheDisk.setSelected(Configuration.cacheOnDisk.get()); - miCacheDisk.setActionCommand(ACTION_CACHE_ON_DISK); - miCacheDisk.addActionListener(this); - - miGotoMainClassOnStartup = new JCheckBoxMenuItem(translate("menu.settings.gotoMainClassOnStartup")); - miGotoMainClassOnStartup.setSelected(Configuration.gotoMainClassOnStartup.get()); - miGotoMainClassOnStartup.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP); - miGotoMainClassOnStartup.addActionListener(this); - - miAutoRenameIdentifiers = new JCheckBoxMenuItem(translate("menu.settings.autoRenameIdentifiers")); - miAutoRenameIdentifiers.setSelected(Configuration.autoRenameIdentifiers.get()); - miAutoRenameIdentifiers.setActionCommand(ACTION_AUTO_RENAME_IDENTIFIERS); - miAutoRenameIdentifiers.addActionListener(this); - - JMenu menuSettings = new JMenu(translate("menu.settings")); - menuSettings.add(miAutoDeobfuscation); - menuSettings.add(miInternalViewer); - menuSettings.add(miParallelSpeedUp); - menuSettings.add(miDecompile); - menuSettings.add(miCacheDisk); - menuSettings.add(miGotoMainClassOnStartup); - menuSettings.add(miAutoRenameIdentifiers); - - miAssociate = new JCheckBoxMenuItem(translate("menu.settings.addtocontextmenu")); - miAssociate.setActionCommand(ACTION_ASSOCIATE); - miAssociate.addActionListener(this); - miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); - - JMenuItem miLanguage = new JMenuItem(translate("menu.settings.language")); - miLanguage.setActionCommand(ACTION_SET_LANGUAGE); - miLanguage.addActionListener(this); - - if (Platform.isWindows()) { - menuSettings.add(miAssociate); - } - menuSettings.add(miLanguage); - - JMenuItem advancedSettingsCommandButton = new JMenuItem(translate("menu.advancedsettings.advancedsettings")); - advancedSettingsCommandButton.setActionCommand(ACTION_ADVANCED_SETTINGS); - advancedSettingsCommandButton.setIcon(View.getIcon("settings16")); - advancedSettingsCommandButton.addActionListener(this); - menuSettings.add(advancedSettingsCommandButton); - - menuBar.add(menuSettings); - JMenu menuHelp = new JMenu(translate("menu.help")); - JMenuItem miAbout = new JMenuItem(translate("menu.help.about")); - miAbout.setIcon(View.getIcon("about16")); - - miAbout.setActionCommand(ACTION_ABOUT); - miAbout.addActionListener(this); - - JMenuItem miCheckUpdates = new JMenuItem(translate("menu.help.checkupdates")); - miCheckUpdates.setActionCommand(ACTION_CHECK_UPDATES); - miCheckUpdates.setIcon(View.getIcon("update16")); - miCheckUpdates.addActionListener(this); - - JMenuItem miHelpUs = new JMenuItem(translate("menu.help.helpus")); - miHelpUs.setActionCommand(ACTION_HELP_US); - miHelpUs.setIcon(View.getIcon("donate16")); - miHelpUs.addActionListener(this); - - JMenuItem miHomepage = new JMenuItem(translate("menu.help.homepage")); - miHomepage.setActionCommand(ACTION_HOMEPAGE); - miHomepage.setIcon(View.getIcon("homepage16")); - miHomepage.addActionListener(this); - - menuHelp.add(miCheckUpdates); - menuHelp.add(miHelpUs); - menuHelp.add(miHomepage); - menuHelp.add(miAbout); - menuBar.add(menuHelp); - - mainFrame.setJMenuBar(menuBar); - - //if (hasAbc) { - menuTools.add(miGotoDocumentClass); - //} - } - - @Override - public void updateComponents(SWF swf, List abcList) { - boolean swfLoaded = swf != null; - boolean hasAbc = swfLoaded && abcList != null && !abcList.isEmpty(); - - /*saveCommandButton.setEnabled(swfLoaded); - saveasCommandButton.setEnabled(swfLoaded); - saveasexeCommandButton.setEnabled(swfLoaded); - exportAllCommandButton.setEnabled(swfLoaded); - exportFlaCommandButton.setEnabled(swfLoaded); - exportSelectionCommandButton.setEnabled(swfLoaded); - reloadCommandButton.setEnabled(swfLoaded); - - renameinvalidCommandButton.setEnabled(swfLoaded); - globalrenameCommandButton.setEnabled(swfLoaded); - deobfuscationCommandButton.setEnabled(swfLoaded); - searchCommandButton.setEnabled(swfLoaded); - - gotoDocumentClassCommandButton.setEnabled(hasAbc); - deobfuscationCommandButton.setEnabled(hasAbc);*/ - } - - private void saveAs(SWF swf, SaveFileMode mode) { - if (Main.saveFileDialog(swf, mode)) { - swf.fileTitle = null; - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - saveCommandButton.setEnabled(mainFrame.panel.getCurrentSwf() != null); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_RELOAD: - if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - Main.reloadApp(); - } - break; - case ACTION_ADVANCED_SETTINGS: - Main.advancedSettings(); - break; - case ACTION_LOAD_MEMORY: - Main.loadFromMemory(); - break; - case ACTION_LOAD_CACHE: - Main.loadFromCache(); - break; - case ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP: - Configuration.gotoMainClassOnStartup.set(miGotoMainClassOnStartup.isSelected()); - break; - case ACTION_AUTO_RENAME_IDENTIFIERS: - Configuration.autoRenameIdentifiers.set(miAutoRenameIdentifiers.isSelected()); - break; - case ACTION_CACHE_ON_DISK: - Configuration.cacheOnDisk.set(miCacheDisk.isSelected()); - if (miCacheDisk.isSelected()) { - Cache.setStorageType(Cache.STORAGE_FILES); - } else { - Cache.setStorageType(Cache.STORAGE_MEMORY); - } - break; - case ACTION_SET_LANGUAGE: - new SelectLanguageDialog().display(); - break; - case ACTION_DISABLE_DECOMPILATION: - Configuration.decompile.set(!miDecompile.isSelected()); - mainFrame.panel.disableDecompilationChanged(); - break; - case ACTION_ASSOCIATE: - if (miAssociate.isSelected() == ContextMenuTools.isAddedToContextMenu()) { - return; - } - ContextMenuTools.addToContextMenu(miAssociate.isSelected(), false); - - //Update checkbox menuitem accordingly (User can cancel rights elevation) - new Timer().schedule(new TimerTask() { - @Override - public void run() { - miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); - } - }, 1000); //It takes some time registry change to apply - break; - case ACTION_GOTO_DOCUMENT_CLASS: - mainFrame.panel.gotoDocumentClass(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_PARALLEL_SPEED_UP: - String confStr = translate("message.confirm.parallel") + "\r\n"; - if (miParallelSpeedUp.isSelected()) { - confStr += " " + translate("message.confirm.on"); - } else { - confStr += " " + translate("message.confirm.off"); - } - if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.parallelSpeedUp.set((Boolean) miParallelSpeedUp.isSelected()); - } else { - miParallelSpeedUp.setSelected(!miParallelSpeedUp.isSelected()); - } - break; - case ACTION_INTERNAL_VIEWER_SWITCH: - Configuration.internalFlashViewer.set(miInternalViewer.isSelected()); - mainFrame.panel.reload(true); - break; - case ACTION_SEARCH_AS: - mainFrame.panel.searchAs(); - break; - case ACTION_AUTO_DEOBFUSCATE: - if (View.showConfirmDialog(mainFrame.panel, translate("message.confirm.autodeobfuscate") + "\r\n" + (miAutoDeobfuscation.isSelected() ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.autoDeobfuscate.set(miAutoDeobfuscation.isSelected()); - mainFrame.panel.autoDeobfuscateChanged(); - } else { - miAutoDeobfuscation.setSelected(!miAutoDeobfuscation.isSelected()); - } - break; - case ACTION_EXIT: - mainFrame.panel.setVisible(false); - if (Main.proxyFrame != null) { - if (Main.proxyFrame.isVisible()) { - return; - } - } - Main.exit(); - break; - } - - if (Main.isWorking()) { - return; - } - - switch (e.getActionCommand()) { - case ACTION_RENAME_ONE_IDENTIFIER: - mainFrame.panel.renameOneIdentifier(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_ABOUT: - Main.about(); - break; - case ACTION_SHOW_PROXY: - Main.showProxy(); - break; - case ACTION_SUB_LIMITER: - if (e.getSource() instanceof JCheckBoxMenuItem) { - Main.setSubLimiter(((JCheckBoxMenuItem) e.getSource()).getState()); - } - break; - case ACTION_SAVE: { - SWF swf = mainFrame.panel.getCurrentSwf(); - SWFNode snode = ((TagTreeModel)mainFrame.panel.tagTree.getModel()).getSwfNode(swf); - if(snode.binaryData!=null){ - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - snode.binaryData.binaryData = baos.toByteArray(); - snode.binaryData.setModified(true); - } catch (IOException ex) { - Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); - } - } - else if(swf.file == null) { - saveAs(swf, SaveFileMode.SAVEAS); - } else { - try { - Main.saveFile(swf, swf.file); - } catch (IOException ex) { - Logger.getLogger(MainFrameClassicMenu.class.getName()).log(Level.SEVERE, null, ex); - View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - } - break; - case ACTION_SAVE_AS: { - SWF swf = mainFrame.panel.getCurrentSwf(); - saveAs(swf, SaveFileMode.SAVEAS); - } - break; - case ACTION_SAVE_AS_EXE: { - SWF swf = mainFrame.panel.getCurrentSwf(); - saveAs(swf, SaveFileMode.EXE); - } - break; - case ACTION_OPEN: - Main.openFileDialog(); - break; - case ACTION_EXPORT_FLA: - mainFrame.panel.exportFla(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_EXPORT_SEL: - case ACTION_EXPORT: - boolean onlySel = e.getActionCommand().endsWith("SEL"); - mainFrame.panel.export(onlySel); - break; - case ACTION_CHECK_UPDATES: - if (!Main.checkForUpdates()) { - View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); - } - break; - case ACTION_HELP_US: - String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html"; - if (java.awt.Desktop.isDesktopSupported()) { - java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); - try { - java.net.URI uri = new java.net.URI(helpUsURL); - desktop.browse(uri); - } catch (URISyntaxException | IOException ex) { - } - } else { - View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); - } - break; - case ACTION_HOMEPAGE: - String homePageURL = ApplicationInfo.PROJECT_PAGE; - if (java.awt.Desktop.isDesktopSupported()) { - java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); - try { - java.net.URI uri = new java.net.URI(homePageURL); - desktop.browse(uri); - } catch (URISyntaxException | IOException ex) { - } - } else { - View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); - } - break; - case ACTION_RESTORE_CONTROL_FLOW: - case ACTION_RESTORE_CONTROL_FLOW_ALL: - boolean all = e.getActionCommand().endsWith("ALL"); - mainFrame.panel.restoreControlFlow(all); - break; - case ACTION_RENAME_IDENTIFIERS: - mainFrame.panel.renameIdentifiers(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_DEOBFUSCATE: - case ACTION_DEOBFUSCATE_ALL: - mainFrame.panel.deobfuscate(); - break; - case ACTION_REMOVE_NON_SCRIPTS: - mainFrame.panel.removeNonScripts(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_REFRESH_DECOMPILED: - mainFrame.panel.refreshDecompiled(); - break; - } - } - -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.console.ContextMenuTools; +import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.helpers.Cache; +import com.sun.jna.Platform; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; + +/** + * + * @author JPEXS + */ +public class MainFrameClassicMenu implements MainFrameMenu, ActionListener { + + static final String ACTION_RELOAD = "RELOAD"; + static final String ACTION_ADVANCED_SETTINGS = "ADVANCEDSETTINGS"; + static final String ACTION_LOAD_MEMORY = "LOADMEMORY"; + static final String ACTION_LOAD_CACHE = "LOADCACHE"; + static final String ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP = "GOTODOCUMENTCLASSONSTARTUP"; + static final String ACTION_AUTO_RENAME_IDENTIFIERS = "AUTORENAMEIDENTIFIERS"; + static final String ACTION_CACHE_ON_DISK = "CACHEONDISK"; + static final String ACTION_SET_LANGUAGE = "SETLANGUAGE"; + static final String ACTION_DISABLE_DECOMPILATION = "DISABLEDECOMPILATION"; + static final String ACTION_ASSOCIATE = "ASSOCIATE"; + static final String ACTION_GOTO_DOCUMENT_CLASS = "GOTODOCUMENTCLASS"; + static final String ACTION_PARALLEL_SPEED_UP = "PARALLELSPEEDUP"; + static final String ACTION_INTERNAL_VIEWER_SWITCH = "INTERNALVIEWERSWITCH"; + static final String ACTION_SEARCH_AS = "SEARCHAS"; + static final String ACTION_AUTO_DEOBFUSCATE = "AUTODEOBFUSCATE"; + static final String ACTION_EXIT = "EXIT"; + + static final String ACTION_RENAME_ONE_IDENTIFIER = "RENAMEONEIDENTIFIER"; + static final String ACTION_ABOUT = "ABOUT"; + static final String ACTION_SHOW_PROXY = "SHOWPROXY"; + static final String ACTION_SUB_LIMITER = "SUBLIMITER"; + static final String ACTION_SAVE = "SAVE"; + static final String ACTION_SAVE_AS = "SAVEAS"; + static final String ACTION_SAVE_AS_EXE = "SAVEASEXE"; + static final String ACTION_OPEN = "OPEN"; + static final String ACTION_EXPORT_FLA = "EXPORTFLA"; + public static final String ACTION_EXPORT_SEL = "EXPORTSEL"; + static final String ACTION_EXPORT = "EXPORT"; + static final String ACTION_CHECK_UPDATES = "CHECKUPDATES"; + static final String ACTION_HELP_US = "HELPUS"; + static final String ACTION_HOMEPAGE = "HOMEPAGE"; + static final String ACTION_RESTORE_CONTROL_FLOW = "RESTORECONTROLFLOW"; + static final String ACTION_RESTORE_CONTROL_FLOW_ALL = "RESTORECONTROLFLOWALL"; + static final String ACTION_RENAME_IDENTIFIERS = "RENAMEIDENTIFIERS"; + static final String ACTION_DEOBFUSCATE = "DEOBFUSCATE"; + static final String ACTION_DEOBFUSCATE_ALL = "DEOBFUSCATEALL"; + static final String ACTION_REMOVE_NON_SCRIPTS = "REMOVENONSCRIPTS"; + static final String ACTION_REFRESH_DECOMPILED = "REFRESHDECOMPILED"; + + private final MainFrameClassic mainFrame; + + private JCheckBoxMenuItem miAutoDeobfuscation; + private JCheckBoxMenuItem miInternalViewer; + private JCheckBoxMenuItem miParallelSpeedUp; + private JCheckBoxMenuItem miAssociate; + private JCheckBoxMenuItem miDecompile; + private JCheckBoxMenuItem miCacheDisk; + private JCheckBoxMenuItem miGotoMainClassOnStartup; + private JCheckBoxMenuItem miAutoRenameIdentifiers; + private JMenuItem saveCommandButton; + private JMenuItem saveasCommandButton; + private JMenuItem saveasexeCommandButton; + private JMenuItem exportAllCommandButton; + private JMenuItem exportFlaCommandButton; + private JMenuItem exportSelectionCommandButton; + + private JMenuItem reloadCommandButton; + private JMenuItem renameinvalidCommandButton; + private JMenuItem globalrenameCommandButton; + private JMenuItem deobfuscationCommandButton; + private JMenuItem searchCommandButton; + private JMenuItem gotoDocumentClassCommandButton; + + public MainFrameClassicMenu(MainFrameClassic mainFrame, boolean externalFlashPlayerUnavailable) { + this.mainFrame = mainFrame; + + createMenuBar(externalFlashPlayerUnavailable); + } + + @Override + public boolean isInternalFlashViewerSelected() { + return miInternalViewer.isSelected(); + } + + private String translate(String key) { + return mainFrame.translate(key); + } + + private void assignListener(JMenuItem b, final String command) { + final MainFrameClassicMenu t = this; + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + t.actionPerformed(new ActionEvent(e.getSource(), 0, command)); + } + }); + } + + private String fixCommandTitle(String title) { + if (title.length() > 2) { + if (title.charAt(1) == ' ') { + title = title.charAt(0) + "\u00A0" + title.substring(2); + } + } + return title; + } + + private void createMenuBar(boolean externalFlashPlayerUnavailable) { + JMenuBar menuBar = new JMenuBar(); + + JMenu menuFile = new JMenu(translate("menu.file")); + JMenuItem miOpen = new JMenuItem(translate("menu.file.open")); + miOpen.setIcon(View.getIcon("open16")); + miOpen.setActionCommand(ACTION_OPEN); + miOpen.addActionListener(this); + JMenuItem miSave = new JMenuItem(translate("menu.file.save")); + miSave.setIcon(View.getIcon("save16")); + miSave.setActionCommand(ACTION_SAVE); + miSave.addActionListener(this); + JMenuItem miSaveAs = new JMenuItem(translate("menu.file.saveas")); + miSaveAs.setIcon(View.getIcon("saveas16")); + miSaveAs.setActionCommand(ACTION_SAVE_AS); + miSaveAs.addActionListener(this); + JMenuItem miSaveAsExe = new JMenuItem(translate("menu.file.saveasexe")); + miSaveAsExe.setIcon(View.getIcon("saveas16")); + miSaveAsExe.setActionCommand(ACTION_SAVE_AS_EXE); + miSaveAsExe.addActionListener(this); + + JMenuItem menuExportFla = new JMenuItem(translate("menu.file.export.fla")); + menuExportFla.setActionCommand(ACTION_EXPORT_FLA); + menuExportFla.addActionListener(this); + menuExportFla.setIcon(View.getIcon("flash16")); + + JMenuItem menuExportAll = new JMenuItem(translate("menu.file.export.all")); + menuExportAll.setActionCommand(ACTION_EXPORT); + menuExportAll.addActionListener(this); + JMenuItem menuExportSel = new JMenuItem(translate("menu.file.export.selection")); + menuExportSel.setActionCommand(ACTION_EXPORT_SEL); + menuExportSel.addActionListener(this); + menuExportAll.setIcon(View.getIcon("export16")); + menuExportSel.setIcon(View.getIcon("exportsel16")); + + menuFile.add(miOpen); + menuFile.add(miSave); + menuFile.add(miSaveAs); + menuFile.add(miSaveAsExe); + menuFile.add(menuExportFla); + menuFile.add(menuExportAll); + menuFile.add(menuExportSel); + menuFile.addSeparator(); + JMenuItem miClose = new JMenuItem(translate("menu.file.exit")); + miClose.setIcon(View.getIcon("exit16")); + miClose.setActionCommand(ACTION_EXIT); + miClose.addActionListener(this); + menuFile.add(miClose); + menuBar.add(menuFile); + JMenu menuDeobfuscation = new JMenu(translate("menu.tools.deobfuscation")); + menuDeobfuscation.setIcon(View.getIcon("deobfuscate16")); + + JMenuItem miDeobfuscation = new JMenuItem(translate("menu.tools.deobfuscation.pcode")); + miDeobfuscation.setActionCommand(ACTION_DEOBFUSCATE); + miDeobfuscation.addActionListener(this); + + miAutoDeobfuscation = new JCheckBoxMenuItem(translate("menu.settings.autodeobfuscation")); + miAutoDeobfuscation.setSelected(Configuration.autoDeobfuscate.get()); + miAutoDeobfuscation.addActionListener(this); + miAutoDeobfuscation.setActionCommand(ACTION_AUTO_DEOBFUSCATE); + + JMenuItem miRenameOneIdentifier = new JMenuItem(translate("menu.tools.deobfuscation.globalrename")); + miRenameOneIdentifier.setActionCommand(ACTION_RENAME_ONE_IDENTIFIER); + miRenameOneIdentifier.addActionListener(this); + + JMenuItem miRenameIdentifiers = new JMenuItem(translate("menu.tools.deobfuscation.renameinvalid")); + miRenameIdentifiers.setActionCommand(ACTION_RENAME_IDENTIFIERS); + miRenameIdentifiers.addActionListener(this); + + menuDeobfuscation.add(miRenameOneIdentifier); + menuDeobfuscation.add(miRenameIdentifiers); + menuDeobfuscation.add(miDeobfuscation); + JMenu menuTools = new JMenu(translate("menu.tools")); + JMenuItem miProxy = new JMenuItem(translate("menu.tools.proxy")); + miProxy.setActionCommand(ACTION_SHOW_PROXY); + miProxy.setIcon(View.getIcon("proxy16")); + miProxy.addActionListener(this); + + JMenuItem miSearchScript = new JMenuItem(translate("menu.tools.searchas")); + miSearchScript.addActionListener(this); + miSearchScript.setActionCommand(ACTION_SEARCH_AS); + miSearchScript.setIcon(View.getIcon("search16")); + + menuTools.add(miSearchScript); + + miInternalViewer = new JCheckBoxMenuItem(translate("menu.settings.internalflashviewer")); + miInternalViewer.setSelected(Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); + if (externalFlashPlayerUnavailable) { + miInternalViewer.setEnabled(false); + } + miInternalViewer.setActionCommand(ACTION_INTERNAL_VIEWER_SWITCH); + miInternalViewer.addActionListener(this); + + miParallelSpeedUp = new JCheckBoxMenuItem(translate("menu.settings.parallelspeedup")); + miParallelSpeedUp.setSelected(Configuration.parallelSpeedUp.get()); + miParallelSpeedUp.setActionCommand(ACTION_PARALLEL_SPEED_UP); + miParallelSpeedUp.addActionListener(this); + + menuTools.add(miProxy); + + menuTools.add(menuDeobfuscation); + + JMenuItem miGotoDocumentClass = new JMenuItem(translate("menu.tools.gotodocumentclass")); + miGotoDocumentClass.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS); + miGotoDocumentClass.addActionListener(this); + menuBar.add(menuTools); + + miDecompile = new JCheckBoxMenuItem(translate("menu.settings.disabledecompilation")); + miDecompile.setSelected(!Configuration.decompile.get()); + miDecompile.setActionCommand(ACTION_DISABLE_DECOMPILATION); + miDecompile.addActionListener(this); + + miCacheDisk = new JCheckBoxMenuItem(translate("menu.settings.cacheOnDisk")); + miCacheDisk.setSelected(Configuration.cacheOnDisk.get()); + miCacheDisk.setActionCommand(ACTION_CACHE_ON_DISK); + miCacheDisk.addActionListener(this); + + miGotoMainClassOnStartup = new JCheckBoxMenuItem(translate("menu.settings.gotoMainClassOnStartup")); + miGotoMainClassOnStartup.setSelected(Configuration.gotoMainClassOnStartup.get()); + miGotoMainClassOnStartup.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP); + miGotoMainClassOnStartup.addActionListener(this); + + miAutoRenameIdentifiers = new JCheckBoxMenuItem(translate("menu.settings.autoRenameIdentifiers")); + miAutoRenameIdentifiers.setSelected(Configuration.autoRenameIdentifiers.get()); + miAutoRenameIdentifiers.setActionCommand(ACTION_AUTO_RENAME_IDENTIFIERS); + miAutoRenameIdentifiers.addActionListener(this); + + JMenu menuSettings = new JMenu(translate("menu.settings")); + menuSettings.add(miAutoDeobfuscation); + menuSettings.add(miInternalViewer); + menuSettings.add(miParallelSpeedUp); + menuSettings.add(miDecompile); + menuSettings.add(miCacheDisk); + menuSettings.add(miGotoMainClassOnStartup); + menuSettings.add(miAutoRenameIdentifiers); + + miAssociate = new JCheckBoxMenuItem(translate("menu.settings.addtocontextmenu")); + miAssociate.setActionCommand(ACTION_ASSOCIATE); + miAssociate.addActionListener(this); + miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); + + JMenuItem miLanguage = new JMenuItem(translate("menu.settings.language")); + miLanguage.setActionCommand(ACTION_SET_LANGUAGE); + miLanguage.addActionListener(this); + + if (Platform.isWindows()) { + menuSettings.add(miAssociate); + } + menuSettings.add(miLanguage); + + JMenuItem advancedSettingsCommandButton = new JMenuItem(translate("menu.advancedsettings.advancedsettings")); + advancedSettingsCommandButton.setActionCommand(ACTION_ADVANCED_SETTINGS); + advancedSettingsCommandButton.setIcon(View.getIcon("settings16")); + advancedSettingsCommandButton.addActionListener(this); + menuSettings.add(advancedSettingsCommandButton); + + menuBar.add(menuSettings); + JMenu menuHelp = new JMenu(translate("menu.help")); + JMenuItem miAbout = new JMenuItem(translate("menu.help.about")); + miAbout.setIcon(View.getIcon("about16")); + + miAbout.setActionCommand(ACTION_ABOUT); + miAbout.addActionListener(this); + + JMenuItem miCheckUpdates = new JMenuItem(translate("menu.help.checkupdates")); + miCheckUpdates.setActionCommand(ACTION_CHECK_UPDATES); + miCheckUpdates.setIcon(View.getIcon("update16")); + miCheckUpdates.addActionListener(this); + + JMenuItem miHelpUs = new JMenuItem(translate("menu.help.helpus")); + miHelpUs.setActionCommand(ACTION_HELP_US); + miHelpUs.setIcon(View.getIcon("donate16")); + miHelpUs.addActionListener(this); + + JMenuItem miHomepage = new JMenuItem(translate("menu.help.homepage")); + miHomepage.setActionCommand(ACTION_HOMEPAGE); + miHomepage.setIcon(View.getIcon("homepage16")); + miHomepage.addActionListener(this); + + menuHelp.add(miCheckUpdates); + menuHelp.add(miHelpUs); + menuHelp.add(miHomepage); + menuHelp.add(miAbout); + menuBar.add(menuHelp); + + mainFrame.setJMenuBar(menuBar); + + //if (hasAbc) { + menuTools.add(miGotoDocumentClass); + //} + } + + @Override + public void updateComponents(SWF swf, List abcList) { + boolean swfLoaded = swf != null; + boolean hasAbc = swfLoaded && abcList != null && !abcList.isEmpty(); + + /*saveCommandButton.setEnabled(swfLoaded); + saveasCommandButton.setEnabled(swfLoaded); + saveasexeCommandButton.setEnabled(swfLoaded); + exportAllCommandButton.setEnabled(swfLoaded); + exportFlaCommandButton.setEnabled(swfLoaded); + exportSelectionCommandButton.setEnabled(swfLoaded); + reloadCommandButton.setEnabled(swfLoaded); + + renameinvalidCommandButton.setEnabled(swfLoaded); + globalrenameCommandButton.setEnabled(swfLoaded); + deobfuscationCommandButton.setEnabled(swfLoaded); + searchCommandButton.setEnabled(swfLoaded); + + gotoDocumentClassCommandButton.setEnabled(hasAbc); + deobfuscationCommandButton.setEnabled(hasAbc);*/ + } + + private void saveAs(SWF swf, SaveFileMode mode) { + if (Main.saveFileDialog(swf, mode)) { + swf.fileTitle = null; + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + saveCommandButton.setEnabled(mainFrame.panel.getCurrentSwf() != null); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_RELOAD: + if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + Main.reloadApp(); + } + break; + case ACTION_ADVANCED_SETTINGS: + Main.advancedSettings(); + break; + case ACTION_LOAD_MEMORY: + Main.loadFromMemory(); + break; + case ACTION_LOAD_CACHE: + Main.loadFromCache(); + break; + case ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP: + Configuration.gotoMainClassOnStartup.set(miGotoMainClassOnStartup.isSelected()); + break; + case ACTION_AUTO_RENAME_IDENTIFIERS: + Configuration.autoRenameIdentifiers.set(miAutoRenameIdentifiers.isSelected()); + break; + case ACTION_CACHE_ON_DISK: + Configuration.cacheOnDisk.set(miCacheDisk.isSelected()); + if (miCacheDisk.isSelected()) { + Cache.setStorageType(Cache.STORAGE_FILES); + } else { + Cache.setStorageType(Cache.STORAGE_MEMORY); + } + break; + case ACTION_SET_LANGUAGE: + new SelectLanguageDialog().display(); + break; + case ACTION_DISABLE_DECOMPILATION: + Configuration.decompile.set(!miDecompile.isSelected()); + mainFrame.panel.disableDecompilationChanged(); + break; + case ACTION_ASSOCIATE: + if (miAssociate.isSelected() == ContextMenuTools.isAddedToContextMenu()) { + return; + } + ContextMenuTools.addToContextMenu(miAssociate.isSelected(), false); + + //Update checkbox menuitem accordingly (User can cancel rights elevation) + new Timer().schedule(new TimerTask() { + @Override + public void run() { + miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); + } + }, 1000); //It takes some time registry change to apply + break; + case ACTION_GOTO_DOCUMENT_CLASS: + mainFrame.panel.gotoDocumentClass(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_PARALLEL_SPEED_UP: + String confStr = translate("message.confirm.parallel") + "\r\n"; + if (miParallelSpeedUp.isSelected()) { + confStr += " " + translate("message.confirm.on"); + } else { + confStr += " " + translate("message.confirm.off"); + } + if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.parallelSpeedUp.set((Boolean) miParallelSpeedUp.isSelected()); + } else { + miParallelSpeedUp.setSelected(!miParallelSpeedUp.isSelected()); + } + break; + case ACTION_INTERNAL_VIEWER_SWITCH: + Configuration.internalFlashViewer.set(miInternalViewer.isSelected()); + mainFrame.panel.reload(true); + break; + case ACTION_SEARCH_AS: + mainFrame.panel.searchAs(); + break; + case ACTION_AUTO_DEOBFUSCATE: + if (View.showConfirmDialog(mainFrame.panel, translate("message.confirm.autodeobfuscate") + "\r\n" + (miAutoDeobfuscation.isSelected() ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.autoDeobfuscate.set(miAutoDeobfuscation.isSelected()); + mainFrame.panel.autoDeobfuscateChanged(); + } else { + miAutoDeobfuscation.setSelected(!miAutoDeobfuscation.isSelected()); + } + break; + case ACTION_EXIT: + mainFrame.panel.setVisible(false); + if (Main.proxyFrame != null) { + if (Main.proxyFrame.isVisible()) { + return; + } + } + Main.exit(); + break; + } + + if (Main.isWorking()) { + return; + } + + switch (e.getActionCommand()) { + case ACTION_RENAME_ONE_IDENTIFIER: + mainFrame.panel.renameOneIdentifier(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_ABOUT: + Main.about(); + break; + case ACTION_SHOW_PROXY: + Main.showProxy(); + break; + case ACTION_SUB_LIMITER: + if (e.getSource() instanceof JCheckBoxMenuItem) { + Main.setSubLimiter(((JCheckBoxMenuItem) e.getSource()).getState()); + } + break; + case ACTION_SAVE: { + SWF swf = mainFrame.panel.getCurrentSwf(); + SWFNode snode = ((TagTreeModel) mainFrame.panel.tagTree.getModel()).getSwfNode(swf); + if (snode.binaryData != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + swf.saveTo(baos); + snode.binaryData.binaryData = baos.toByteArray(); + snode.binaryData.setModified(true); + } catch (IOException ex) { + Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); + } + } else if (swf.file == null) { + saveAs(swf, SaveFileMode.SAVEAS); + } else { + try { + Main.saveFile(swf, swf.file); + } catch (IOException ex) { + Logger.getLogger(MainFrameClassicMenu.class.getName()).log(Level.SEVERE, null, ex); + View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + } + break; + case ACTION_SAVE_AS: { + SWF swf = mainFrame.panel.getCurrentSwf(); + saveAs(swf, SaveFileMode.SAVEAS); + } + break; + case ACTION_SAVE_AS_EXE: { + SWF swf = mainFrame.panel.getCurrentSwf(); + saveAs(swf, SaveFileMode.EXE); + } + break; + case ACTION_OPEN: + Main.openFileDialog(); + break; + case ACTION_EXPORT_FLA: + mainFrame.panel.exportFla(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_EXPORT_SEL: + case ACTION_EXPORT: + boolean onlySel = e.getActionCommand().endsWith("SEL"); + mainFrame.panel.export(onlySel); + break; + case ACTION_CHECK_UPDATES: + if (!Main.checkForUpdates()) { + View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); + } + break; + case ACTION_HELP_US: + String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html"; + if (java.awt.Desktop.isDesktopSupported()) { + java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); + try { + java.net.URI uri = new java.net.URI(helpUsURL); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); + } + break; + case ACTION_HOMEPAGE: + String homePageURL = ApplicationInfo.PROJECT_PAGE; + if (java.awt.Desktop.isDesktopSupported()) { + java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); + try { + java.net.URI uri = new java.net.URI(homePageURL); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); + } + break; + case ACTION_RESTORE_CONTROL_FLOW: + case ACTION_RESTORE_CONTROL_FLOW_ALL: + boolean all = e.getActionCommand().endsWith("ALL"); + mainFrame.panel.restoreControlFlow(all); + break; + case ACTION_RENAME_IDENTIFIERS: + mainFrame.panel.renameIdentifiers(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_DEOBFUSCATE: + case ACTION_DEOBFUSCATE_ALL: + mainFrame.panel.deobfuscate(); + break; + case ACTION_REMOVE_NON_SCRIPTS: + mainFrame.panel.removeNonScripts(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_REFRESH_DECOMPILED: + mainFrame.panel.refreshDecompiled(); + break; + } + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java index 617c961ed..7d19d58b0 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java +++ b/src/com/jpexs/decompiler/flash/gui/MainFrameRibbonMenu.java @@ -1,804 +1,803 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.console.ContextMenuTools; -import com.jpexs.decompiler.flash.gui.helpers.CheckResources; -import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.utf8.Utf8Helper; -import com.jpexs.process.ProcessTools; -import com.sun.jna.Platform; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.ScrollPane; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JCheckBox; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; -import org.pushingpixels.flamingo.api.common.JCommandButton; -import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; -import org.pushingpixels.flamingo.api.ribbon.JRibbon; -import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; -import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter; -import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary; -import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; -import org.pushingpixels.flamingo.api.ribbon.RibbonTask; -import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy; -import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; -import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; -import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; -import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; - -/** - * - * @author JPEXS - */ -public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener { - - static final String ACTION_RELOAD = "RELOAD"; - static final String ACTION_ADVANCED_SETTINGS = "ADVANCEDSETTINGS"; - static final String ACTION_LOAD_MEMORY = "LOADMEMORY"; - static final String ACTION_LOAD_CACHE = "LOADCACHE"; - static final String ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP = "GOTODOCUMENTCLASSONSTARTUP"; - static final String ACTION_AUTO_RENAME_IDENTIFIERS = "AUTORENAMEIDENTIFIERS"; - static final String ACTION_CACHE_ON_DISK = "CACHEONDISK"; - static final String ACTION_SET_LANGUAGE = "SETLANGUAGE"; - static final String ACTION_DISABLE_DECOMPILATION = "DISABLEDECOMPILATION"; - static final String ACTION_ASSOCIATE = "ASSOCIATE"; - static final String ACTION_GOTO_DOCUMENT_CLASS = "GOTODOCUMENTCLASS"; - static final String ACTION_PARALLEL_SPEED_UP = "PARALLELSPEEDUP"; - static final String ACTION_INTERNAL_VIEWER_SWITCH = "INTERNALVIEWERSWITCH"; - static final String ACTION_SEARCH = "SEARCH"; - static final String ACTION_TIMELINE = "TIMELINE"; - static final String ACTION_AUTO_DEOBFUSCATE = "AUTODEOBFUSCATE"; - static final String ACTION_EXIT = "EXIT"; - - static final String ACTION_RENAME_ONE_IDENTIFIER = "RENAMEONEIDENTIFIER"; - static final String ACTION_ABOUT = "ABOUT"; - static final String ACTION_SHOW_PROXY = "SHOWPROXY"; - static final String ACTION_SUB_LIMITER = "SUBLIMITER"; - static final String ACTION_SAVE = "SAVE"; - static final String ACTION_SAVE_AS = "SAVEAS"; - static final String ACTION_SAVE_AS_EXE = "SAVEASEXE"; - static final String ACTION_OPEN = "OPEN"; - static final String ACTION_CLOSE = "CLOSE"; - static final String ACTION_CLOSE_ALL = "CLOSEALL"; - static final String ACTION_EXPORT_FLA = "EXPORTFLA"; - public static final String ACTION_EXPORT_SEL = "EXPORTSEL"; - static final String ACTION_EXPORT = "EXPORT"; - static final String ACTION_IMPORT_TEXT = "IMPORTTEXT"; - static final String ACTION_CHECK_UPDATES = "CHECKUPDATES"; - static final String ACTION_HELP_US = "HELPUS"; - static final String ACTION_HOMEPAGE = "HOMEPAGE"; - static final String ACTION_RESTORE_CONTROL_FLOW = "RESTORECONTROLFLOW"; - static final String ACTION_RESTORE_CONTROL_FLOW_ALL = "RESTORECONTROLFLOWALL"; - static final String ACTION_RENAME_IDENTIFIERS = "RENAMEIDENTIFIERS"; - static final String ACTION_DEOBFUSCATE = "DEOBFUSCATE"; - static final String ACTION_DEOBFUSCATE_ALL = "DEOBFUSCATEALL"; - static final String ACTION_REMOVE_NON_SCRIPTS = "REMOVENONSCRIPTS"; - static final String ACTION_REFRESH_DECOMPILED = "REFRESHDECOMPILED"; - static final String ACTION_CLEAR_RECENT_FILES = "CLEARRECENTFILES"; - static final String ACTION_CHECK_RESOURCES = "CHECKRESOURCES"; - - private final MainFrameRibbon mainFrame; - - private JCheckBox miAutoDeobfuscation; - private JCheckBox miInternalViewer; - private JCheckBox miParallelSpeedUp; - private JCheckBox miAssociate; - private JCheckBox miDecompile; - private JCheckBox miCacheDisk; - private JCheckBox miGotoMainClassOnStartup; - private JCheckBox miAutoRenameIdentifiers; - private JCommandButton saveCommandButton; - private JCommandButton saveasCommandButton; - private JCommandButton saveasexeCommandButton; - private JCommandButton exportAllCommandButton; - private JCommandButton exportFlaCommandButton; - private JCommandButton exportSelectionCommandButton; - private JCommandButton importTextCommandButton; - - private JCommandButton reloadCommandButton; - private JCommandButton renameinvalidCommandButton; - private JCommandButton globalrenameCommandButton; - private JCommandButton deobfuscationCommandButton; - private JCommandButton searchCommandButton; - private JCommandButton timeLineCommandButton; - private JCommandButton gotoDocumentClassCommandButton; - private JCommandButton clearRecentFilesCommandButton; - - RibbonApplicationMenuEntryPrimary exportFlaMenu; - RibbonApplicationMenuEntryPrimary exportAllMenu; - RibbonApplicationMenuEntryPrimary exportSelMenu; - RibbonApplicationMenuEntryPrimary closeFileMenu; - RibbonApplicationMenuEntryPrimary closeAllFilesMenu; - - public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) { - this.mainFrame = mainFrame; - - ribbon.addTask(createFileRibbonTask()); - ribbon.addTask(createToolsRibbonTask()); - ribbon.addTask(createSettingsRibbonTask(externalFlashPlayerUnavailable)); - ribbon.addTask(createHelpRibbonTask()); - - if (Configuration.debugMode.get()) { - ribbon.addTask(createDebugRibbonTask()); - } - - ribbon.setApplicationMenu(createMainMenu()); - } - - @Override - public boolean isInternalFlashViewerSelected() { - return miInternalViewer.isSelected(); - } - - private String translate(String key) { - return mainFrame.translate(key); - } - - private void assignListener(JCommandButton b, final String command) { - final MainFrameRibbonMenu t = this; - b.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - t.actionPerformed(new ActionEvent(e.getSource(), 0, command)); - } - }); - } - - private String fixCommandTitle(String title) { - if (title.length() > 2) { - if (title.charAt(1) == ' ') { - title = title.charAt(0) + "\u00A0" + title.substring(2); - } - } - return title; - } - - private RibbonApplicationMenu createMainMenu() { - RibbonApplicationMenu mainMenu = new RibbonApplicationMenu(); - exportFlaMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportfla32"), translate("menu.file.export.fla"), new ActionRedirector(this, ACTION_EXPORT_FLA), JCommandButton.CommandButtonKind.ACTION_ONLY); - exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, ACTION_EXPORT), JCommandButton.CommandButtonKind.ACTION_ONLY); - exportSelMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportsel32"), translate("menu.file.export.selection"), new ActionRedirector(this, ACTION_EXPORT_SEL), JCommandButton.CommandButtonKind.ACTION_ONLY); - RibbonApplicationMenuEntryPrimary checkUpdatesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("update32"), translate("menu.help.checkupdates"), new ActionRedirector(this, ACTION_CHECK_UPDATES), JCommandButton.CommandButtonKind.ACTION_ONLY); - RibbonApplicationMenuEntryPrimary aboutMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("about32"), translate("menu.help.about"), new ActionRedirector(this, ACTION_ABOUT), JCommandButton.CommandButtonKind.ACTION_ONLY); - RibbonApplicationMenuEntryPrimary openFileMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("open32"), translate("menu.file.open"), new ActionRedirector(this, ACTION_OPEN), JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); - closeFileMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("close32"), translate("menu.file.close"), new ActionRedirector(this, ACTION_CLOSE), JCommandButton.CommandButtonKind.ACTION_ONLY); - closeAllFilesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("close32"), translate("menu.file.closeAll"), new ActionRedirector(this, ACTION_CLOSE_ALL), JCommandButton.CommandButtonKind.ACTION_ONLY); - openFileMenu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() { - @Override - public void menuEntryActivated(JPanel targetPanel) { - targetPanel.removeAll(); - JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM); - String groupName = translate("menu.recentFiles"); - openHistoryPanel.addButtonGroup(groupName); - List recentFiles = Configuration.getRecentFiles(); - int j = 0; - for (int i = recentFiles.size() - 1; i >= 0; i--) { - String path = recentFiles.get(i); - RecentFilesButton historyButton = new RecentFilesButton(j + " " + path, null); - historyButton.fileName = path; - historyButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent ae) { - RecentFilesButton source = (RecentFilesButton) ae.getSource(); - if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) { - if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { - Configuration.removeRecentFile(source.fileName); - } - } - } - }); - j++; - historyButton.setHorizontalAlignment(SwingUtilities.LEFT); - openHistoryPanel.addButtonToLastGroup(historyButton); - } - openHistoryPanel.setMaxButtonColumns(1); - targetPanel.setLayout(new BorderLayout()); - targetPanel.add(openHistoryPanel, BorderLayout.CENTER); - } - }); - - RibbonApplicationMenuEntryFooter exitMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon("exit32"), translate("menu.file.exit"), new ActionRedirector(this, "EXIT")); - - mainMenu.addMenuEntry(openFileMenu); - mainMenu.addMenuEntry(closeFileMenu); - mainMenu.addMenuEntry(closeAllFilesMenu); - mainMenu.addMenuSeparator(); - mainMenu.addMenuEntry(exportFlaMenu); - mainMenu.addMenuEntry(exportAllMenu); - mainMenu.addMenuEntry(exportSelMenu); - mainMenu.addMenuSeparator(); - mainMenu.addMenuEntry(checkUpdatesMenu); - mainMenu.addMenuEntry(aboutMenu); - mainMenu.addFooterEntry(exitMenu); - mainMenu.addMenuSeparator(); - - return mainMenu; - } - - private List getResizePolicies(JRibbonBand ribbonBand) { - List resizePolicies = new ArrayList<>(); - resizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel())); - resizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); - return resizePolicies; - } - - private List getIconBandResizePolicies(JRibbonBand ribbonBand) { - List resizePolicies = new ArrayList<>(); - resizePolicies.add(new BaseRibbonBandResizePolicy(ribbonBand.getControlPanel()) { - @Override - public int getPreferredWidth(int i, int i1) { - return 105; - } - - @Override - public void install(int i, int i1) { - } - }); - resizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); - return resizePolicies; - } - - private RibbonTask createFileRibbonTask() { - JRibbonBand editBand = new JRibbonBand(translate("menu.general"), null); - editBand.setResizePolicies(getResizePolicies(editBand)); - JCommandButton openCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.open")), View.getResizableIcon("open32")); - assignListener(openCommandButton, ACTION_OPEN); - saveCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.save")), View.getResizableIcon("save32")); - assignListener(saveCommandButton, ACTION_SAVE); - saveasCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.saveas")), View.getResizableIcon("saveas16")); - assignListener(saveasCommandButton, ACTION_SAVE_AS); - - reloadCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.reload")), View.getResizableIcon("reload16")); - assignListener(reloadCommandButton, ACTION_RELOAD); - - editBand.addCommandButton(openCommandButton, RibbonElementPriority.TOP); - editBand.addCommandButton(saveCommandButton, RibbonElementPriority.TOP); - editBand.addCommandButton(saveasCommandButton, RibbonElementPriority.MEDIUM); - editBand.addCommandButton(reloadCommandButton, RibbonElementPriority.MEDIUM); - - JRibbonBand exportBand = new JRibbonBand(translate("menu.export"), null); - exportBand.setResizePolicies(getResizePolicies(exportBand)); - exportFlaCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.fla")), View.getResizableIcon("exportfla32")); - assignListener(exportFlaCommandButton, ACTION_EXPORT_FLA); - exportAllCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.all")), View.getResizableIcon("export16")); - assignListener(exportAllCommandButton, ACTION_EXPORT); - exportSelectionCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.selection")), View.getResizableIcon("exportsel16")); - assignListener(exportSelectionCommandButton, ACTION_EXPORT_SEL); - saveasexeCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.saveasexe")), View.getResizableIcon("saveasexe16")); - assignListener(saveasexeCommandButton, ACTION_SAVE_AS_EXE); - - exportBand.addCommandButton(exportFlaCommandButton, RibbonElementPriority.TOP); - exportBand.addCommandButton(exportAllCommandButton, RibbonElementPriority.MEDIUM); - exportBand.addCommandButton(exportSelectionCommandButton, RibbonElementPriority.MEDIUM); - exportBand.addCommandButton(saveasexeCommandButton, RibbonElementPriority.MEDIUM); - - JRibbonBand importBand = new JRibbonBand(translate("menu.import"), null); - importBand.setResizePolicies(getResizePolicies(importBand)); - importTextCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.import.text")), View.getResizableIcon("import32")); - assignListener(importTextCommandButton, ACTION_IMPORT_TEXT); - - importBand.addCommandButton(importTextCommandButton, RibbonElementPriority.TOP); - - return new RibbonTask(translate("menu.file"), editBand, exportBand, importBand); - } - - private RibbonTask createToolsRibbonTask() { - //----------------------------------------- TOOLS ----------------------------------- - - JRibbonBand toolsBand = new JRibbonBand(translate("menu.tools"), null); - toolsBand.setResizePolicies(getResizePolicies(toolsBand)); - - searchCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.search")), View.getResizableIcon("search32")); - assignListener(searchCommandButton, ACTION_SEARCH); - - timeLineCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.timeline")), View.getResizableIcon("timeline32")); - assignListener(timeLineCommandButton, ACTION_TIMELINE); - - gotoDocumentClassCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.gotodocumentclass")), View.getResizableIcon("gotomainclass32")); - assignListener(gotoDocumentClassCommandButton, ACTION_GOTO_DOCUMENT_CLASS); - - JCommandButton proxyCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.proxy")), View.getResizableIcon("proxy16")); - assignListener(proxyCommandButton, ACTION_SHOW_PROXY); - - JCommandButton loadMemoryCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.searchmemory")), View.getResizableIcon("loadmemory16")); - assignListener(loadMemoryCommandButton, ACTION_LOAD_MEMORY); - - JCommandButton loadCacheCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.searchcache")), View.getResizableIcon("loadcache16")); - assignListener(loadCacheCommandButton, ACTION_LOAD_CACHE); - - toolsBand.addCommandButton(searchCommandButton, RibbonElementPriority.TOP); - toolsBand.addCommandButton(timeLineCommandButton, RibbonElementPriority.TOP); - toolsBand.addCommandButton(gotoDocumentClassCommandButton, RibbonElementPriority.TOP); - toolsBand.addCommandButton(proxyCommandButton, RibbonElementPriority.MEDIUM); - toolsBand.addCommandButton(loadMemoryCommandButton, RibbonElementPriority.MEDIUM); - toolsBand.addCommandButton(loadCacheCommandButton, RibbonElementPriority.MEDIUM); - if (!ProcessTools.toolsAvailable()) { - loadMemoryCommandButton.setEnabled(false); - } - JRibbonBand deobfuscationBand = new JRibbonBand(translate("menu.tools.deobfuscation"), null); - deobfuscationBand.setResizePolicies(getResizePolicies(deobfuscationBand)); - - deobfuscationCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.pcode")), View.getResizableIcon("deobfuscate32")); - assignListener(deobfuscationCommandButton, ACTION_DEOBFUSCATE); - globalrenameCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.globalrename")), View.getResizableIcon("rename16")); - assignListener(globalrenameCommandButton, ACTION_RENAME_ONE_IDENTIFIER); - renameinvalidCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.renameinvalid")), View.getResizableIcon("renameall16")); - assignListener(renameinvalidCommandButton, ACTION_RENAME_IDENTIFIERS); - - deobfuscationBand.addCommandButton(deobfuscationCommandButton, RibbonElementPriority.TOP); - deobfuscationBand.addCommandButton(globalrenameCommandButton, RibbonElementPriority.MEDIUM); - deobfuscationBand.addCommandButton(renameinvalidCommandButton, RibbonElementPriority.MEDIUM); - - //JRibbonBand otherToolsBand = new JRibbonBand(translate("menu.tools.otherTools"), null); - //otherToolsBand.setResizePolicies(getResizePolicies(otherToolsBand)); - return new RibbonTask(translate("menu.tools"), toolsBand, deobfuscationBand/*, otherToolsBand*/); - } - - private RibbonTask createSettingsRibbonTask(boolean externalFlashPlayerUnavailable) { - //----------------------------------------- SETTINGS ----------------------------------- - - JRibbonBand settingsBand = new JRibbonBand(translate("menu.settings"), null); - settingsBand.setResizePolicies(getResizePolicies(settingsBand)); - - miAutoDeobfuscation = new JCheckBox(translate("menu.settings.autodeobfuscation")); - miAutoDeobfuscation.setSelected(Configuration.autoDeobfuscate.get()); - miAutoDeobfuscation.addActionListener(this); - miAutoDeobfuscation.setActionCommand(ACTION_AUTO_DEOBFUSCATE); - - miInternalViewer = new JCheckBox(translate("menu.settings.internalflashviewer")); - miInternalViewer.setSelected(Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); - if (externalFlashPlayerUnavailable) { - miInternalViewer.setEnabled(false); - } - miInternalViewer.setActionCommand(ACTION_INTERNAL_VIEWER_SWITCH); - miInternalViewer.addActionListener(this); - - miParallelSpeedUp = new JCheckBox(translate("menu.settings.parallelspeedup")); - miParallelSpeedUp.setSelected(Configuration.parallelSpeedUp.get()); - miParallelSpeedUp.setActionCommand(ACTION_PARALLEL_SPEED_UP); - miParallelSpeedUp.addActionListener(this); - - miDecompile = new JCheckBox(translate("menu.settings.disabledecompilation")); - miDecompile.setSelected(!Configuration.decompile.get()); - miDecompile.setActionCommand(ACTION_DISABLE_DECOMPILATION); - miDecompile.addActionListener(this); - - miAssociate = new JCheckBox(translate("menu.settings.addtocontextmenu")); - miAssociate.setActionCommand(ACTION_ASSOCIATE); - miAssociate.addActionListener(this); - miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); - - miCacheDisk = new JCheckBox(translate("menu.settings.cacheOnDisk")); - miCacheDisk.setSelected(Configuration.cacheOnDisk.get()); - miCacheDisk.setActionCommand(ACTION_CACHE_ON_DISK); - miCacheDisk.addActionListener(this); - - miGotoMainClassOnStartup = new JCheckBox(translate("menu.settings.gotoMainClassOnStartup")); - miGotoMainClassOnStartup.setSelected(Configuration.gotoMainClassOnStartup.get()); - miGotoMainClassOnStartup.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP); - miGotoMainClassOnStartup.addActionListener(this); - - miAutoRenameIdentifiers = new JCheckBox(translate("menu.settings.autoRenameIdentifiers")); - miAutoRenameIdentifiers.setSelected(Configuration.autoRenameIdentifiers.get()); - miAutoRenameIdentifiers.setActionCommand(ACTION_AUTO_RENAME_IDENTIFIERS); - miAutoRenameIdentifiers.addActionListener(this); - - settingsBand.addRibbonComponent(new JRibbonComponent(miAutoDeobfuscation)); - settingsBand.addRibbonComponent(new JRibbonComponent(miInternalViewer)); - settingsBand.addRibbonComponent(new JRibbonComponent(miParallelSpeedUp)); - settingsBand.addRibbonComponent(new JRibbonComponent(miDecompile)); - if (Platform.isWindows()) { - settingsBand.addRibbonComponent(new JRibbonComponent(miAssociate)); - } - settingsBand.addRibbonComponent(new JRibbonComponent(miCacheDisk)); - settingsBand.addRibbonComponent(new JRibbonComponent(miGotoMainClassOnStartup)); - settingsBand.addRibbonComponent(new JRibbonComponent(miAutoRenameIdentifiers)); - - JRibbonBand languageBand = new JRibbonBand(translate("menu.language"), null); - List languageBandResizePolicies = getIconBandResizePolicies(languageBand); - languageBand.setResizePolicies(languageBandResizePolicies); - JCommandButton setLanguageCommandButton = new JCommandButton(fixCommandTitle(translate("menu.settings.language")), View.getResizableIcon("setlanguage32")); - assignListener(setLanguageCommandButton, ACTION_SET_LANGUAGE); - languageBand.addCommandButton(setLanguageCommandButton, RibbonElementPriority.TOP); - - JRibbonBand advancedSettingsBand = new JRibbonBand(translate("menu.advancedsettings.advancedsettings"), null); - advancedSettingsBand.setResizePolicies(getResizePolicies(advancedSettingsBand)); - JCommandButton advancedSettingsCommandButton = new JCommandButton(fixCommandTitle(translate("menu.advancedsettings.advancedsettings")), View.getResizableIcon("settings32")); - assignListener(advancedSettingsCommandButton, ACTION_ADVANCED_SETTINGS); - advancedSettingsBand.addCommandButton(advancedSettingsCommandButton, RibbonElementPriority.TOP); - - clearRecentFilesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.otherTools.clearRecentFiles")), View.getResizableIcon("clearrecent16")); - assignListener(clearRecentFilesCommandButton, ACTION_CLEAR_RECENT_FILES); - advancedSettingsBand.addCommandButton(clearRecentFilesCommandButton, RibbonElementPriority.MEDIUM); - - return new RibbonTask(translate("menu.settings"), settingsBand, languageBand, advancedSettingsBand); - } - - private RibbonTask createHelpRibbonTask() { - //----------------------------------------- HELP ----------------------------------- - - JRibbonBand helpBand = new JRibbonBand(translate("menu.help"), null); - helpBand.setResizePolicies(getResizePolicies(helpBand)); - - JCommandButton checkForUpdatesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.checkupdates")), View.getResizableIcon("update16")); - assignListener(checkForUpdatesCommandButton, ACTION_CHECK_UPDATES); - JCommandButton helpUsUpdatesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.helpus")), View.getResizableIcon("donate32")); - assignListener(helpUsUpdatesCommandButton, ACTION_HELP_US); - JCommandButton homepageCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.homepage")), View.getResizableIcon("homepage16")); - assignListener(homepageCommandButton, ACTION_HOMEPAGE); - JCommandButton aboutCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.about")), View.getResizableIcon("about32")); - assignListener(aboutCommandButton, ACTION_ABOUT); - - helpBand.addCommandButton(aboutCommandButton, RibbonElementPriority.TOP); - helpBand.addCommandButton(checkForUpdatesCommandButton, RibbonElementPriority.MEDIUM); - helpBand.addCommandButton(homepageCommandButton, RibbonElementPriority.MEDIUM); - helpBand.addCommandButton(helpUsUpdatesCommandButton, RibbonElementPriority.TOP); - return new RibbonTask(translate("menu.help"), helpBand); - } - - private RibbonTask createDebugRibbonTask() { - //----------------------------------------- DEBUG ----------------------------------- - - JRibbonBand debugBand = new JRibbonBand("Debug", null); - debugBand.setResizePolicies(getResizePolicies(debugBand)); - - JCommandButton removeNonScriptsCommandButton = new JCommandButton(fixCommandTitle("Remove non scripts"), View.getResizableIcon("update16")); - assignListener(removeNonScriptsCommandButton, ACTION_REMOVE_NON_SCRIPTS); - - JCommandButton refreshDecompiledCommandButton = new JCommandButton(fixCommandTitle("Refresh decompiled script"), View.getResizableIcon("update16")); - assignListener(refreshDecompiledCommandButton, ACTION_REFRESH_DECOMPILED); - - JCommandButton checkResourcesCommandButton = new JCommandButton(fixCommandTitle("Check resources"), View.getResizableIcon("update16")); - assignListener(checkResourcesCommandButton, ACTION_CHECK_RESOURCES); - - debugBand.addCommandButton(removeNonScriptsCommandButton, RibbonElementPriority.MEDIUM); - debugBand.addCommandButton(refreshDecompiledCommandButton, RibbonElementPriority.MEDIUM); - debugBand.addCommandButton(checkResourcesCommandButton, RibbonElementPriority.MEDIUM); - return new RibbonTask("Debug", debugBand); - } - - @Override - public void updateComponents(SWF swf, List abcList) { - boolean swfLoaded = swf != null; - boolean hasAbc = swfLoaded && abcList != null && !abcList.isEmpty(); - - exportAllMenu.setEnabled(swfLoaded); - exportFlaMenu.setEnabled(swfLoaded); - exportSelMenu.setEnabled(swfLoaded); - closeFileMenu.setEnabled(swfLoaded); - closeAllFilesMenu.setEnabled(swfLoaded); - - boolean isBundle = swfLoaded && (swf.swfList != null) && swf.swfList.isBundle; - saveCommandButton.setEnabled(swfLoaded && !isBundle); - saveasCommandButton.setEnabled(swfLoaded); - saveasexeCommandButton.setEnabled(swfLoaded); - exportAllCommandButton.setEnabled(swfLoaded); - exportFlaCommandButton.setEnabled(swfLoaded); - exportSelectionCommandButton.setEnabled(swfLoaded); - importTextCommandButton.setEnabled(swfLoaded); - reloadCommandButton.setEnabled(swfLoaded); - - renameinvalidCommandButton.setEnabled(swfLoaded); - globalrenameCommandButton.setEnabled(swfLoaded); - deobfuscationCommandButton.setEnabled(swfLoaded); - searchCommandButton.setEnabled(swfLoaded); - timeLineCommandButton.setEnabled(swfLoaded); - - gotoDocumentClassCommandButton.setEnabled(hasAbc); - deobfuscationCommandButton.setEnabled(hasAbc); - } - - private void saveAs(SWF swf, SaveFileMode mode) { - if (Main.saveFileDialog(swf, mode)) { - swf.fileTitle = null; - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - saveCommandButton.setEnabled(mainFrame.panel.getCurrentSwf() != null); - } - } - - private void clearModified(SWF swf) { - for (Tag tag : swf.tags) { - if (tag.isModified()) { - tag.createOriginalData(); - tag.setModified(false); - } - } - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_RELOAD: - if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - Main.reloadApp(); - } - break; - case ACTION_ADVANCED_SETTINGS: - Main.advancedSettings(); - break; - case ACTION_LOAD_MEMORY: - Main.loadFromMemory(); - break; - case ACTION_LOAD_CACHE: - Main.loadFromCache(); - break; - case ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP: - Configuration.gotoMainClassOnStartup.set(miGotoMainClassOnStartup.isSelected()); - break; - case ACTION_AUTO_RENAME_IDENTIFIERS: - Configuration.autoRenameIdentifiers.set(miAutoRenameIdentifiers.isSelected()); - break; - case ACTION_CACHE_ON_DISK: - Configuration.cacheOnDisk.set(miCacheDisk.isSelected()); - if (miCacheDisk.isSelected()) { - Cache.setStorageType(Cache.STORAGE_FILES); - } else { - Cache.setStorageType(Cache.STORAGE_MEMORY); - } - break; - case ACTION_SET_LANGUAGE: - new SelectLanguageDialog().display(); - break; - case ACTION_DISABLE_DECOMPILATION: - Configuration.decompile.set(!miDecompile.isSelected()); - mainFrame.panel.disableDecompilationChanged(); - break; - case ACTION_ASSOCIATE: - if (miAssociate.isSelected() == ContextMenuTools.isAddedToContextMenu()) { - return; - } - ContextMenuTools.addToContextMenu(miAssociate.isSelected(), false); - - //Update checkbox menuitem accordingly (User can cancel rights elevation) - new Timer().schedule(new TimerTask() { - @Override - public void run() { - miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); - } - }, 1000); //It takes some time registry change to apply - break; - case ACTION_GOTO_DOCUMENT_CLASS: - mainFrame.panel.gotoDocumentClass(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_PARALLEL_SPEED_UP: - String confStr = translate("message.confirm.parallel") + "\r\n"; - if (miParallelSpeedUp.isSelected()) { - confStr += " " + translate("message.confirm.on"); - } else { - confStr += " " + translate("message.confirm.off"); - } - if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.parallelSpeedUp.set((Boolean) miParallelSpeedUp.isSelected()); - } else { - miParallelSpeedUp.setSelected(!miParallelSpeedUp.isSelected()); - } - break; - case ACTION_INTERNAL_VIEWER_SWITCH: - Configuration.internalFlashViewer.set(miInternalViewer.isSelected()); - mainFrame.panel.reload(true); - break; - case ACTION_SEARCH: - mainFrame.panel.searchAs(); - break; - case ACTION_TIMELINE: - mainFrame.panel.timeline(); - break; - case ACTION_AUTO_DEOBFUSCATE: - if (View.showConfirmDialog(mainFrame.panel, translate("message.confirm.autodeobfuscate") + "\r\n" + (miAutoDeobfuscation.isSelected() ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { - Configuration.autoDeobfuscate.set(miAutoDeobfuscation.isSelected()); - mainFrame.panel.autoDeobfuscateChanged(); - } else { - miAutoDeobfuscation.setSelected(!miAutoDeobfuscation.isSelected()); - } - break; - case ACTION_CLEAR_RECENT_FILES: - Configuration.recentFiles.set(null); - break; - case ACTION_EXIT: - mainFrame.panel.setVisible(false); - if (Main.proxyFrame != null) { - if (Main.proxyFrame.isVisible()) { - return; - } - } - Main.exit(); - break; - } - - if (Main.isWorking()) { - return; - } - - switch (e.getActionCommand()) { - case ACTION_RENAME_ONE_IDENTIFIER: - mainFrame.panel.renameOneIdentifier(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_ABOUT: - Main.about(); - break; - case ACTION_SHOW_PROXY: - Main.showProxy(); - break; - case ACTION_SUB_LIMITER: - if (e.getSource() instanceof JCheckBoxMenuItem) { - Main.setSubLimiter(((JCheckBoxMenuItem) e.getSource()).getState()); - } - break; - case ACTION_SAVE: { - SWF swf = mainFrame.panel.getCurrentSwf(); - SWFNode snode = ((TagTreeModel)mainFrame.panel.tagTree.getModel()).getSwfNode(swf); - if(snode.binaryData!=null){ - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - swf.saveTo(baos); - snode.binaryData.binaryData = baos.toByteArray(); - snode.binaryData.setModified(true); - } catch (IOException ex) { - Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); - } - } - else if (swf.file == null) { - saveAs(swf, SaveFileMode.SAVEAS); - } else { - try { - Main.saveFile(swf, swf.file); - } catch (IOException ex) { - Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, null, ex); - View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - } - clearModified(swf); - } - break; - case ACTION_SAVE_AS: { - SWF swf = mainFrame.panel.getCurrentSwf(); - saveAs(swf, SaveFileMode.SAVEAS); - clearModified(swf); - } - break; - case ACTION_SAVE_AS_EXE: { - SWF swf = mainFrame.panel.getCurrentSwf(); - saveAs(swf, SaveFileMode.EXE); - } - break; - case ACTION_OPEN: - Main.openFileDialog(); - break; - case ACTION_CLOSE: - Main.closeFile(mainFrame.panel.getCurrentSwfList()); - break; - case ACTION_CLOSE_ALL: - Main.closeAll(); - break; - case ACTION_EXPORT_FLA: - mainFrame.panel.exportFla(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_IMPORT_TEXT: - mainFrame.panel.importText(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_EXPORT_SEL: - case ACTION_EXPORT: - boolean onlySel = e.getActionCommand().endsWith("SEL"); - mainFrame.panel.export(onlySel); - break; - case ACTION_CHECK_UPDATES: - if (!Main.checkForUpdates()) { - View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); - } - break; - case ACTION_HELP_US: - String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html"; - if (java.awt.Desktop.isDesktopSupported()) { - java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); - try { - java.net.URI uri = new java.net.URI(helpUsURL); - desktop.browse(uri); - } catch (URISyntaxException | IOException ex) { - } - } else { - View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); - } - break; - case ACTION_HOMEPAGE: - String homePageURL = ApplicationInfo.PROJECT_PAGE; - if (java.awt.Desktop.isDesktopSupported()) { - java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); - try { - java.net.URI uri = new java.net.URI(homePageURL); - desktop.browse(uri); - } catch (URISyntaxException | IOException ex) { - } - } else { - View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); - } - break; - case ACTION_RESTORE_CONTROL_FLOW: - case ACTION_RESTORE_CONTROL_FLOW_ALL: - boolean all = e.getActionCommand().endsWith("ALL"); - mainFrame.panel.restoreControlFlow(all); - break; - case ACTION_RENAME_IDENTIFIERS: - mainFrame.panel.renameIdentifiers(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_DEOBFUSCATE: - case ACTION_DEOBFUSCATE_ALL: - mainFrame.panel.deobfuscate(); - break; - case ACTION_REMOVE_NON_SCRIPTS: - mainFrame.panel.removeNonScripts(mainFrame.panel.getCurrentSwf()); - break; - case ACTION_REFRESH_DECOMPILED: - mainFrame.panel.refreshDecompiled(); - break; - case ACTION_CHECK_RESOURCES: - ByteArrayOutputStream os = new ByteArrayOutputStream(); - PrintStream stream = new PrintStream(os); - CheckResources.checkResources(stream); - final String str = new String(os.toByteArray(), Utf8Helper.charset); - JDialog dialog = new JDialog() { - - @Override - public void setVisible(boolean bln) { - setSize(new Dimension(800, 600)); - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - ScrollPane scrollPane = new ScrollPane(); - JEditorPane editor = new JEditorPane(); - editor.setEditable(false); - editor.setText(str); - scrollPane.add(editor); - this.add(scrollPane, BorderLayout.CENTER); - this.setModal(true); - View.centerScreen(this); - super.setVisible(bln); - } - }; - dialog.setVisible(true); - break; - } - } - -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.console.ContextMenuTools; +import com.jpexs.decompiler.flash.gui.helpers.CheckResources; +import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.utf8.Utf8Helper; +import com.jpexs.process.ProcessTools; +import com.sun.jna.Platform; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState; +import org.pushingpixels.flamingo.api.common.JCommandButton; +import org.pushingpixels.flamingo.api.common.JCommandButtonPanel; +import org.pushingpixels.flamingo.api.ribbon.JRibbon; +import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; +import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter; +import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary; +import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; +import org.pushingpixels.flamingo.api.ribbon.RibbonTask; +import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy; +import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies; +import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy; +import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy; +import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel; + +/** + * + * @author JPEXS + */ +public class MainFrameRibbonMenu implements MainFrameMenu, ActionListener { + + static final String ACTION_RELOAD = "RELOAD"; + static final String ACTION_ADVANCED_SETTINGS = "ADVANCEDSETTINGS"; + static final String ACTION_LOAD_MEMORY = "LOADMEMORY"; + static final String ACTION_LOAD_CACHE = "LOADCACHE"; + static final String ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP = "GOTODOCUMENTCLASSONSTARTUP"; + static final String ACTION_AUTO_RENAME_IDENTIFIERS = "AUTORENAMEIDENTIFIERS"; + static final String ACTION_CACHE_ON_DISK = "CACHEONDISK"; + static final String ACTION_SET_LANGUAGE = "SETLANGUAGE"; + static final String ACTION_DISABLE_DECOMPILATION = "DISABLEDECOMPILATION"; + static final String ACTION_ASSOCIATE = "ASSOCIATE"; + static final String ACTION_GOTO_DOCUMENT_CLASS = "GOTODOCUMENTCLASS"; + static final String ACTION_PARALLEL_SPEED_UP = "PARALLELSPEEDUP"; + static final String ACTION_INTERNAL_VIEWER_SWITCH = "INTERNALVIEWERSWITCH"; + static final String ACTION_SEARCH = "SEARCH"; + static final String ACTION_TIMELINE = "TIMELINE"; + static final String ACTION_AUTO_DEOBFUSCATE = "AUTODEOBFUSCATE"; + static final String ACTION_EXIT = "EXIT"; + + static final String ACTION_RENAME_ONE_IDENTIFIER = "RENAMEONEIDENTIFIER"; + static final String ACTION_ABOUT = "ABOUT"; + static final String ACTION_SHOW_PROXY = "SHOWPROXY"; + static final String ACTION_SUB_LIMITER = "SUBLIMITER"; + static final String ACTION_SAVE = "SAVE"; + static final String ACTION_SAVE_AS = "SAVEAS"; + static final String ACTION_SAVE_AS_EXE = "SAVEASEXE"; + static final String ACTION_OPEN = "OPEN"; + static final String ACTION_CLOSE = "CLOSE"; + static final String ACTION_CLOSE_ALL = "CLOSEALL"; + static final String ACTION_EXPORT_FLA = "EXPORTFLA"; + public static final String ACTION_EXPORT_SEL = "EXPORTSEL"; + static final String ACTION_EXPORT = "EXPORT"; + static final String ACTION_IMPORT_TEXT = "IMPORTTEXT"; + static final String ACTION_CHECK_UPDATES = "CHECKUPDATES"; + static final String ACTION_HELP_US = "HELPUS"; + static final String ACTION_HOMEPAGE = "HOMEPAGE"; + static final String ACTION_RESTORE_CONTROL_FLOW = "RESTORECONTROLFLOW"; + static final String ACTION_RESTORE_CONTROL_FLOW_ALL = "RESTORECONTROLFLOWALL"; + static final String ACTION_RENAME_IDENTIFIERS = "RENAMEIDENTIFIERS"; + static final String ACTION_DEOBFUSCATE = "DEOBFUSCATE"; + static final String ACTION_DEOBFUSCATE_ALL = "DEOBFUSCATEALL"; + static final String ACTION_REMOVE_NON_SCRIPTS = "REMOVENONSCRIPTS"; + static final String ACTION_REFRESH_DECOMPILED = "REFRESHDECOMPILED"; + static final String ACTION_CLEAR_RECENT_FILES = "CLEARRECENTFILES"; + static final String ACTION_CHECK_RESOURCES = "CHECKRESOURCES"; + + private final MainFrameRibbon mainFrame; + + private JCheckBox miAutoDeobfuscation; + private JCheckBox miInternalViewer; + private JCheckBox miParallelSpeedUp; + private JCheckBox miAssociate; + private JCheckBox miDecompile; + private JCheckBox miCacheDisk; + private JCheckBox miGotoMainClassOnStartup; + private JCheckBox miAutoRenameIdentifiers; + private JCommandButton saveCommandButton; + private JCommandButton saveasCommandButton; + private JCommandButton saveasexeCommandButton; + private JCommandButton exportAllCommandButton; + private JCommandButton exportFlaCommandButton; + private JCommandButton exportSelectionCommandButton; + private JCommandButton importTextCommandButton; + + private JCommandButton reloadCommandButton; + private JCommandButton renameinvalidCommandButton; + private JCommandButton globalrenameCommandButton; + private JCommandButton deobfuscationCommandButton; + private JCommandButton searchCommandButton; + private JCommandButton timeLineCommandButton; + private JCommandButton gotoDocumentClassCommandButton; + private JCommandButton clearRecentFilesCommandButton; + + RibbonApplicationMenuEntryPrimary exportFlaMenu; + RibbonApplicationMenuEntryPrimary exportAllMenu; + RibbonApplicationMenuEntryPrimary exportSelMenu; + RibbonApplicationMenuEntryPrimary closeFileMenu; + RibbonApplicationMenuEntryPrimary closeAllFilesMenu; + + public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) { + this.mainFrame = mainFrame; + + ribbon.addTask(createFileRibbonTask()); + ribbon.addTask(createToolsRibbonTask()); + ribbon.addTask(createSettingsRibbonTask(externalFlashPlayerUnavailable)); + ribbon.addTask(createHelpRibbonTask()); + + if (Configuration.debugMode.get()) { + ribbon.addTask(createDebugRibbonTask()); + } + + ribbon.setApplicationMenu(createMainMenu()); + } + + @Override + public boolean isInternalFlashViewerSelected() { + return miInternalViewer.isSelected(); + } + + private String translate(String key) { + return mainFrame.translate(key); + } + + private void assignListener(JCommandButton b, final String command) { + final MainFrameRibbonMenu t = this; + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + t.actionPerformed(new ActionEvent(e.getSource(), 0, command)); + } + }); + } + + private String fixCommandTitle(String title) { + if (title.length() > 2) { + if (title.charAt(1) == ' ') { + title = title.charAt(0) + "\u00A0" + title.substring(2); + } + } + return title; + } + + private RibbonApplicationMenu createMainMenu() { + RibbonApplicationMenu mainMenu = new RibbonApplicationMenu(); + exportFlaMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportfla32"), translate("menu.file.export.fla"), new ActionRedirector(this, ACTION_EXPORT_FLA), JCommandButton.CommandButtonKind.ACTION_ONLY); + exportAllMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("export32"), translate("menu.file.export.all"), new ActionRedirector(this, ACTION_EXPORT), JCommandButton.CommandButtonKind.ACTION_ONLY); + exportSelMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("exportsel32"), translate("menu.file.export.selection"), new ActionRedirector(this, ACTION_EXPORT_SEL), JCommandButton.CommandButtonKind.ACTION_ONLY); + RibbonApplicationMenuEntryPrimary checkUpdatesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("update32"), translate("menu.help.checkupdates"), new ActionRedirector(this, ACTION_CHECK_UPDATES), JCommandButton.CommandButtonKind.ACTION_ONLY); + RibbonApplicationMenuEntryPrimary aboutMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("about32"), translate("menu.help.about"), new ActionRedirector(this, ACTION_ABOUT), JCommandButton.CommandButtonKind.ACTION_ONLY); + RibbonApplicationMenuEntryPrimary openFileMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("open32"), translate("menu.file.open"), new ActionRedirector(this, ACTION_OPEN), JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); + closeFileMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("close32"), translate("menu.file.close"), new ActionRedirector(this, ACTION_CLOSE), JCommandButton.CommandButtonKind.ACTION_ONLY); + closeAllFilesMenu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon("close32"), translate("menu.file.closeAll"), new ActionRedirector(this, ACTION_CLOSE_ALL), JCommandButton.CommandButtonKind.ACTION_ONLY); + openFileMenu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() { + @Override + public void menuEntryActivated(JPanel targetPanel) { + targetPanel.removeAll(); + JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM); + String groupName = translate("menu.recentFiles"); + openHistoryPanel.addButtonGroup(groupName); + List recentFiles = Configuration.getRecentFiles(); + int j = 0; + for (int i = recentFiles.size() - 1; i >= 0; i--) { + String path = recentFiles.get(i); + RecentFilesButton historyButton = new RecentFilesButton(j + " " + path, null); + historyButton.fileName = path; + historyButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + RecentFilesButton source = (RecentFilesButton) ae.getSource(); + if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) { + if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) { + Configuration.removeRecentFile(source.fileName); + } + } + } + }); + j++; + historyButton.setHorizontalAlignment(SwingUtilities.LEFT); + openHistoryPanel.addButtonToLastGroup(historyButton); + } + openHistoryPanel.setMaxButtonColumns(1); + targetPanel.setLayout(new BorderLayout()); + targetPanel.add(openHistoryPanel, BorderLayout.CENTER); + } + }); + + RibbonApplicationMenuEntryFooter exitMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon("exit32"), translate("menu.file.exit"), new ActionRedirector(this, "EXIT")); + + mainMenu.addMenuEntry(openFileMenu); + mainMenu.addMenuEntry(closeFileMenu); + mainMenu.addMenuEntry(closeAllFilesMenu); + mainMenu.addMenuSeparator(); + mainMenu.addMenuEntry(exportFlaMenu); + mainMenu.addMenuEntry(exportAllMenu); + mainMenu.addMenuEntry(exportSelMenu); + mainMenu.addMenuSeparator(); + mainMenu.addMenuEntry(checkUpdatesMenu); + mainMenu.addMenuEntry(aboutMenu); + mainMenu.addFooterEntry(exitMenu); + mainMenu.addMenuSeparator(); + + return mainMenu; + } + + private List getResizePolicies(JRibbonBand ribbonBand) { + List resizePolicies = new ArrayList<>(); + resizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel())); + resizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); + return resizePolicies; + } + + private List getIconBandResizePolicies(JRibbonBand ribbonBand) { + List resizePolicies = new ArrayList<>(); + resizePolicies.add(new BaseRibbonBandResizePolicy(ribbonBand.getControlPanel()) { + @Override + public int getPreferredWidth(int i, int i1) { + return 105; + } + + @Override + public void install(int i, int i1) { + } + }); + resizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel())); + return resizePolicies; + } + + private RibbonTask createFileRibbonTask() { + JRibbonBand editBand = new JRibbonBand(translate("menu.general"), null); + editBand.setResizePolicies(getResizePolicies(editBand)); + JCommandButton openCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.open")), View.getResizableIcon("open32")); + assignListener(openCommandButton, ACTION_OPEN); + saveCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.save")), View.getResizableIcon("save32")); + assignListener(saveCommandButton, ACTION_SAVE); + saveasCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.saveas")), View.getResizableIcon("saveas16")); + assignListener(saveasCommandButton, ACTION_SAVE_AS); + + reloadCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.reload")), View.getResizableIcon("reload16")); + assignListener(reloadCommandButton, ACTION_RELOAD); + + editBand.addCommandButton(openCommandButton, RibbonElementPriority.TOP); + editBand.addCommandButton(saveCommandButton, RibbonElementPriority.TOP); + editBand.addCommandButton(saveasCommandButton, RibbonElementPriority.MEDIUM); + editBand.addCommandButton(reloadCommandButton, RibbonElementPriority.MEDIUM); + + JRibbonBand exportBand = new JRibbonBand(translate("menu.export"), null); + exportBand.setResizePolicies(getResizePolicies(exportBand)); + exportFlaCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.fla")), View.getResizableIcon("exportfla32")); + assignListener(exportFlaCommandButton, ACTION_EXPORT_FLA); + exportAllCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.all")), View.getResizableIcon("export16")); + assignListener(exportAllCommandButton, ACTION_EXPORT); + exportSelectionCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.export.selection")), View.getResizableIcon("exportsel16")); + assignListener(exportSelectionCommandButton, ACTION_EXPORT_SEL); + saveasexeCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.saveasexe")), View.getResizableIcon("saveasexe16")); + assignListener(saveasexeCommandButton, ACTION_SAVE_AS_EXE); + + exportBand.addCommandButton(exportFlaCommandButton, RibbonElementPriority.TOP); + exportBand.addCommandButton(exportAllCommandButton, RibbonElementPriority.MEDIUM); + exportBand.addCommandButton(exportSelectionCommandButton, RibbonElementPriority.MEDIUM); + exportBand.addCommandButton(saveasexeCommandButton, RibbonElementPriority.MEDIUM); + + JRibbonBand importBand = new JRibbonBand(translate("menu.import"), null); + importBand.setResizePolicies(getResizePolicies(importBand)); + importTextCommandButton = new JCommandButton(fixCommandTitle(translate("menu.file.import.text")), View.getResizableIcon("import32")); + assignListener(importTextCommandButton, ACTION_IMPORT_TEXT); + + importBand.addCommandButton(importTextCommandButton, RibbonElementPriority.TOP); + + return new RibbonTask(translate("menu.file"), editBand, exportBand, importBand); + } + + private RibbonTask createToolsRibbonTask() { + //----------------------------------------- TOOLS ----------------------------------- + + JRibbonBand toolsBand = new JRibbonBand(translate("menu.tools"), null); + toolsBand.setResizePolicies(getResizePolicies(toolsBand)); + + searchCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.search")), View.getResizableIcon("search32")); + assignListener(searchCommandButton, ACTION_SEARCH); + + timeLineCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.timeline")), View.getResizableIcon("timeline32")); + assignListener(timeLineCommandButton, ACTION_TIMELINE); + + gotoDocumentClassCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.gotodocumentclass")), View.getResizableIcon("gotomainclass32")); + assignListener(gotoDocumentClassCommandButton, ACTION_GOTO_DOCUMENT_CLASS); + + JCommandButton proxyCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.proxy")), View.getResizableIcon("proxy16")); + assignListener(proxyCommandButton, ACTION_SHOW_PROXY); + + JCommandButton loadMemoryCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.searchmemory")), View.getResizableIcon("loadmemory16")); + assignListener(loadMemoryCommandButton, ACTION_LOAD_MEMORY); + + JCommandButton loadCacheCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.searchcache")), View.getResizableIcon("loadcache16")); + assignListener(loadCacheCommandButton, ACTION_LOAD_CACHE); + + toolsBand.addCommandButton(searchCommandButton, RibbonElementPriority.TOP); + toolsBand.addCommandButton(timeLineCommandButton, RibbonElementPriority.TOP); + toolsBand.addCommandButton(gotoDocumentClassCommandButton, RibbonElementPriority.TOP); + toolsBand.addCommandButton(proxyCommandButton, RibbonElementPriority.MEDIUM); + toolsBand.addCommandButton(loadMemoryCommandButton, RibbonElementPriority.MEDIUM); + toolsBand.addCommandButton(loadCacheCommandButton, RibbonElementPriority.MEDIUM); + if (!ProcessTools.toolsAvailable()) { + loadMemoryCommandButton.setEnabled(false); + } + JRibbonBand deobfuscationBand = new JRibbonBand(translate("menu.tools.deobfuscation"), null); + deobfuscationBand.setResizePolicies(getResizePolicies(deobfuscationBand)); + + deobfuscationCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.pcode")), View.getResizableIcon("deobfuscate32")); + assignListener(deobfuscationCommandButton, ACTION_DEOBFUSCATE); + globalrenameCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.globalrename")), View.getResizableIcon("rename16")); + assignListener(globalrenameCommandButton, ACTION_RENAME_ONE_IDENTIFIER); + renameinvalidCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.deobfuscation.renameinvalid")), View.getResizableIcon("renameall16")); + assignListener(renameinvalidCommandButton, ACTION_RENAME_IDENTIFIERS); + + deobfuscationBand.addCommandButton(deobfuscationCommandButton, RibbonElementPriority.TOP); + deobfuscationBand.addCommandButton(globalrenameCommandButton, RibbonElementPriority.MEDIUM); + deobfuscationBand.addCommandButton(renameinvalidCommandButton, RibbonElementPriority.MEDIUM); + + //JRibbonBand otherToolsBand = new JRibbonBand(translate("menu.tools.otherTools"), null); + //otherToolsBand.setResizePolicies(getResizePolicies(otherToolsBand)); + return new RibbonTask(translate("menu.tools"), toolsBand, deobfuscationBand/*, otherToolsBand*/); + } + + private RibbonTask createSettingsRibbonTask(boolean externalFlashPlayerUnavailable) { + //----------------------------------------- SETTINGS ----------------------------------- + + JRibbonBand settingsBand = new JRibbonBand(translate("menu.settings"), null); + settingsBand.setResizePolicies(getResizePolicies(settingsBand)); + + miAutoDeobfuscation = new JCheckBox(translate("menu.settings.autodeobfuscation")); + miAutoDeobfuscation.setSelected(Configuration.autoDeobfuscate.get()); + miAutoDeobfuscation.addActionListener(this); + miAutoDeobfuscation.setActionCommand(ACTION_AUTO_DEOBFUSCATE); + + miInternalViewer = new JCheckBox(translate("menu.settings.internalflashviewer")); + miInternalViewer.setSelected(Configuration.internalFlashViewer.get() || externalFlashPlayerUnavailable); + if (externalFlashPlayerUnavailable) { + miInternalViewer.setEnabled(false); + } + miInternalViewer.setActionCommand(ACTION_INTERNAL_VIEWER_SWITCH); + miInternalViewer.addActionListener(this); + + miParallelSpeedUp = new JCheckBox(translate("menu.settings.parallelspeedup")); + miParallelSpeedUp.setSelected(Configuration.parallelSpeedUp.get()); + miParallelSpeedUp.setActionCommand(ACTION_PARALLEL_SPEED_UP); + miParallelSpeedUp.addActionListener(this); + + miDecompile = new JCheckBox(translate("menu.settings.disabledecompilation")); + miDecompile.setSelected(!Configuration.decompile.get()); + miDecompile.setActionCommand(ACTION_DISABLE_DECOMPILATION); + miDecompile.addActionListener(this); + + miAssociate = new JCheckBox(translate("menu.settings.addtocontextmenu")); + miAssociate.setActionCommand(ACTION_ASSOCIATE); + miAssociate.addActionListener(this); + miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); + + miCacheDisk = new JCheckBox(translate("menu.settings.cacheOnDisk")); + miCacheDisk.setSelected(Configuration.cacheOnDisk.get()); + miCacheDisk.setActionCommand(ACTION_CACHE_ON_DISK); + miCacheDisk.addActionListener(this); + + miGotoMainClassOnStartup = new JCheckBox(translate("menu.settings.gotoMainClassOnStartup")); + miGotoMainClassOnStartup.setSelected(Configuration.gotoMainClassOnStartup.get()); + miGotoMainClassOnStartup.setActionCommand(ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP); + miGotoMainClassOnStartup.addActionListener(this); + + miAutoRenameIdentifiers = new JCheckBox(translate("menu.settings.autoRenameIdentifiers")); + miAutoRenameIdentifiers.setSelected(Configuration.autoRenameIdentifiers.get()); + miAutoRenameIdentifiers.setActionCommand(ACTION_AUTO_RENAME_IDENTIFIERS); + miAutoRenameIdentifiers.addActionListener(this); + + settingsBand.addRibbonComponent(new JRibbonComponent(miAutoDeobfuscation)); + settingsBand.addRibbonComponent(new JRibbonComponent(miInternalViewer)); + settingsBand.addRibbonComponent(new JRibbonComponent(miParallelSpeedUp)); + settingsBand.addRibbonComponent(new JRibbonComponent(miDecompile)); + if (Platform.isWindows()) { + settingsBand.addRibbonComponent(new JRibbonComponent(miAssociate)); + } + settingsBand.addRibbonComponent(new JRibbonComponent(miCacheDisk)); + settingsBand.addRibbonComponent(new JRibbonComponent(miGotoMainClassOnStartup)); + settingsBand.addRibbonComponent(new JRibbonComponent(miAutoRenameIdentifiers)); + + JRibbonBand languageBand = new JRibbonBand(translate("menu.language"), null); + List languageBandResizePolicies = getIconBandResizePolicies(languageBand); + languageBand.setResizePolicies(languageBandResizePolicies); + JCommandButton setLanguageCommandButton = new JCommandButton(fixCommandTitle(translate("menu.settings.language")), View.getResizableIcon("setlanguage32")); + assignListener(setLanguageCommandButton, ACTION_SET_LANGUAGE); + languageBand.addCommandButton(setLanguageCommandButton, RibbonElementPriority.TOP); + + JRibbonBand advancedSettingsBand = new JRibbonBand(translate("menu.advancedsettings.advancedsettings"), null); + advancedSettingsBand.setResizePolicies(getResizePolicies(advancedSettingsBand)); + JCommandButton advancedSettingsCommandButton = new JCommandButton(fixCommandTitle(translate("menu.advancedsettings.advancedsettings")), View.getResizableIcon("settings32")); + assignListener(advancedSettingsCommandButton, ACTION_ADVANCED_SETTINGS); + advancedSettingsBand.addCommandButton(advancedSettingsCommandButton, RibbonElementPriority.TOP); + + clearRecentFilesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.tools.otherTools.clearRecentFiles")), View.getResizableIcon("clearrecent16")); + assignListener(clearRecentFilesCommandButton, ACTION_CLEAR_RECENT_FILES); + advancedSettingsBand.addCommandButton(clearRecentFilesCommandButton, RibbonElementPriority.MEDIUM); + + return new RibbonTask(translate("menu.settings"), settingsBand, languageBand, advancedSettingsBand); + } + + private RibbonTask createHelpRibbonTask() { + //----------------------------------------- HELP ----------------------------------- + + JRibbonBand helpBand = new JRibbonBand(translate("menu.help"), null); + helpBand.setResizePolicies(getResizePolicies(helpBand)); + + JCommandButton checkForUpdatesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.checkupdates")), View.getResizableIcon("update16")); + assignListener(checkForUpdatesCommandButton, ACTION_CHECK_UPDATES); + JCommandButton helpUsUpdatesCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.helpus")), View.getResizableIcon("donate32")); + assignListener(helpUsUpdatesCommandButton, ACTION_HELP_US); + JCommandButton homepageCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.homepage")), View.getResizableIcon("homepage16")); + assignListener(homepageCommandButton, ACTION_HOMEPAGE); + JCommandButton aboutCommandButton = new JCommandButton(fixCommandTitle(translate("menu.help.about")), View.getResizableIcon("about32")); + assignListener(aboutCommandButton, ACTION_ABOUT); + + helpBand.addCommandButton(aboutCommandButton, RibbonElementPriority.TOP); + helpBand.addCommandButton(checkForUpdatesCommandButton, RibbonElementPriority.MEDIUM); + helpBand.addCommandButton(homepageCommandButton, RibbonElementPriority.MEDIUM); + helpBand.addCommandButton(helpUsUpdatesCommandButton, RibbonElementPriority.TOP); + return new RibbonTask(translate("menu.help"), helpBand); + } + + private RibbonTask createDebugRibbonTask() { + //----------------------------------------- DEBUG ----------------------------------- + + JRibbonBand debugBand = new JRibbonBand("Debug", null); + debugBand.setResizePolicies(getResizePolicies(debugBand)); + + JCommandButton removeNonScriptsCommandButton = new JCommandButton(fixCommandTitle("Remove non scripts"), View.getResizableIcon("update16")); + assignListener(removeNonScriptsCommandButton, ACTION_REMOVE_NON_SCRIPTS); + + JCommandButton refreshDecompiledCommandButton = new JCommandButton(fixCommandTitle("Refresh decompiled script"), View.getResizableIcon("update16")); + assignListener(refreshDecompiledCommandButton, ACTION_REFRESH_DECOMPILED); + + JCommandButton checkResourcesCommandButton = new JCommandButton(fixCommandTitle("Check resources"), View.getResizableIcon("update16")); + assignListener(checkResourcesCommandButton, ACTION_CHECK_RESOURCES); + + debugBand.addCommandButton(removeNonScriptsCommandButton, RibbonElementPriority.MEDIUM); + debugBand.addCommandButton(refreshDecompiledCommandButton, RibbonElementPriority.MEDIUM); + debugBand.addCommandButton(checkResourcesCommandButton, RibbonElementPriority.MEDIUM); + return new RibbonTask("Debug", debugBand); + } + + @Override + public void updateComponents(SWF swf, List abcList) { + boolean swfLoaded = swf != null; + boolean hasAbc = swfLoaded && abcList != null && !abcList.isEmpty(); + + exportAllMenu.setEnabled(swfLoaded); + exportFlaMenu.setEnabled(swfLoaded); + exportSelMenu.setEnabled(swfLoaded); + closeFileMenu.setEnabled(swfLoaded); + closeAllFilesMenu.setEnabled(swfLoaded); + + boolean isBundle = swfLoaded && (swf.swfList != null) && swf.swfList.isBundle; + saveCommandButton.setEnabled(swfLoaded && !isBundle); + saveasCommandButton.setEnabled(swfLoaded); + saveasexeCommandButton.setEnabled(swfLoaded); + exportAllCommandButton.setEnabled(swfLoaded); + exportFlaCommandButton.setEnabled(swfLoaded); + exportSelectionCommandButton.setEnabled(swfLoaded); + importTextCommandButton.setEnabled(swfLoaded); + reloadCommandButton.setEnabled(swfLoaded); + + renameinvalidCommandButton.setEnabled(swfLoaded); + globalrenameCommandButton.setEnabled(swfLoaded); + deobfuscationCommandButton.setEnabled(swfLoaded); + searchCommandButton.setEnabled(swfLoaded); + timeLineCommandButton.setEnabled(swfLoaded); + + gotoDocumentClassCommandButton.setEnabled(hasAbc); + deobfuscationCommandButton.setEnabled(hasAbc); + } + + private void saveAs(SWF swf, SaveFileMode mode) { + if (Main.saveFileDialog(swf, mode)) { + swf.fileTitle = null; + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + saveCommandButton.setEnabled(mainFrame.panel.getCurrentSwf() != null); + } + } + + private void clearModified(SWF swf) { + for (Tag tag : swf.tags) { + if (tag.isModified()) { + tag.createOriginalData(); + tag.setModified(false); + } + } + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_RELOAD: + if (View.showConfirmDialog(null, translate("message.confirm.reload"), translate("message.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + Main.reloadApp(); + } + break; + case ACTION_ADVANCED_SETTINGS: + Main.advancedSettings(); + break; + case ACTION_LOAD_MEMORY: + Main.loadFromMemory(); + break; + case ACTION_LOAD_CACHE: + Main.loadFromCache(); + break; + case ACTION_GOTO_DOCUMENT_CLASS_ON_STARTUP: + Configuration.gotoMainClassOnStartup.set(miGotoMainClassOnStartup.isSelected()); + break; + case ACTION_AUTO_RENAME_IDENTIFIERS: + Configuration.autoRenameIdentifiers.set(miAutoRenameIdentifiers.isSelected()); + break; + case ACTION_CACHE_ON_DISK: + Configuration.cacheOnDisk.set(miCacheDisk.isSelected()); + if (miCacheDisk.isSelected()) { + Cache.setStorageType(Cache.STORAGE_FILES); + } else { + Cache.setStorageType(Cache.STORAGE_MEMORY); + } + break; + case ACTION_SET_LANGUAGE: + new SelectLanguageDialog().display(); + break; + case ACTION_DISABLE_DECOMPILATION: + Configuration.decompile.set(!miDecompile.isSelected()); + mainFrame.panel.disableDecompilationChanged(); + break; + case ACTION_ASSOCIATE: + if (miAssociate.isSelected() == ContextMenuTools.isAddedToContextMenu()) { + return; + } + ContextMenuTools.addToContextMenu(miAssociate.isSelected(), false); + + //Update checkbox menuitem accordingly (User can cancel rights elevation) + new Timer().schedule(new TimerTask() { + @Override + public void run() { + miAssociate.setSelected(ContextMenuTools.isAddedToContextMenu()); + } + }, 1000); //It takes some time registry change to apply + break; + case ACTION_GOTO_DOCUMENT_CLASS: + mainFrame.panel.gotoDocumentClass(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_PARALLEL_SPEED_UP: + String confStr = translate("message.confirm.parallel") + "\r\n"; + if (miParallelSpeedUp.isSelected()) { + confStr += " " + translate("message.confirm.on"); + } else { + confStr += " " + translate("message.confirm.off"); + } + if (View.showConfirmDialog(null, confStr, translate("message.parallel"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.parallelSpeedUp.set((Boolean) miParallelSpeedUp.isSelected()); + } else { + miParallelSpeedUp.setSelected(!miParallelSpeedUp.isSelected()); + } + break; + case ACTION_INTERNAL_VIEWER_SWITCH: + Configuration.internalFlashViewer.set(miInternalViewer.isSelected()); + mainFrame.panel.reload(true); + break; + case ACTION_SEARCH: + mainFrame.panel.searchAs(); + break; + case ACTION_TIMELINE: + mainFrame.panel.timeline(); + break; + case ACTION_AUTO_DEOBFUSCATE: + if (View.showConfirmDialog(mainFrame.panel, translate("message.confirm.autodeobfuscate") + "\r\n" + (miAutoDeobfuscation.isSelected() ? translate("message.confirm.on") : translate("message.confirm.off")), translate("message.confirm"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + Configuration.autoDeobfuscate.set(miAutoDeobfuscation.isSelected()); + mainFrame.panel.autoDeobfuscateChanged(); + } else { + miAutoDeobfuscation.setSelected(!miAutoDeobfuscation.isSelected()); + } + break; + case ACTION_CLEAR_RECENT_FILES: + Configuration.recentFiles.set(null); + break; + case ACTION_EXIT: + mainFrame.panel.setVisible(false); + if (Main.proxyFrame != null) { + if (Main.proxyFrame.isVisible()) { + return; + } + } + Main.exit(); + break; + } + + if (Main.isWorking()) { + return; + } + + switch (e.getActionCommand()) { + case ACTION_RENAME_ONE_IDENTIFIER: + mainFrame.panel.renameOneIdentifier(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_ABOUT: + Main.about(); + break; + case ACTION_SHOW_PROXY: + Main.showProxy(); + break; + case ACTION_SUB_LIMITER: + if (e.getSource() instanceof JCheckBoxMenuItem) { + Main.setSubLimiter(((JCheckBoxMenuItem) e.getSource()).getState()); + } + break; + case ACTION_SAVE: { + SWF swf = mainFrame.panel.getCurrentSwf(); + SWFNode snode = ((TagTreeModel) mainFrame.panel.tagTree.getModel()).getSwfNode(swf); + if (snode.binaryData != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + swf.saveTo(baos); + snode.binaryData.binaryData = baos.toByteArray(); + snode.binaryData.setModified(true); + } catch (IOException ex) { + Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, "Cannot save SWF", ex); + } + } else if (swf.file == null) { + saveAs(swf, SaveFileMode.SAVEAS); + } else { + try { + Main.saveFile(swf, swf.file); + } catch (IOException ex) { + Logger.getLogger(MainFrameRibbonMenu.class.getName()).log(Level.SEVERE, null, ex); + View.showMessageDialog(null, translate("error.file.save"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + } + clearModified(swf); + } + break; + case ACTION_SAVE_AS: { + SWF swf = mainFrame.panel.getCurrentSwf(); + saveAs(swf, SaveFileMode.SAVEAS); + clearModified(swf); + } + break; + case ACTION_SAVE_AS_EXE: { + SWF swf = mainFrame.panel.getCurrentSwf(); + saveAs(swf, SaveFileMode.EXE); + } + break; + case ACTION_OPEN: + Main.openFileDialog(); + break; + case ACTION_CLOSE: + Main.closeFile(mainFrame.panel.getCurrentSwfList()); + break; + case ACTION_CLOSE_ALL: + Main.closeAll(); + break; + case ACTION_EXPORT_FLA: + mainFrame.panel.exportFla(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_IMPORT_TEXT: + mainFrame.panel.importText(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_EXPORT_SEL: + case ACTION_EXPORT: + boolean onlySel = e.getActionCommand().endsWith("SEL"); + mainFrame.panel.export(onlySel); + break; + case ACTION_CHECK_UPDATES: + if (!Main.checkForUpdates()) { + View.showMessageDialog(null, translate("update.check.nonewversion"), translate("update.check.title"), JOptionPane.INFORMATION_MESSAGE); + } + break; + case ACTION_HELP_US: + String helpUsURL = ApplicationInfo.PROJECT_PAGE + "/help_us.html"; + if (java.awt.Desktop.isDesktopSupported()) { + java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); + try { + java.net.URI uri = new java.net.URI(helpUsURL); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + View.showMessageDialog(null, translate("message.helpus").replace("%url%", helpUsURL)); + } + break; + case ACTION_HOMEPAGE: + String homePageURL = ApplicationInfo.PROJECT_PAGE; + if (java.awt.Desktop.isDesktopSupported()) { + java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); + try { + java.net.URI uri = new java.net.URI(homePageURL); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + View.showMessageDialog(null, translate("message.homepage").replace("%url%", homePageURL)); + } + break; + case ACTION_RESTORE_CONTROL_FLOW: + case ACTION_RESTORE_CONTROL_FLOW_ALL: + boolean all = e.getActionCommand().endsWith("ALL"); + mainFrame.panel.restoreControlFlow(all); + break; + case ACTION_RENAME_IDENTIFIERS: + mainFrame.panel.renameIdentifiers(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_DEOBFUSCATE: + case ACTION_DEOBFUSCATE_ALL: + mainFrame.panel.deobfuscate(); + break; + case ACTION_REMOVE_NON_SCRIPTS: + mainFrame.panel.removeNonScripts(mainFrame.panel.getCurrentSwf()); + break; + case ACTION_REFRESH_DECOMPILED: + mainFrame.panel.refreshDecompiled(); + break; + case ACTION_CHECK_RESOURCES: + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PrintStream stream = new PrintStream(os); + CheckResources.checkResources(stream); + final String str = new String(os.toByteArray(), Utf8Helper.charset); + JDialog dialog = new JDialog() { + + @Override + public void setVisible(boolean bln) { + setSize(new Dimension(800, 600)); + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + ScrollPane scrollPane = new ScrollPane(); + JEditorPane editor = new JEditorPane(); + editor.setEditable(false); + editor.setText(str); + scrollPane.add(editor); + this.add(scrollPane, BorderLayout.CENTER); + this.setModal(true); + View.centerScreen(this); + super.setVisible(bln); + } + }; + dialog.setVisible(true); + break; + } + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 1892ecd7f..149ba6063 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1,2749 +1,2742 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.SWF; -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.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -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.TextExporter; -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; -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.TextExportMode; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; -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.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.gui.abc.ABCPanel; -import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; -import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; -import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; -import com.jpexs.decompiler.flash.gui.action.ActionPanel; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.timeline.TimelineFrame; -import com.jpexs.decompiler.flash.gui.treenodes.SWFBundleNode; -import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; -import com.jpexs.decompiler.flash.gui.treenodes.StringNode; -import com.jpexs.decompiler.flash.helpers.Freed; -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.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.DefineFont3Tag; -import com.jpexs.decompiler.flash.tags.DefineFont4Tag; -import com.jpexs.decompiler.flash.tags.DefineFontTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.tags.DefineShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -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.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.text.ParseException; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.StringItem; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.treenodes.ContainerNode; -import com.jpexs.decompiler.flash.treenodes.FrameNode; -import com.jpexs.decompiler.flash.treenodes.TagNode; -import com.jpexs.decompiler.flash.treenodes.TreeNode; -import com.jpexs.decompiler.flash.types.MATRIX; -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.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Desktop; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureListener; -import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceDragEvent; -import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DragSourceEvent; -import java.awt.dnd.DragSourceListener; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.UnsupportedAudioFileException; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.Icon; -import javax.swing.JColorChooser; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.JTree; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; - -/** - * - * @author JPEXS - */ -public final class MainPanel extends JPanel implements ActionListener, TreeSelectionListener, SearchListener, Freed { - - private final MainFrame mainFrame; - private final List swfs; - private ABCPanel abcPanel; - private ActionPanel actionPanel; - private final JPanel welcomePanel; - private final MainFrameStatusPanel statusPanel; - private final MainFrameMenu mainMenu; - private final JProgressBar progressBar = new JProgressBar(0, 100); - private DeobfuscationDialog deobfuscationDialog; - public TagTree tagTree; - private final FlashPlayerPanel flashPanel; - private final JPanel contentPanel; - private final JPanel displayPanel; - private JPanel folderPreviewPanel; - private boolean isWelcomeScreen = true; - private static final String CARDPREVIEWPANEL = "Preview card"; - private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; - private static final String CARDEMPTYPANEL = "Empty card"; - private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; - private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; - private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; - private static final String DETAILCARDEMPTYPANEL = "Empty card"; - private static final String SPLIT_PANE1 = "SPLITPANE1"; - private static final String WELCOME_PANEL = "WELCOMEPANEL"; - private final JSplitPane splitPane1; - private final JSplitPane splitPane2; - private boolean splitsInited = false; - private JPanel detailPanel; - private JTextField filterField = new MyTextField(""); - private JPanel searchPanel; - private PreviewPanel previewPanel; - private AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - private CancellableWorker setSourceWorker; - public TreeNode oldNode; - - private SoundTagPlayer soundThread = null; - - public static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; - public static final String ACTION_REPLACE = "REPLACE"; - private static final String ACTION_REMOVE_ITEM = "REMOVEITEM"; - private static final String ACTION_CLOSE_SWF = "CLOSESWF"; - private static final String ACTION_EXPAND_RECURSIVE = "EXPANDRECURSIVE"; - - // play morph shape in 2 second(s) - public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; - - public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; - - private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); - - public void setPercent(int percent) { - progressBar.setValue(percent); - progressBar.setVisible(true); - } - - public void hidePercent() { - if (progressBar.isVisible()) { - progressBar.setVisible(false); - } - } - - static { - try { - File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { - tabbedPane.add(tab); - - JLabel lbl = new JLabel(title); - lbl.setIcon(icon); - lbl.setIconTextGap(5); - lbl.setHorizontalTextPosition(SwingConstants.RIGHT); - - tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); - } - - public void setStatus(String s) { - statusPanel.setStatus(s); - } - - public void setWorkStatus(String s, CancellableWorker worker) { - statusPanel.setWorkStatus(s, worker); - } - - private void createContextMenu() { - final JPopupMenu contextPopupMenu = new JPopupMenu(); - - final JMenuItem expandRecursiveMenuItem = new JMenuItem(translate("contextmenu.expandAll")); - expandRecursiveMenuItem.addActionListener(this); - expandRecursiveMenuItem.setActionCommand(ACTION_EXPAND_RECURSIVE); - contextPopupMenu.add(expandRecursiveMenuItem); - - final JMenuItem removeMenuItem = new JMenuItem(translate("contextmenu.remove")); - removeMenuItem.addActionListener(this); - removeMenuItem.setActionCommand(ACTION_REMOVE_ITEM); - contextPopupMenu.add(removeMenuItem); - - final JMenuItem exportSelectionMenuItem = new JMenuItem(translate("menu.file.export.selection")); - exportSelectionMenuItem.setActionCommand(MainFrameRibbonMenu.ACTION_EXPORT_SEL); - exportSelectionMenuItem.addActionListener(this); - contextPopupMenu.add(exportSelectionMenuItem); - - final JMenuItem replaceSelectionMenuItem = new JMenuItem(translate("button.replace")); - replaceSelectionMenuItem.setActionCommand(ACTION_REPLACE); - replaceSelectionMenuItem.addActionListener(this); - contextPopupMenu.add(replaceSelectionMenuItem); - - final JMenuItem closeSelectionMenuItem = new JMenuItem(translate("contextmenu.closeSwf")); - closeSelectionMenuItem.setActionCommand(ACTION_CLOSE_SWF); - closeSelectionMenuItem.addActionListener(this); - contextPopupMenu.add(closeSelectionMenuItem); - - final JMenu moveTagMenu = new JMenu(translate("contextmenu.moveTag")); - moveTagMenu.addActionListener(this); - contextPopupMenu.add(moveTagMenu); - - tagTree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - - int row = tagTree.getClosestRowForLocation(e.getX(), e.getY()); - int[] selectionRows = tagTree.getSelectionRows(); - if (!Helper.contains(selectionRows, row)) { - tagTree.setSelectionRow(row); - } - - TreePath[] paths = tagTree.getSelectionPaths(); - if (paths == null || paths.length == 0) { - return; - } - boolean allSelectedIsTagOrFrame = true; - for (TreePath treePath : paths) { - TreeNode treeNode = (TreeNode) treePath.getLastPathComponent(); - - TreeItem tag = treeNode.getItem(); - if (!(tag instanceof Tag) && !(tag instanceof FrameNodeItem)) { - allSelectedIsTagOrFrame = false; - break; - } - } - - replaceSelectionMenuItem.setVisible(false); - closeSelectionMenuItem.setVisible(false); - moveTagMenu.setVisible(false); - expandRecursiveMenuItem.setVisible(false); - - if (paths.length == 1) { - TreeNode treeNode = (TreeNode) paths[0].getLastPathComponent(); - - TreeItem item = ((TreeNode) treeNode).getItem(); - - if (item instanceof ImageTag && ((ImageTag) item).importSupported()) { - replaceSelectionMenuItem.setVisible(true); - } - - if (item instanceof DefineBinaryDataTag) { - replaceSelectionMenuItem.setVisible(true); - } - - if (item instanceof DefineSoundTag) { - replaceSelectionMenuItem.setVisible(true); - } - - if (treeNode instanceof SWFNode) { - closeSelectionMenuItem.setVisible(true); - } - - if (item instanceof Tag && swfs.size() > 1) { - final Tag tag = (Tag) item; - moveTagMenu.removeAll(); - for (SWFList targetSwfList : swfs) { - for (final SWF targetSwf : targetSwfList) { - if (targetSwf != tag.getSwf()) { - JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); - swfItem.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent ae) { - tag.getSwf().tags.remove(tag); - tag.setSwf(targetSwf); - targetSwf.tags.add(tag); - refreshTree(); - } - }); - moveTagMenu.add(swfItem); - } - } - } - moveTagMenu.setVisible(true); - } - - TreeModel model = tagTree.getModel(); - expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); - } - - removeMenuItem.setVisible(allSelectedIsTagOrFrame); - exportSelectionMenuItem.setEnabled(hasExportableNodes()); - contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - } - - private JPanel createWelcomePanel() { - JPanel welcomePanel = new JPanel(); - welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); - JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); - welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); - welcomeToLabel.setAlignmentX(0.5f); - JPanel appNamePanel = new JPanel(new FlowLayout()); - JLabel jpLabel = new JLabel("JPEXS "); - jpLabel.setAlignmentX(0.5f); - jpLabel.setForeground(new Color(0, 0, 160)); - jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - jpLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(jpLabel); - - JLabel ffLabel = new JLabel("Free Flash "); - ffLabel.setAlignmentX(0.5f); - ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - ffLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(ffLabel); - - JLabel decLabel = new JLabel("Decompiler"); - decLabel.setAlignmentX(0.5f); - decLabel.setForeground(Color.red); - decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(decLabel); - appNamePanel.setAlignmentX(0.5f); - welcomePanel.add(Box.createGlue()); - welcomePanel.add(welcomeToLabel); - welcomePanel.add(appNamePanel); - JLabel startLabel = new JLabel(translate("startup.selectopen")); - startLabel.setAlignmentX(0.5f); - startLabel.setFont(startLabel.getFont().deriveFont(30)); - welcomePanel.add(startLabel); - welcomePanel.add(Box.createGlue()); - return welcomePanel; - } - - private JPanel createFolderPreviewCard() { - JPanel folderPreviewCard = new JPanel(new BorderLayout()); - folderPreviewPanel = new JPanel(new WrapLayout(FlowLayout.LEFT)); - folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); - - return folderPreviewCard; - } - - public String translate(String key) { - return mainFrame.translate(key); - } - - public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel) { - super(); - - this.mainFrame = mainFrame; - this.mainMenu = mainMenu; - this.flashPanel = flashPanel; - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - - setLayout(new BorderLayout()); - swfs = new ArrayList<>(); - - detailPanel = new JPanel(); - detailPanel.setLayout(new CardLayout()); - JPanel whitePanel = new JPanel(); - whitePanel.setBackground(Color.white); - detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); - CardLayout cl2 = (CardLayout) (detailPanel.getLayout()); - cl2.show(detailPanel, DETAILCARDEMPTYPANEL); - - UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); - tagTree = new TagTree((TagTreeModel) null); - tagTree.addTreeSelectionListener(this); - - DragSource dragSource = DragSource.getDefaultDragSource(); - dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[]{DataFlavor.javaFileListFlavor}; - } - - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(DataFlavor.javaFileListFlavor); - } - - @Override - public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - if (flavor.equals(DataFlavor.javaFileListFlavor)) { - List files; - String tempDir = System.getProperty("java.io.tmpdir"); - if (!tempDir.endsWith(File.separator)) { - tempDir += File.separator; - } - Random rnd = new Random(); - tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); - File fTempDir = new File(tempDir); - if (!fTempDir.exists()) { - if (!fTempDir.mkdirs()) { - if (!fTempDir.exists()) { - throw new IOException("cannot create directory " + fTempDir); - } - } - } - - File ftemp = new File(tempDir); - ExportDialog exd = new ExportDialog(null); - files = exportSelection(errorHandler, tempDir, exd); - files.clear(); - - File[] fs = ftemp.listFiles(); - files.addAll(Arrays.asList(fs)); - - Main.stopWork(); - - for (File f : files) { - f.deleteOnExit(); - } - new File(tempDir).deleteOnExit(); - return files; - - } - return null; - } - }, new DragSourceListener() { - @Override - public void dragEnter(DragSourceDragEvent dsde) { - enableDrop(false); - } - - @Override - public void dragOver(DragSourceDragEvent dsde) { - } - - @Override - public void dropActionChanged(DragSourceDragEvent dsde) { - } - - @Override - public void dragExit(DragSourceEvent dse) { - } - - @Override - public void dragDropEnd(DragSourceDropEvent dsde) { - enableDrop(true); - } - }); - } - }); - - createContextMenu(); - - statusPanel = new MainFrameStatusPanel(this); - add(statusPanel, BorderLayout.SOUTH); - - displayPanel = new JPanel(new CardLayout()); - - previewPanel = new PreviewPanel(this, flashPanel); - displayPanel.add(previewPanel, CARDPREVIEWPANEL); - displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); - - displayPanel.add(new JPanel(), CARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); - - searchPanel = new JPanel(); - searchPanel.setLayout(new BorderLayout()); - searchPanel.add(filterField, BorderLayout.CENTER); - searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); - JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); - closeSearchButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - filterField.setText(""); - doFilter(); - searchPanel.setVisible(false); - } - }); - searchPanel.add(closeSearchButton, BorderLayout.EAST); - JPanel pan1 = new JPanel(new BorderLayout()); - pan1.add(new JScrollPane(tagTree), BorderLayout.CENTER); - pan1.add(searchPanel, BorderLayout.SOUTH); - - filterField.addActionListener(this); - - searchPanel.setVisible(false); - - filterField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - warn(); - } - - public void warn() { - doFilter(); - } - }); - - //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); - splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pan1, detailPanel); - splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel); - - welcomePanel = createWelcomePanel(); - add(welcomePanel, BorderLayout.CENTER); - - splitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - if (splitsInited) { - Configuration.guiSplitPane1DividerLocation.set((int) pce.getNewValue()); - } - } - }); - - splitPane2.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - if (detailPanel.isVisible()) { - Configuration.guiSplitPane2DividerLocation.set((int) pce.getNewValue()); - } - } - }); - - CardLayout cl3 = new CardLayout(); - contentPanel = new JPanel(cl3); - contentPanel.add(welcomePanel, WELCOME_PANEL); - contentPanel.add(splitPane1, SPLIT_PANE1); - add(contentPanel); - cl3.show(contentPanel, WELCOME_PANEL); - - tagTree.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if ((e.getKeyCode() == 'F') && (e.isControlDown())) { - searchPanel.setVisible(true); - filterField.requestFocusInWindow(); - } - } - }); - detailPanel.setVisible(false); - - updateUi(); - - //Opening files with drag&drop to main window - enableDrop(true); - } - - public void load(SWFList newSwfs, boolean first) { - - previewPanel.clear(); - - swfs.add(newSwfs); - - for (SWF swf : newSwfs) { - - - boolean hasAbc = !swf.abcList.isEmpty(); - swf.isAS3 = hasAbc; - - tagTree.setModel(new TagTreeModel(mainFrame, swfs)); - - if (hasAbc) { - if (abcPanel == null) { - abcPanel = new ABCPanel(this); - displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); - detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); - } - abcPanel.setSwf(swf); - } else { - if (actionPanel == null) { - actionPanel = new ActionPanel(this); - displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); - } - } - - expandSwfNodes(); - - for (Tag t : swf.tags) { - if (t instanceof JPEGTablesTag) { - swf.jtt = (JPEGTablesTag) t; - } - } - - if (Configuration.autoRenameIdentifiers.get()) { - try { - swf.deobfuscateIdentifiers(RenameType.TYPENUMBER); - swf.assignClassesToSymbols(); - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - } catch (InterruptedException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - showDetail(DETAILCARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); - updateUi(swf); - - /*if (first && Configuration.gotoMainClassOnStartup.get()) { - gotoDocumentClass(swf); - }*/ - first = false; - } - } - - private void updateUi(final SWF swf) { - - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - - List abcList = swf.abcList; - - boolean hasAbc = !abcList.isEmpty(); - - if (hasAbc) { - abcPanel.setSwf(swf); - } - - if (isWelcomeScreen) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, SPLIT_PANE1); - isWelcomeScreen = false; - } - - mainMenu.updateComponents(swf, abcList); - } - - private void updateUi() { - if (!isWelcomeScreen && swfs.isEmpty()) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, WELCOME_PANEL); - isWelcomeScreen = true; - } - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - mainMenu.updateComponents(null, null); - } - - public void closeAll() { - swfs.clear(); - oldNode = null; - previewPanel.clear(); - if (abcPanel != null) { - abcPanel.clearSwf(); - } - if (actionPanel != null) { - actionPanel.clearSource(); - } - updateUi(); - refreshTree(); - } - - public void close(SWFList swfList) { - swfs.remove(swfList); - if (abcPanel != null) { - for (SWF swf : swfList) { - if (abcPanel.swf == swf) { - abcPanel.clearSwf(); - } - } - } - if (actionPanel != null) { - actionPanel.clearSource(); - } - oldNode = null; - previewPanel.clear(); - updateUi(); - refreshTree(); - } - - public void enableDrop(boolean value) { - if (value) { - setDropTarget(new DropTarget() { - @Override - public synchronized void drop(DropTargetDropEvent dtde) { - try { - dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); - @SuppressWarnings("unchecked") - List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); - if (!droppedFiles.isEmpty()) { - String path = droppedFiles.get(0).getAbsolutePath(); - Main.openFile(path, null); - } - } catch (UnsupportedFlavorException | IOException ex) { - } - } - }); - } else { - setDropTarget(null); - } - } - - public void updateClassesList() { - List nodes = getASTreeNodes(tagTree); - boolean updateNeeded = false; - for (TreeNode n : nodes) { - if (n.getItem() instanceof ClassesListTreeModel) { - ((ClassesListTreeModel) n.getItem()).update(); - updateNeeded = true; - } - } - - refreshTree(); - - if (updateNeeded) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - tagTree.updateUI(); - } - }); - } - } - - public void doFilter() { - List nodes = getASTreeNodes(tagTree); - boolean updateNeeded = false; - for (TreeNode n : nodes) { - if (n.getItem() instanceof ClassesListTreeModel) { - ((ClassesListTreeModel) n.getItem()).setFilter(filterField.getText()); - updateNeeded = true; - } - } - - if (updateNeeded) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - tagTree.updateUI(); - } - }); - } - } - - @Override - public void setVisible(boolean b) { - super.setVisible(b); - if (b) { - if (abcPanel != null) { - abcPanel.initSplits(); - } - if (actionPanel != null) { - actionPanel.initSplits(); - } - - final MainPanel t = this; - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - splitPane1.setDividerLocation(Configuration.guiSplitPane1DividerLocation.get(getWidth() / 3)); - int confDivLoc = Configuration.guiSplitPane2DividerLocation.get(splitPane2.getHeight() * 3 / 5); - if (confDivLoc > splitPane2.getHeight() - 10) { //In older releases, divider location was saved when detailPanel was invisible too - confDivLoc = splitPane2.getHeight() * 3 / 5; - } - splitPane2.setDividerLocation(confDivLoc); - previewPanel.setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(previewPanel.getWidth() / 2)); - - splitPos = splitPane2.getDividerLocation(); - splitsInited = true; - previewPanel.setSplitsInited(); - } - }); - - } - } - - public static void getShapes(List list, List shapes) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getShapes(((Container) t).getSubItems(), shapes); - } - if ((t instanceof DefineShapeTag) - || (t instanceof DefineShape2Tag) - || (t instanceof DefineShape3Tag) - || (t instanceof DefineShape4Tag)) { - shapes.add((Tag) t); - } - } - } - - public static void getFonts(List list, List fonts) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getFonts(((Container) t).getSubItems(), fonts); - } - if ((t instanceof DefineFontTag) - || (t instanceof DefineFont2Tag) - || (t instanceof DefineFont3Tag) - || (t instanceof DefineFont4Tag)) { - fonts.add((Tag) t); - } - } - } - - - - public static void getMorphShapes(List list, List morphShapes) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getMorphShapes(((Container) t).getSubItems(), morphShapes); - } - if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) { - morphShapes.add((Tag) t); - } - } - } - - public static void getImages(List list, List images) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getImages(((Container) t).getSubItems(), images); - } - if ((t instanceof DefineBitsTag) - || (t instanceof DefineBitsJPEG2Tag) - || (t instanceof DefineBitsJPEG3Tag) - || (t instanceof DefineBitsJPEG4Tag) - || (t instanceof DefineBitsLosslessTag) - || (t instanceof DefineBitsLossless2Tag)) { - images.add((Tag) t); - } - } - } - - public static void getTexts(List list, List texts) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getTexts(((Container) t).getSubItems(), texts); - } - if ((t instanceof DefineTextTag) - || (t instanceof DefineText2Tag) - || (t instanceof DefineEditTextTag)) { - texts.add((Tag) t); - } - } - } - - public static void getSprites(List list, List sprites) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getSprites(((Container) t).getSubItems(), sprites); - } - if (t instanceof DefineSpriteTag) { - sprites.add((Tag) t); - } - } - } - - public static void getButtons(List list, List buttons) { - for (ContainerItem t : list) { - if (t instanceof Container) { - getButtons(((Container) t).getSubItems(), buttons); - } - if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) { - buttons.add((Tag) t); - } - } - } - - public List getSelectedNodes() { - List ret = new ArrayList<>(); - TreePath[] tps = tagTree.getSelectionPaths(); - if (tps == null) { - return ret; - } - for (TreePath tp : tps) { - TagNode te = (TagNode) tp.getLastPathComponent(); - ret.add(te); - } - return ret; - } - - public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { - String oldName = identifier; - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - swf.renameAS2Identifier(oldName, newName); - View.showMessageDialog(null, translate("rename.finished.identifier")); - updateClassesList(); - reload(true); - } - } - } - - public void renameMultiname(List abcList, int multiNameIndex) { - String oldName = ""; - if (abcPanel.abc.constants.getMultiname(multiNameIndex).name_index > 0) { - oldName = abcPanel.abc.constants.getString(abcPanel.abc.constants.getMultiname(multiNameIndex).name_index); - } - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - int mulCount = 0; - for (ABCContainerTag cnt : abcList) { - ABC abc = cnt.getABC(); - for (int m = 1; m < abc.constants.getMultinameCount(); m++) { - int ni = abc.constants.getMultiname(m).name_index; - String n = ""; - if (ni > 0) { - n = abc.constants.getString(ni); - } - if (n.equals(oldName)) { - abc.renameMultiname(m, newName); - mulCount++; - } - } - } - View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", "" + mulCount)); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - reload(true); - abcPanel.hilightScript(abcPanel.swf, abcPanel.decompiledTextArea.getScriptLeaf().getPath().toString()); - } - } - } - - public List getSelected(JTree tree) { - TreeSelectionModel tsm = tree.getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); - ret.add(treeNode); - } - return ret; - } - - public List getAllSubs(JTree tree, TreeNode o) { - TagTreeModel tm = (TagTreeModel) tree.getModel(); - List ret = new ArrayList<>(); - for (int i = 0; i < tm.getChildCount(o); i++) { - TreeNode c = tm.getChild(o, i); - ret.add(c); - ret.addAll(getAllSubs(tree, c)); - } - return ret; - } - - public List getAllSelected(TagTree tree) { - TreeSelectionModel tsm = tree.getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); - ret.add(treeNode); - ret.addAll(getAllSubs(tree, treeNode)); - } - return ret; - } - - public List getASTreeNodes(TagTree tree) { - List result = new ArrayList<>(); - TagTreeModel tm = (TagTreeModel) tree.getModel(); - if (tm == null) { - return result; - } - TreeNode root = tm.getRoot(); - for (int i = 0; i < tm.getChildCount(root); i++) { - // first level node can be SWFNode and SWFBundleNode - TreeNode node = tm.getChild(root, i); - if (node instanceof SWFBundleNode) { - for (int j = 0; j < tm.getChildCount(node); j++) { - // child of SWFBundleNode should be SWFNode - SWFNode swfNode = (SWFNode) tm.getChild(node, j); - result.add(swfNode.scriptsNode); - } - } else if (node instanceof SWFNode) { - SWFNode swfNode = (SWFNode) tm.getChild(root, i); - result.add(swfNode.scriptsNode); - } - } - return result; - } - - public boolean confirmExperimental() { - return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; - } - private SearchDialog searchDialog; - - public boolean hasExportableNodes() { - return !getSelection(getCurrentSwf()).isEmpty(); - } - - private List getSelection(SWF swf) { - List ret = new ArrayList<>(); - List sel = getAllSelected(tagTree); - for (TreeNode d : sel) { - if (d.getItem().getSwf() != swf) { - continue; - } - if (d instanceof ContainerNode) { - ContainerNode n = (ContainerNode) d; - TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); - if (nodeType == TreeNodeType.IMAGE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SHAPE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.AS) { - ret.add(n); - } - if (nodeType == TreeNodeType.MOVIE) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SOUND) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.TEXT) { - ret.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.FONT) { - ret.add((Tag) n.getItem()); - } - } - if (d instanceof FrameNode) { - FrameNode fn = (FrameNode) d; - if (!fn.scriptsNode) { - ret.add(d.getItem()); - } - } - if (d instanceof TreeElement) { - if (((TreeElement) d).isLeaf()) { - TreeElement treeElement = (TreeElement) d; - ret.add((ScriptPack) treeElement.getItem()); - } - } - } - return ret; - } - - public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException { - - List ret = new ArrayList<>(); - List sel = getAllSelected(tagTree); - - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - List as3scripts = new ArrayList<>(); - List images = new ArrayList<>(); - List shapes = new ArrayList<>(); - List morphshapes = new ArrayList<>(); - List movies = new ArrayList<>(); - List sounds = new ArrayList<>(); - List texts = new ArrayList<>(); - List as12scripts = new ArrayList<>(); - List binaryData = new ArrayList<>(); - Map> frames = new HashMap<>(); - List fonts = new ArrayList<>(); - - for (TreeNode d : sel) { - if (d.getItem().getSwf() != swf) { - continue; - } - if (d instanceof ContainerNode) { - ContainerNode n = (ContainerNode) d; - TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); - if (nodeType == TreeNodeType.IMAGE) { - images.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SHAPE) { - shapes.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - morphshapes.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.AS) { - as12scripts.add(n); - } - if (nodeType == TreeNodeType.MOVIE) { - movies.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.SOUND) { - sounds.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - binaryData.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.TEXT) { - texts.add((Tag) n.getItem()); - } - if (nodeType == TreeNodeType.FONT) { - fonts.add((Tag) n.getItem()); - } - } - if (d instanceof FrameNode) { - FrameNode fn = (FrameNode) d; - if (!fn.scriptsNode) { - - FrameNodeItem fni = (FrameNodeItem) d.getItem(); - Tag par = fni.getParent(); - int frame = fni.getFrame() - 1; //Fix to zero based - int parentId = 0; - if (par != null) { - parentId = ((CharacterTag) par).getCharacterId(); - } - if (!frames.containsKey(parentId)) { - frames.put(parentId, new ArrayList()); - } - frames.get(parentId).add(frame); - } - } - if (d instanceof TreeElement) { - if (((TreeElement) d).isLeaf()) { - TreeElement treeElement = (TreeElement) d; - as3scripts.add((ScriptPack) treeElement.getItem()); - } - } - } - - if (selFile == null) { - selFile = selectExportDir(); - if (selFile == null) { - return new ArrayList<>(); - } - } - - final ScriptExportMode scriptMode = export.getValue(ScriptExportMode.class); - ret.addAll(new ImageExporter().exportImages(handler, selFile + File.separator + "images", images, - new ImageExportSettings(export.getValue(ImageExportMode.class)))); - ret.addAll(new ShapeExporter().exportShapes(handler, selFile + File.separator + "shapes", shapes, - new ShapeExportSettings(export.getValue(ShapeExportMode.class)))); - ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + "morphshapes", morphshapes, - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class)))); - ret.addAll(new TextExporter().exportTexts(handler, selFile + File.separator + "texts", texts, - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get()))); - ret.addAll(new MovieExporter().exportMovies(handler, selFile + File.separator + "movies", movies, - new MovieExportSettings(export.getValue(MovieExportMode.class)))); - ret.addAll(new SoundExporter().exportSounds(handler, selFile + File.separator + "sounds", sounds, - new SoundExportSettings(export.getValue(SoundExportMode.class)))); - ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + "binaryData", binaryData, - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)))); - ret.addAll(new FontExporter().exportFonts(handler, selFile + File.separator + "fonts", fonts, - new FontExportSettings(export.getValue(FontExportMode.class)))); - - for (Entry> entry : frames.entrySet()) { - ret.addAll(swf.exportFrames(handler, selFile + File.separator + "frames", entry.getKey(), entry.getValue(), - new FramesExportSettings(export.getValue(FramesExportMode.class)))); - } - List abcList = swf.abcList; - if (abcPanel != null) { - for (int i = 0; i < as3scripts.size(); i++) { - ScriptPack tls = as3scripts.get(i); - Main.startWork(translate("work.exporting") + " " + (i + 1) + "/" + as3scripts.size() + " " + tls.getPath() + " ..."); - ret.add(tls.export(selFile, abcList, scriptMode, Configuration.parallelSpeedUp.get())); - } - } else { - List allNodes = new ArrayList<>(); - List allAs12Scripts = new ArrayList<>(); - - if (abcPanel == null) { - allAs12Scripts = getASTreeNodes(tagTree); - } - for (TreeNode asn : allAs12Scripts) { - allNodes.add(asn); - TagNode.setExport(allNodes, false); - TagNode.setExport(as12scripts, true); - ret.addAll(TagNode.exportNodeAS(handler, allNodes, selFile, scriptMode, null)); - } - } - } - } - return ret; - } - - public SWFList getCurrentSwfList() { - SWF swf = getCurrentSwf(); - if (swf == null) { - return null; - } - - return swf.swfList; - } - - public SWF getCurrentSwf() { - if (swfs == null || swfs.isEmpty()) { - return null; - } - - TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); - if (treeNode == null) { - return swfs.get(0).get(0); - } - - if (treeNode instanceof SWFBundleNode) { - return null; - } - - return treeNode.getItem().getSwf(); - } - - private void clearCache() { - if (abcPanel != null) { - abcPanel.decompiledTextArea.clearScriptCache(); - } - if (actionPanel != null) { - actionPanel.clearCache(); - } - } - - public void gotoDocumentClass(SWF swf) { - if (swf == null) { - return; - } - String documentClass = null; - loopdc: - for (Tag t : swf.tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t; - for (int i = 0; i < sc.tags.length; i++) { - if (sc.tags[i] == 0) { - documentClass = sc.names[i]; - break loopdc; - } - } - } - } - if (documentClass != null) { - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); - abcPanel.setSwf(swf); - abcPanel.hilightScript(swf, documentClass); - } - } - - public void disableDecompilationChanged() { - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - reload(true); - updateClassesList(); - } - - public void searchAs() { - if (searchDialog == null) { - searchDialog = new SearchDialog(); - } - searchDialog.setVisible(true); - if (searchDialog.result) { - final String txt = searchDialog.searchField.getText(); - if (!txt.isEmpty()) { - final SWF swf = getCurrentSwf(); - - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - boolean found = false; - if (searchDialog.searchInASRadioButton.isSelected()) { - if (swf.isAS3) { - if (abcPanel != null && abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { - found = true; - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); - } - }); - } - } else { - if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { - found = true; - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - showCard(CARDACTIONSCRIPTPANEL); - } - }); - } - } - } else if (searchDialog.searchInTextsRadioButton.isSelected()) { - if (searchText(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected(), swf)) { - found = true; - } - } - - if (!found) { - View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - - return null; - } - }.execute(); - } - } - } - - private boolean searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { - if ((txt != null) && (!txt.isEmpty())) { - SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); - textSearchPanel.setOptions(ignoreCase, regexp); - List found = new ArrayList<>(); - Pattern pat = null; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); - } - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (pat.matcher(textTag.getFormattedText()).find()) { - found.add(textTag); - } - } - } - textSearchPanel.setSearchText(txt); - return textSearchPanel.setResults(found); - } - return false; - } - - @Override - public void updateSearchPos(TextTag item) { - setTreeItem(item); - previewPanel.getTextPanel().updateSearchPos(); - } - - public void setTreeItem(TreeItem treeItem) { - TagTreeModel ttm = (TagTreeModel) tagTree.getModel(); - TreePath tp = ttm.getTagPath(treeItem); - if (tp != null) { - tagTree.setSelectionPath(tp); - tagTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void autoDeobfuscateChanged() { - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - reload(true); - updateClassesList(); - } - - public void renameOneIdentifier(final SWF swf) { - if (swf.fileAttributes != null && swf.fileAttributes.actionScript3) { - final int multiName = abcPanel.decompiledTextArea.getMultinameUnderCursor(); - final List abcList = swf.abcList; - if (multiName > 0) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - Main.startWork(translate("work.renaming") + "..."); - renameMultiname(abcList, multiName); - return null; - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - - } else { - View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } else { - final String identifier = actionPanel.getStringUnderCursor(); - if (identifier != null) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - Main.startWork(translate("work.renaming") + "..."); - try { - renameIdentifier(swf, identifier); - } catch (InterruptedException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - } else { - View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - } - - public void exportFla(final SWF swf) { - JFileChooser fc = new JFileChooser(); - String selDir = Configuration.lastOpenDir.get(); - fc.setCurrentDirectory(new File(selDir)); - if (!selDir.endsWith(File.separator)) { - selDir += File.separator; - } - String fileName = new File(swf.file).getName(); - fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; - fc.setSelectedFile(new File(selDir + fileName)); - List flaFilters = new ArrayList<>(); - List xflFilters = new ArrayList<>(); - List versions = new ArrayList<>(); - FileFilter defaultFilter = null; - for (int i = FLAVersion.values().length - 1; i >= 0; i--) { - final FLAVersion v = FLAVersion.values()[i]; - if (!swf.isAS3 && v.minASVersion() > 2) { - //This version does not support AS1/2 - } else { - versions.add(v); - FileFilter f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); - } - - @Override - public String getDescription() { - return translate("filter.fla").replace("%version%", v.applicationName()); - } - }; - if (v == FLAVersion.CS6) { - defaultFilter = f; - fc.setFileFilter(f); - } else { - fc.addChoosableFileFilter(f); - } - flaFilters.add(f); - f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); - } - - @Override - public String getDescription() { - return translate("filter.xfl").replace("%version%", v.applicationName()); - } - }; - fc.addChoosableFileFilter(f); - xflFilters.add(f); - } - } - if (defaultFilter == null) { - defaultFilter = flaFilters.get(0); - } - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showSaveDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File sf = Helper.fixDialogFile(fc.getSelectedFile()); - - Main.startWork(translate("work.exporting.fla") + "..."); - final boolean compressed = flaFilters.contains(fc.getFileFilter()); - if (!compressed) { - if (sf.getName().endsWith(".fla")) { - sf = new File(sf.getAbsolutePath().substring(0, sf.getAbsolutePath().length() - 4) + ".xfl"); - } - } - final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(fc.getFileFilter()) : xflFilters.indexOf(fc.getFileFilter())); - final File selfile = sf; - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - Helper.freeMem(); - try { - if (compressed) { - swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } else { - swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } - } catch (IOException ex) { - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); - } - Helper.freeMem(); - return null; - } - - @Override - protected void done() { - Main.stopWork(); - if (Configuration.openFolderAfterFlaExport.get()) { - try { - Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); - } catch (IOException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - }.execute(); - } - } - - private Map splitTextRecords(String texts) { - String[] textsArr = texts.split(Helper.newLine + Configuration.textExportSingleFileSeparator.get() + Helper.newLine); - String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine; - Map result = new HashMap<>(); - for (String text : textsArr) { - String[] textArr = text.split(Helper.newLine, 2); - String idLine = textArr[0]; - if (idLine.startsWith("ID:")) { - int id = Integer.parseInt(idLine.substring(3).trim()); - String[] records = textArr[1].split(recordSeparator); - result.put(id, records); - } else { - if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return null; - } - } - } - return result; - } - - private void importTextsSingleFile(File textsFile, SWF swf) { - String texts = Helper.readTextFile(textsFile.getPath()); - Map records = splitTextRecords(texts); - if (records != null) { - for (int characterId : records.keySet()) { - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (textTag.getCharacterId() == characterId) { - String[] currentRecords = records.get(characterId); - String text = textTag.getFormattedText(); - if (!saveText(textTag, text, currentRecords)) { - if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - } - break; - } - } - } - } - } - } - - private void importTextsSingleFileFormatted(File textsFile, SWF swf) { - String texts = Helper.readTextFile(textsFile.getPath()); - Map records = splitTextRecords(texts); - if (records != null) { - for (int characterId : records.keySet()) { - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (textTag.getCharacterId() == characterId) { - String[] currentRecords = records.get(characterId); - if (!saveText(textTag, currentRecords[0], null)) { - if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - } - break; - } - } - } - } - } - } - - private void importTextsMultipleFiles(String folder, SWF swf) { - File textsFolder = new File(Path.combine(folder, TextExporter.TEXT_EXPORT_FOLDER)); - String[] files = textsFolder.list(new FilenameFilter() { - - private Pattern pat = Pattern.compile("\\d+\\.txt", Pattern.CASE_INSENSITIVE); - - @Override - public boolean accept(File dir, String name) { - - return pat.matcher(name).matches(); - } - }); - - for (String fileName : files) { - String texts = Helper.readTextFile(Path.combine(textsFolder.getPath(), fileName)); - int characterId = Integer.parseInt(fileName.split("\\.")[0]); - String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine; - boolean formatted = !texts.contains(recordSeparator) && texts.startsWith("[" + Helper.newLine); - if (!formatted) { - String[] records = texts.split(recordSeparator); - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (textTag.getCharacterId() == characterId) { - String text = textTag.getFormattedText(); - if (!saveText(textTag, text, records)) { - if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - } - break; - } - } - } - } else { - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (textTag.getCharacterId() == characterId) { - if (!saveText(textTag, texts, null)) { - if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - } - break; - } - } - } - } - } - } - - public void importText(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - File textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); - // try to import formatted texts - if (textsFile.exists()) { - importTextsSingleFileFormatted(textsFile, swf); - } else { - textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); - // try to import plain texts - if (textsFile.exists()) { - importTextsSingleFile(textsFile, swf); - } else { - importTextsMultipleFiles(selFile, swf); - } - } - - SWF.clearImageCache(); - reload(true); - } - } - - private String selectExportDir() { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("export.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - Main.startWork(translate("work.exporting") + "..."); - final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); - return selFile; - } - return null; - } - - public void export(final boolean onlySel) { - - final SWF swf = getCurrentSwf(); - List sel = getSelection(swf); - if (!onlySel) { - sel = null; - } else { - if (sel.isEmpty()) { - return; - } - } - final ExportDialog export = new ExportDialog(sel); - export.setVisible(true); - if (!export.cancelled) { - final String selFile = selectExportDir(); - if (selFile != null) { - final long timeBefore = System.currentTimeMillis(); - Main.startWork(translate("work.exporting") + "..."); - final ScriptExportMode exportMode = export.getValue(ScriptExportMode.class); - - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - try { - if (onlySel) { - exportSelection(errorHandler, selFile, export); - } else { - swf.exportImages(errorHandler, selFile + File.separator + "images", - new ImageExportSettings(export.getValue(ImageExportMode.class))); - swf.exportShapes(errorHandler, selFile + File.separator + "shapes", - new ShapeExportSettings(export.getValue(ShapeExportMode.class))); - swf.exportMorphShapes(errorHandler, selFile + File.separator + "morphshapes", - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class))); - swf.exportTexts(errorHandler, selFile + File.separator + "texts", - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get())); - swf.exportMovies(errorHandler, selFile + File.separator + "movies", - new MovieExportSettings(export.getValue(MovieExportMode.class))); - swf.exportSounds(errorHandler, selFile + File.separator + "sounds", - new SoundExportSettings(export.getValue(SoundExportMode.class))); - swf.exportBinaryData(errorHandler, selFile + File.separator + "binaryData", - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class))); - swf.exportFonts(errorHandler, selFile + File.separator + "fonts", - new FontExportSettings(export.getValue(FontExportMode.class))); - swf.exportFrames(errorHandler, selFile + File.separator + "frames", 0, null, - new FramesExportSettings(export.getValue(FramesExportMode.class))); - for (CharacterTag c : swf.characters.values()) { - if (c instanceof DefineSpriteTag) { - swf.exportFrames(errorHandler, selFile + File.separator + "frames", c.getCharacterId(), null, - new FramesExportSettings(export.getValue(FramesExportMode.class))); - } - } - swf.exportActionScript(errorHandler, selFile, exportMode, Configuration.parallelSpeedUp.get()); - } - } catch (Exception ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error during export", ex); - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); - } - return null; - } - - @Override - protected void done() { - Main.stopWork(); - long timeAfter = System.currentTimeMillis(); - final long timeMs = timeAfter - timeBefore; - - View.execInEventDispatchLater(new Runnable() { - - @Override - public void run() { - setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); - } - }); - } - }.execute(); - - } - } - } - - public void restoreControlFlow(final boolean all) { - Main.startWork(translate("work.restoringControlFlow")); - if ((!all) || confirmExperimental()) { - new CancellableWorker() { - @Override - protected Object doInBackground() throws Exception { - int cnt = 0; - if (all) { - for (ABCContainerTag tag : abcPanel.swf.abcList) { - tag.getABC().restoreControlFlow(); - } - } else { - int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - if (bi != -1) { - abcPanel.abc.bodies.get(bi).restoreControlFlow(abcPanel.abc.constants, abcPanel.decompiledTextArea.getCurrentTrait(), abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, abcPanel.decompiledTextArea.getCurrentTrait()); - } - return true; - } - - @Override - protected void done() { - Main.stopWork(); - View.showMessageDialog(null, translate("work.restoringControlFlow.complete")); - - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - abcPanel.reload(); - updateClassesList(); - } - }); - } - }.execute(); - } - } - - public void renameIdentifiers(final SWF swf) { - if (confirmExperimental()) { - final RenameType renameType = new RenameDialog().display(); - if (renameType != null) { - Main.startWork(translate("work.renaming.identifiers") + "..."); - new CancellableWorker() { - @Override - protected Integer doInBackground() throws Exception { - int cnt = swf.deobfuscateIdentifiers(renameType); - return cnt; - } - - @Override - protected void done() { - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - try { - int cnt = get(); - Main.stopWork(); - View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt)); - swf.assignClassesToSymbols(); - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - reload(true); - } catch (Exception ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex); - Main.stopWork(); - View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); - } - } - }); - } - }.execute(); - } - } - } - - public void deobfuscate() { - if (deobfuscationDialog == null) { - deobfuscationDialog = new DeobfuscationDialog(); - } - deobfuscationDialog.setVisible(true); - if (deobfuscationDialog.ok) { - Main.startWork(translate("work.deobfuscating") + "..."); - new CancellableWorker() { - @Override - protected Object doInBackground() throws Exception { - try { - if (deobfuscationDialog.processAllCheckbox.isSelected()) { - for (ABCContainerTag tag : abcPanel.swf.abcList) { - if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { - tag.getABC().removeDeadCode(); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { - tag.getABC().removeTraps(); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { - tag.getABC().removeTraps(); - tag.getABC().restoreControlFlow(); - } - } - } else { - int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); - if (bi != -1) { - if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { - abcPanel.abc.bodies.get(bi).removeDeadCode(abcPanel.abc.constants, t, abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { - abcPanel.abc.bodies.get(bi).removeTraps(abcPanel.abc.constants, abcPanel.abc, t, abcPanel.decompiledTextArea.getScriptLeaf().scriptIndex, abcPanel.decompiledTextArea.getClassIndex(), abcPanel.decompiledTextArea.getIsStatic(), ""/*FIXME*/); - } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { - abcPanel.abc.bodies.get(bi).removeTraps(abcPanel.abc.constants, abcPanel.abc, t, abcPanel.decompiledTextArea.getScriptLeaf().scriptIndex, abcPanel.decompiledTextArea.getClassIndex(), abcPanel.decompiledTextArea.getIsStatic(), ""/*FIXME*/); - abcPanel.abc.bodies.get(bi).restoreControlFlow(abcPanel.abc.constants, t, abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); - } - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, t); - } - } catch (Exception ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Deobfuscation error", ex); - } - return true; - } - - @Override - protected void done() { - Main.stopWork(); - View.showMessageDialog(null, translate("work.deobfuscating.complete")); - - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - clearCache(); - abcPanel.reload(); - updateClassesList(); - } - }); - } - }.execute(); - } - } - - public void removeNonScripts(SWF swf) { - List tags = new ArrayList<>(swf.tags); - for (Tag tag : tags) { - System.out.println(tag.getClass()); - if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { - swf.removeTag(tag); - } - } - refreshTree(); - } - - public void refreshTree() { - previewPanel.clear(); - showCard(CARDEMPTYPANEL); - TreeItem treeItem = tagTree.getCurrentTreeItem(); - for (SWFList sWFList : swfs) { - for (SWF swf : sWFList.swfs) { - swf.updateInnerTagsForShowFrameTags(); - } - } - View.refreshTree(tagTree, new TagTreeModel(mainFrame, swfs)); - if (treeItem != null) { - setTreeItem(treeItem); - } - } - - public void refreshDecompiled() { - clearCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - reload(true); - updateClassesList(); - } - - public boolean saveText(TextTag textTag, String formattedText, String[] texts) { - try { - if (textTag.setFormattedText(new MissingCharacterHandler() { - @Override - public boolean handle(FontTag font, char character) { - String fontName = font.getSwf().sourceFontsMap.get(font.getFontId()); - if (fontName == null) { - fontName = font.getFontName(); - } - fontName = FontTag.findInstalledFontName(fontName); - Font f = new Font(fontName, font.getFontStyle(), 18); - if (!f.canDisplay(character)) { - View.showMessageDialog(null, translate("error.font.nocharacter").replace("%char%", "" + character), translate("error"), JOptionPane.ERROR_MESSAGE); - return false; - } - font.addCharacter(character, fontName); - return true; - - } - }, formattedText, texts)) { - textTag.setModified(true); - return true; - } - } catch (ParseException ex) { - View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", "" + ex.line), translate("error"), JOptionPane.ERROR_MESSAGE); - } - return false; - } - - public void removeTag(Tag tag) { - tag.getSwf().removeTag(tag); - refreshTree(); - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_SELECT_BKCOLOR: - Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.swfBackgroundColor); - if (newColor != null) { - View.swfBackgroundColor = newColor; - reload(true); - } - break; - case ACTION_REPLACE: { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof DefineSoundTag) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - fc.setFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".mp3")) - || (f.getName().toLowerCase().endsWith(".wav")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds"); - } - }); - fc.addChoosableFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".mp3")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds.mp3"); - } - }); - fc.addChoosableFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".wav")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.sounds.wav"); - } - }); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); - DefineSoundTag ds = (DefineSoundTag) item; - int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; - if (selfile.getName().toLowerCase().endsWith(".mp3")) { - soundFormat = SoundFormat.FORMAT_MP3; - } - boolean ok = false; - try { - ok = ds.setSound(new FileInputStream(selfile), soundFormat); - } catch (IOException ex) { - //ignore - } - if (!ok) { - View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } else { - reload(true); - } - } - } - if (item instanceof ImageTag) { - ImageTag it = (ImageTag) item; - if (it.importSupported()) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - fc.setFileFilter(new FileFilter() { - @Override - public boolean accept(File f) { - return (f.getName().toLowerCase().endsWith(".jpg")) - || (f.getName().toLowerCase().endsWith(".jpeg")) - || (f.getName().toLowerCase().endsWith(".gif")) - || (f.getName().toLowerCase().endsWith(".png")) - || (f.isDirectory()); - } - - @Override - public String getDescription() { - return translate("filter.images"); - } - }); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - SWF swf = it.getSwf(); - if (it instanceof DefineBitsTag) { - DefineBitsJPEG2Tag jpeg2Tag = new DefineBitsJPEG2Tag(swf, it.getOriginalHeaderData(), it.getOriginalData(), it.getPos(), it.getCharacterId(), data); - jpeg2Tag.setModified(true); - swf.tags.set(swf.tags.indexOf(it), jpeg2Tag); - swf.updateCharacters(); - refreshTree(); - setTreeItem(jpeg2Tag); - } else { - it.setImage(data); - } - SWF.clearImageCache(); - } catch (IOException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - reload(true); - } - } - } - if (item instanceof DefineBinaryDataTag) { - DefineBinaryDataTag bt = (DefineBinaryDataTag) item; - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File selfile = Helper.fixDialogFile(fc.getSelectedFile()); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - bt.binaryData = data; - bt.setModified(true); - reload(true); - } - } - } - break; - case ACTION_EXPAND_RECURSIVE: { - TreePath path = tagTree.getSelectionPath(); - if (path == null) { - return; - } - View.expandTreeNodesRecursive(tagTree, path, true); - } - break; - case ACTION_REMOVE_ITEM: - List sel = getSelected(tagTree); - - List tagsToRemove = new ArrayList<>(); - for (TreeNode o : sel) { - TreeItem tag = o.getItem(); - if (tag instanceof Tag) { - tagsToRemove.add((Tag) tag); - } else if (tag instanceof FrameNodeItem) { - FrameNodeItem frame = (FrameNodeItem) tag; - tagsToRemove.add(frame.getShowFrameTag()); - } - } - - if (tagsToRemove.size() == 1) { - Tag tag = tagsToRemove.get(0); - if (View.showConfirmDialog(this, translate("message.confirm.remove").replace("%item%", tag.toString()), translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - tag.getSwf().removeTag(tag); - refreshTree(); - } - } else if (tagsToRemove.size() > 1) { - if (View.showConfirmDialog(this, translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())), translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { - for (Tag tag : tagsToRemove) { - tag.getSwf().removeTag(tag); - } - refreshTree(); - } - } - break; - case ACTION_CLOSE_SWF: { - Main.closeFile(getCurrentSwfList()); - } - } - if (Main.isWorking()) { - return; - } - - switch (e.getActionCommand()) { - - case MainFrameRibbonMenu.ACTION_EXPORT_SEL: - export(true); - break; - } - } - - private int splitPos = 0; - - public void showDetail(String card) { - CardLayout cl = (CardLayout) (detailPanel.getLayout()); - cl.show(detailPanel, card); - if (card.equals(DETAILCARDEMPTYPANEL)) { - if (detailPanel.isVisible()) { - splitPos = splitPane2.getDividerLocation(); - detailPanel.setVisible(false); - } - } else { - if (!detailPanel.isVisible()) { - detailPanel.setVisible(true); - splitPane2.setDividerLocation(splitPos); - } - } - - } - - public void showCard(String card) { - CardLayout cl = (CardLayout) (displayPanel.getLayout()); - cl.show(displayPanel, card); - } - - @Override - public void valueChanged(TreeSelectionEvent e) { - TreeNode treeNode = (TreeNode) e.getPath().getLastPathComponent(); - TreeItem treeItem = treeNode.getItem(); - if (!(treeItem instanceof SWFList)) { - SWF swf = treeItem.getSwf(); - /*if (swfs.contains(swf.swfList)) { //why? - updateUi(swf); - } else { - updateUi(); - }*/ - updateUi(swf); - } else { - updateUi(); - } - previewPanel.setEditText(false); - reload(false); - } - - public void unloadFlashPlayer() { - if (flashPanel != null) { - try { - flashPanel.close(); - } catch (IOException ex) { - // ignore - } - } - } - - private void stopFlashPlayer() { - if (flashPanel != null) { - if (!flashPanel.isStopped()) { - flashPanel.stopSWF(); - } - } - } - - public boolean isInternalFlashViewerSelected() { - return mainMenu.isInternalFlashViewerSelected(); - } - - public void reload(boolean forceReload) { - TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); - if (treeNode == null) { - return; - } - - if (!forceReload && (treeNode == oldNode)) { - return; - } - - oldNode = treeNode; - - TreeItem tagObj = treeNode.getItem(); - - if (flashPanel != null) { - flashPanel.specialPlayback = false; - } - previewPanel.clear(); - if (soundThread != null) { - soundThread.pause(); - } - stopFlashPlayer(); - if (tagObj instanceof ScriptPack) { - final ScriptPack scriptLeaf = (ScriptPack) tagObj; - final List abcList = scriptLeaf.abc.swf.abcList; - if (setSourceWorker != null) { - setSourceWorker.cancel(true); - setSourceWorker = null; - } - if (!Main.isWorking()) { - Main.startWork(AppStrings.translate("work.decompiling") + "..."); - CancellableWorker worker = new CancellableWorker() { - - @Override - protected Void doInBackground() throws Exception { - int classIndex = -1; - for (Trait t : scriptLeaf.abc.script_info.get(scriptLeaf.scriptIndex).traits.traits) { - if (t instanceof TraitClass) { - classIndex = ((TraitClass) t).class_info; - break; - } - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); - abcPanel.navigator.setABC(abcList, scriptLeaf.abc); - abcPanel.navigator.setClassIndex(classIndex, scriptLeaf.scriptIndex); - abcPanel.setAbc(scriptLeaf.abc); - abcPanel.decompiledTextArea.setScript(scriptLeaf, abcList); - abcPanel.decompiledTextArea.setClassIndex(classIndex); - abcPanel.decompiledTextArea.setNoTrait(); - return null; - } - - @Override - protected void done() { - Main.stopWork(); - - View.execInEventDispatch(new Runnable() { - - @Override - public void run() { - try { - get(); - } catch (CancellationException ex) { - abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled")); - } catch (Exception ex) { - abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("decompilationError") + ": " + ex); - } - } - }); - } - }; - worker.execute(); - setSourceWorker = worker; - Main.startWork(translate("work.decompiling") + "...", worker); - } - - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); - return; - } else { - showDetail(DETAILCARDEMPTYPANEL); - } - - previewPanel.setImageReplaceButtonVisible(false); - - if (treeNode instanceof StringNode) { - showCard(CARDFOLDERPREVIEWPANEL); - showFolderPreview(treeNode); - } else if (treeNode instanceof SWFNode) { - SWFNode swfNode = (SWFNode) treeNode; - SWF swf = swfNode.getItem(); - showCard(CARDPREVIEWPANEL); - if (isInternalFlashViewerSelected()) { - previewPanel.showImagePanel(swf, swf, -1); - } else { - previewPanel.setParametersPanelVisible(false); - if (flashPanel != null) { - previewPanel.showFlashViewerPanel(); - previewPanel.showSwf(swf); - } - } - } else if (tagObj instanceof DefineBinaryDataTag) { - DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tagObj; - showCard(CARDPREVIEWPANEL); - previewPanel.showBinaryPanel(binaryTag.binaryData); - } else if (tagObj instanceof ASMSource) { - showCard(CARDACTIONSCRIPTPANEL); - actionPanel.setSource((ASMSource) tagObj, !forceReload); - } else if (tagObj instanceof ImageTag) { - ImageTag imageTag = (ImageTag) tagObj; - previewPanel.setImageReplaceButtonVisible(imageTag.importSupported()); - showCard(CARDPREVIEWPANEL); - previewPanel.showImagePanel(imageTag.getImage()); - } else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (!(tagObj instanceof FontTag)) && (isInternalFlashViewerSelected())) { - final Tag tag = (Tag) tagObj; - showCard(CARDPREVIEWPANEL); - DrawableTag d = (DrawableTag) tag; - Timelined timelined = null; - if (tagObj instanceof Timelined && !(tagObj instanceof ButtonTag)) { - timelined = (Timelined) tag; - } else { - timelined = makeTimelined(tag); - } - - previewPanel.setParametersPanelVisible(false); - previewPanel.showImagePanel(timelined, tag.getSwf(), -1); - } else if ((tagObj instanceof FontTag) && (isInternalFlashViewerSelected())) { - FontTag fontTag = (FontTag) tagObj; - showCard(CARDPREVIEWPANEL); - showFontTag(fontTag); - } else if ((tagObj instanceof TextTag) && (isInternalFlashViewerSelected())) { - TextTag textTag = (TextTag) tagObj; - showCard(CARDPREVIEWPANEL); - showTextTag(textTag); - } else if (tagObj instanceof FrameNodeItem && ((FrameNodeItem) tagObj).isDisplayed() && (isInternalFlashViewerSelected())) { - showCard(CARDPREVIEWPANEL); - FrameNodeItem fn = (FrameNodeItem) tagObj; - SWF swf = fn.getSwf(); - List controlTags = swf.tags; - int containerId = 0; - RECT rect = swf.displayRect; - int totalFrameCount = swf.frameCount; - Timelined timelined = swf; - if (fn.getParent() instanceof DefineSpriteTag) { - DefineSpriteTag parentSprite = (DefineSpriteTag) fn.getParent(); - controlTags = parentSprite.subTags; - containerId = parentSprite.spriteId; - rect = parentSprite.getRect(); - totalFrameCount = parentSprite.frameCount; - timelined = parentSprite; - } - previewPanel.showImagePanel(timelined, swf, fn.getFrame() - 1); - } else if ((tagObj instanceof SoundTag)){ //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { - showCard(CARDPREVIEWPANEL); - previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); - previewPanel.setImageReplaceButtonVisible(tagObj instanceof DefineSoundTag); - try { - soundThread = new SoundTagPlayer((SoundTag) tagObj, Integer.MAX_VALUE); - previewPanel.setMedia(soundThread); - soundThread.play(); - } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } else if (((tagObj instanceof FrameNodeItem) && ((FrameNodeItem) tagObj).isDisplayed()) || ((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag) || (tagObj instanceof SoundStreamHeadTypeTag)) { - showCard(CARDPREVIEWPANEL); - previewPanel.createAndShowTempSwf(tagObj); - - if (tagObj instanceof TextTag) { - showTextTag((TextTag) tagObj); - } else if (tagObj instanceof FontTag) { - showFontTag((FontTag) tagObj); - } else { - previewPanel.setParametersPanelVisible(false); - } - } else if (tagObj instanceof Tag) { - showCard(CARDPREVIEWPANEL); - previewPanel.showGenericTagPanel((Tag) tagObj); - } else { - showCard(CARDEMPTYPANEL); - } - } - - private void showFontTag(FontTag ft) { - - previewPanel.showFontPanel(ft); - } - - private void showTextTag(TextTag textTag) { - - previewPanel.showTextPanel(textTag); - } - - private void showFolderPreview(TreeNode treeNode) { - folderPreviewPanel.removeAll(); - StringNode stringNode = (StringNode) treeNode; - StringItem item = stringNode.getItem(); - String folderName = item.getName(); - SWF swf = item.swf; - JPanel panel = folderPreviewPanel; - switch (folderName) { - case TagTreeModel.FOLDER_SHAPES: - for (Tag tag : swf.tags) { - if (tag instanceof ShapeTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_MORPHSHAPES: - for (Tag tag : swf.tags) { - if (tag instanceof MorphShapeTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_SPRITES: - for (Tag tag : swf.tags) { - if (tag instanceof DefineSpriteTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_BUTTONS: - for (Tag tag : swf.tags) { - if (tag instanceof ButtonTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_FONTS: - for (Tag tag : swf.tags) { - if (tag instanceof FontTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_FRAMES: - for (TreeNode subNode : treeNode.subNodes) { - FrameNode frameNode = (FrameNode) subNode; - FrameNodeItem fn = frameNode.getItem(); - Component c = PreviewImage.createFolderPreviewImage(this, fn); - if (c != null) { - panel.add(c); - } - } - break; - case TagTreeModel.FOLDER_IMAGES: - for (Tag tag : swf.tags) { - if (tag instanceof ImageTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - case TagTreeModel.FOLDER_TEXTS: - for (Tag tag : swf.tags) { - if (tag instanceof TextTag) { - Component c = PreviewImage.createFolderPreviewImage(this, tag); - if (c != null) { - panel.add(c); - } - } - } - break; - } - panel.revalidate(); - panel.repaint(); - } - - public void expandSwfNodes() { - TreeModel model = tagTree.getModel(); - Object node = model.getRoot(); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - tagTree.expandPath(new TreePath(new Object[]{node, child})); - } - } - - private boolean isFreeing; - - @Override - public boolean isFreeing() { - return isFreeing; - } - - @Override - public void free() { - isFreeing = true; - Helper.emptyObject(mainMenu); - Helper.emptyObject(statusPanel); - Helper.emptyObject(this); - } - - public void setErrorState(ErrorState errorState) { - statusPanel.setErrorState(errorState); - } - - public void timeline() { - final SWF swf = getCurrentSwf(); - if (swf != null) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item instanceof DefineSpriteTag) { - TimelineFrame tf = new TimelineFrame((DefineSpriteTag) item); - tf.setVisible(true); - return; - } - - TimelineFrame tf = new TimelineFrame(swf); - tf.setVisible(true); - } - } - - public static Timelined makeTimelined(final Tag tag) { - - return new Timelined() { - - private Timeline tim; - - @Override - public Timeline getTimeline() { - if (tim != null) { - return tim; - } - tim = new Timeline(tag.getSwf(), new ArrayList(), ((CharacterTag) tag).getCharacterId(), getRect()); - if (tag instanceof MorphShapeTag) { - tim.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; - int framesCnt = tim.frameRate * MORPH_SHAPE_ANIMATION_LENGTH; - for (int i = 0; i < framesCnt; i++) { - Frame f = new Frame(tim); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.ratio = i * 65535 / framesCnt; - f.layers.put(1, ds); - tim.frames.add(f); - } - } else if (tag instanceof FontTag) { - int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); - for (int i = 0; i < pageCount; i++) { - Frame f = new Frame(tim); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.time = i; - f.layers.put(1, ds); - tim.frames.add(f); - } - } else { - Frame f = new Frame(tim); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - tim.frames.add(f); - } - tim.displayRect = getRect(); - return tim; - } - - @Override - public void resetTimeline() { - tim = null; - } - - @Override - public RECT getRect() { - return ((BoundedTag) tag).getRect(); - } - }; - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.SWF; +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.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +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.TextExporter; +import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; +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.TextExportMode; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FramesExportSettings; +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.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.gui.abc.ABCPanel; +import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; +import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; +import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; +import com.jpexs.decompiler.flash.gui.action.ActionPanel; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.timeline.TimelineFrame; +import com.jpexs.decompiler.flash.gui.treenodes.SWFBundleNode; +import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; +import com.jpexs.decompiler.flash.gui.treenodes.StringNode; +import com.jpexs.decompiler.flash.helpers.Freed; +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.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont3Tag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +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.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.ParseException; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.StringItem; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.treenodes.ContainerNode; +import com.jpexs.decompiler.flash.treenodes.FrameNode; +import com.jpexs.decompiler.flash.treenodes.TagNode; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.decompiler.flash.types.MATRIX; +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.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JColorChooser; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.filechooser.FileFilter; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +/** + * + * @author JPEXS + */ +public final class MainPanel extends JPanel implements ActionListener, TreeSelectionListener, SearchListener, Freed { + + private final MainFrame mainFrame; + private final List swfs; + private ABCPanel abcPanel; + private ActionPanel actionPanel; + private final JPanel welcomePanel; + private final MainFrameStatusPanel statusPanel; + private final MainFrameMenu mainMenu; + private final JProgressBar progressBar = new JProgressBar(0, 100); + private DeobfuscationDialog deobfuscationDialog; + public TagTree tagTree; + private final FlashPlayerPanel flashPanel; + private final JPanel contentPanel; + private final JPanel displayPanel; + private JPanel folderPreviewPanel; + private boolean isWelcomeScreen = true; + private static final String CARDPREVIEWPANEL = "Preview card"; + private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; + private static final String CARDEMPTYPANEL = "Empty card"; + private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; + private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; + private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; + private static final String DETAILCARDEMPTYPANEL = "Empty card"; + private static final String SPLIT_PANE1 = "SPLITPANE1"; + private static final String WELCOME_PANEL = "WELCOMEPANEL"; + private final JSplitPane splitPane1; + private final JSplitPane splitPane2; + private boolean splitsInited = false; + private JPanel detailPanel; + private JTextField filterField = new MyTextField(""); + private JPanel searchPanel; + private PreviewPanel previewPanel; + private AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + private CancellableWorker setSourceWorker; + public TreeNode oldNode; + + private SoundTagPlayer soundThread = null; + + public static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; + public static final String ACTION_REPLACE = "REPLACE"; + private static final String ACTION_REMOVE_ITEM = "REMOVEITEM"; + private static final String ACTION_CLOSE_SWF = "CLOSESWF"; + private static final String ACTION_EXPAND_RECURSIVE = "EXPANDRECURSIVE"; + + // play morph shape in 2 second(s) + public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; + + public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; + + private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); + + public void setPercent(int percent) { + progressBar.setValue(percent); + progressBar.setVisible(true); + } + + public void hidePercent() { + if (progressBar.isVisible()) { + progressBar.setVisible(false); + } + } + + static { + try { + File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { + tabbedPane.add(tab); + + JLabel lbl = new JLabel(title); + lbl.setIcon(icon); + lbl.setIconTextGap(5); + lbl.setHorizontalTextPosition(SwingConstants.RIGHT); + + tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); + } + + public void setStatus(String s) { + statusPanel.setStatus(s); + } + + public void setWorkStatus(String s, CancellableWorker worker) { + statusPanel.setWorkStatus(s, worker); + } + + private void createContextMenu() { + final JPopupMenu contextPopupMenu = new JPopupMenu(); + + final JMenuItem expandRecursiveMenuItem = new JMenuItem(translate("contextmenu.expandAll")); + expandRecursiveMenuItem.addActionListener(this); + expandRecursiveMenuItem.setActionCommand(ACTION_EXPAND_RECURSIVE); + contextPopupMenu.add(expandRecursiveMenuItem); + + final JMenuItem removeMenuItem = new JMenuItem(translate("contextmenu.remove")); + removeMenuItem.addActionListener(this); + removeMenuItem.setActionCommand(ACTION_REMOVE_ITEM); + contextPopupMenu.add(removeMenuItem); + + final JMenuItem exportSelectionMenuItem = new JMenuItem(translate("menu.file.export.selection")); + exportSelectionMenuItem.setActionCommand(MainFrameRibbonMenu.ACTION_EXPORT_SEL); + exportSelectionMenuItem.addActionListener(this); + contextPopupMenu.add(exportSelectionMenuItem); + + final JMenuItem replaceSelectionMenuItem = new JMenuItem(translate("button.replace")); + replaceSelectionMenuItem.setActionCommand(ACTION_REPLACE); + replaceSelectionMenuItem.addActionListener(this); + contextPopupMenu.add(replaceSelectionMenuItem); + + final JMenuItem closeSelectionMenuItem = new JMenuItem(translate("contextmenu.closeSwf")); + closeSelectionMenuItem.setActionCommand(ACTION_CLOSE_SWF); + closeSelectionMenuItem.addActionListener(this); + contextPopupMenu.add(closeSelectionMenuItem); + + final JMenu moveTagMenu = new JMenu(translate("contextmenu.moveTag")); + moveTagMenu.addActionListener(this); + contextPopupMenu.add(moveTagMenu); + + tagTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + + int row = tagTree.getClosestRowForLocation(e.getX(), e.getY()); + int[] selectionRows = tagTree.getSelectionRows(); + if (!Helper.contains(selectionRows, row)) { + tagTree.setSelectionRow(row); + } + + TreePath[] paths = tagTree.getSelectionPaths(); + if (paths == null || paths.length == 0) { + return; + } + boolean allSelectedIsTagOrFrame = true; + for (TreePath treePath : paths) { + TreeNode treeNode = (TreeNode) treePath.getLastPathComponent(); + + TreeItem tag = treeNode.getItem(); + if (!(tag instanceof Tag) && !(tag instanceof FrameNodeItem)) { + allSelectedIsTagOrFrame = false; + break; + } + } + + replaceSelectionMenuItem.setVisible(false); + closeSelectionMenuItem.setVisible(false); + moveTagMenu.setVisible(false); + expandRecursiveMenuItem.setVisible(false); + + if (paths.length == 1) { + TreeNode treeNode = (TreeNode) paths[0].getLastPathComponent(); + + TreeItem item = ((TreeNode) treeNode).getItem(); + + if (item instanceof ImageTag && ((ImageTag) item).importSupported()) { + replaceSelectionMenuItem.setVisible(true); + } + + if (item instanceof DefineBinaryDataTag) { + replaceSelectionMenuItem.setVisible(true); + } + + if (item instanceof DefineSoundTag) { + replaceSelectionMenuItem.setVisible(true); + } + + if (treeNode instanceof SWFNode) { + closeSelectionMenuItem.setVisible(true); + } + + if (item instanceof Tag && swfs.size() > 1) { + final Tag tag = (Tag) item; + moveTagMenu.removeAll(); + for (SWFList targetSwfList : swfs) { + for (final SWF targetSwf : targetSwfList) { + if (targetSwf != tag.getSwf()) { + JMenuItem swfItem = new JMenuItem(targetSwf.getShortFileName()); + swfItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + tag.getSwf().tags.remove(tag); + tag.setSwf(targetSwf); + targetSwf.tags.add(tag); + refreshTree(); + } + }); + moveTagMenu.add(swfItem); + } + } + } + moveTagMenu.setVisible(true); + } + + TreeModel model = tagTree.getModel(); + expandRecursiveMenuItem.setVisible(model.getChildCount(treeNode) > 0); + } + + removeMenuItem.setVisible(allSelectedIsTagOrFrame); + exportSelectionMenuItem.setEnabled(hasExportableNodes()); + contextPopupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + + private JPanel createWelcomePanel() { + JPanel welcomePanel = new JPanel(); + welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); + JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); + welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); + welcomeToLabel.setAlignmentX(0.5f); + JPanel appNamePanel = new JPanel(new FlowLayout()); + JLabel jpLabel = new JLabel("JPEXS "); + jpLabel.setAlignmentX(0.5f); + jpLabel.setForeground(new Color(0, 0, 160)); + jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + jpLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(jpLabel); + + JLabel ffLabel = new JLabel("Free Flash "); + ffLabel.setAlignmentX(0.5f); + ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + ffLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(ffLabel); + + JLabel decLabel = new JLabel("Decompiler"); + decLabel.setAlignmentX(0.5f); + decLabel.setForeground(Color.red); + decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(decLabel); + appNamePanel.setAlignmentX(0.5f); + welcomePanel.add(Box.createGlue()); + welcomePanel.add(welcomeToLabel); + welcomePanel.add(appNamePanel); + JLabel startLabel = new JLabel(translate("startup.selectopen")); + startLabel.setAlignmentX(0.5f); + startLabel.setFont(startLabel.getFont().deriveFont(30)); + welcomePanel.add(startLabel); + welcomePanel.add(Box.createGlue()); + return welcomePanel; + } + + private JPanel createFolderPreviewCard() { + JPanel folderPreviewCard = new JPanel(new BorderLayout()); + folderPreviewPanel = new JPanel(new WrapLayout(FlowLayout.LEFT)); + folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); + + return folderPreviewCard; + } + + public String translate(String key) { + return mainFrame.translate(key); + } + + public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel) { + super(); + + this.mainFrame = mainFrame; + this.mainMenu = mainMenu; + this.flashPanel = flashPanel; + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + + setLayout(new BorderLayout()); + swfs = new ArrayList<>(); + + detailPanel = new JPanel(); + detailPanel.setLayout(new CardLayout()); + JPanel whitePanel = new JPanel(); + whitePanel.setBackground(Color.white); + detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); + CardLayout cl2 = (CardLayout) (detailPanel.getLayout()); + cl2.show(detailPanel, DETAILCARDEMPTYPANEL); + + UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); + tagTree = new TagTree((TagTreeModel) null); + tagTree.addTreeSelectionListener(this); + + DragSource dragSource = DragSource.getDefaultDragSource(); + dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{DataFlavor.javaFileListFlavor}; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(DataFlavor.javaFileListFlavor); + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (flavor.equals(DataFlavor.javaFileListFlavor)) { + List files; + String tempDir = System.getProperty("java.io.tmpdir"); + if (!tempDir.endsWith(File.separator)) { + tempDir += File.separator; + } + Random rnd = new Random(); + tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); + File fTempDir = new File(tempDir); + if (!fTempDir.exists()) { + if (!fTempDir.mkdirs()) { + if (!fTempDir.exists()) { + throw new IOException("cannot create directory " + fTempDir); + } + } + } + + File ftemp = new File(tempDir); + ExportDialog exd = new ExportDialog(null); + files = exportSelection(errorHandler, tempDir, exd); + files.clear(); + + File[] fs = ftemp.listFiles(); + files.addAll(Arrays.asList(fs)); + + Main.stopWork(); + + for (File f : files) { + f.deleteOnExit(); + } + new File(tempDir).deleteOnExit(); + return files; + + } + return null; + } + }, new DragSourceListener() { + @Override + public void dragEnter(DragSourceDragEvent dsde) { + enableDrop(false); + } + + @Override + public void dragOver(DragSourceDragEvent dsde) { + } + + @Override + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + @Override + public void dragExit(DragSourceEvent dse) { + } + + @Override + public void dragDropEnd(DragSourceDropEvent dsde) { + enableDrop(true); + } + }); + } + }); + + createContextMenu(); + + statusPanel = new MainFrameStatusPanel(this); + add(statusPanel, BorderLayout.SOUTH); + + displayPanel = new JPanel(new CardLayout()); + + previewPanel = new PreviewPanel(this, flashPanel); + displayPanel.add(previewPanel, CARDPREVIEWPANEL); + displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); + + displayPanel.add(new JPanel(), CARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); + + searchPanel = new JPanel(); + searchPanel.setLayout(new BorderLayout()); + searchPanel.add(filterField, BorderLayout.CENTER); + searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); + JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); + closeSearchButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + filterField.setText(""); + doFilter(); + searchPanel.setVisible(false); + } + }); + searchPanel.add(closeSearchButton, BorderLayout.EAST); + JPanel pan1 = new JPanel(new BorderLayout()); + pan1.add(new JScrollPane(tagTree), BorderLayout.CENTER); + pan1.add(searchPanel, BorderLayout.SOUTH); + + filterField.addActionListener(this); + + searchPanel.setVisible(false); + + filterField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + warn(); + } + + public void warn() { + doFilter(); + } + }); + + //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); + splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pan1, detailPanel); + splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel); + + welcomePanel = createWelcomePanel(); + add(welcomePanel, BorderLayout.CENTER); + + splitPane1.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (splitsInited) { + Configuration.guiSplitPane1DividerLocation.set((int) pce.getNewValue()); + } + } + }); + + splitPane2.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (detailPanel.isVisible()) { + Configuration.guiSplitPane2DividerLocation.set((int) pce.getNewValue()); + } + } + }); + + CardLayout cl3 = new CardLayout(); + contentPanel = new JPanel(cl3); + contentPanel.add(welcomePanel, WELCOME_PANEL); + contentPanel.add(splitPane1, SPLIT_PANE1); + add(contentPanel); + cl3.show(contentPanel, WELCOME_PANEL); + + tagTree.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if ((e.getKeyCode() == 'F') && (e.isControlDown())) { + searchPanel.setVisible(true); + filterField.requestFocusInWindow(); + } + } + }); + detailPanel.setVisible(false); + + updateUi(); + + //Opening files with drag&drop to main window + enableDrop(true); + } + + public void load(SWFList newSwfs, boolean first) { + + previewPanel.clear(); + + swfs.add(newSwfs); + + for (SWF swf : newSwfs) { + + boolean hasAbc = !swf.abcList.isEmpty(); + swf.isAS3 = hasAbc; + + tagTree.setModel(new TagTreeModel(mainFrame, swfs)); + + if (hasAbc) { + if (abcPanel == null) { + abcPanel = new ABCPanel(this); + displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); + detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); + } + abcPanel.setSwf(swf); + } else { + if (actionPanel == null) { + actionPanel = new ActionPanel(this); + displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); + } + } + + expandSwfNodes(); + + for (Tag t : swf.tags) { + if (t instanceof JPEGTablesTag) { + swf.jtt = (JPEGTablesTag) t; + } + } + + if (Configuration.autoRenameIdentifiers.get()) { + try { + swf.deobfuscateIdentifiers(RenameType.TYPENUMBER); + swf.assignClassesToSymbols(); + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + } catch (InterruptedException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + showDetail(DETAILCARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); + updateUi(swf); + + /*if (first && Configuration.gotoMainClassOnStartup.get()) { + gotoDocumentClass(swf); + }*/ + first = false; + } + } + + private void updateUi(final SWF swf) { + + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + + List abcList = swf.abcList; + + boolean hasAbc = !abcList.isEmpty(); + + if (hasAbc) { + abcPanel.setSwf(swf); + } + + if (isWelcomeScreen) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, SPLIT_PANE1); + isWelcomeScreen = false; + } + + mainMenu.updateComponents(swf, abcList); + } + + private void updateUi() { + if (!isWelcomeScreen && swfs.isEmpty()) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, WELCOME_PANEL); + isWelcomeScreen = true; + } + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + mainMenu.updateComponents(null, null); + } + + public void closeAll() { + swfs.clear(); + oldNode = null; + previewPanel.clear(); + if (abcPanel != null) { + abcPanel.clearSwf(); + } + if (actionPanel != null) { + actionPanel.clearSource(); + } + updateUi(); + refreshTree(); + } + + public void close(SWFList swfList) { + swfs.remove(swfList); + if (abcPanel != null) { + for (SWF swf : swfList) { + if (abcPanel.swf == swf) { + abcPanel.clearSwf(); + } + } + } + if (actionPanel != null) { + actionPanel.clearSource(); + } + oldNode = null; + previewPanel.clear(); + updateUi(); + refreshTree(); + } + + public void enableDrop(boolean value) { + if (value) { + setDropTarget(new DropTarget() { + @Override + public synchronized void drop(DropTargetDropEvent dtde) { + try { + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + @SuppressWarnings("unchecked") + List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); + if (!droppedFiles.isEmpty()) { + String path = droppedFiles.get(0).getAbsolutePath(); + Main.openFile(path, null); + } + } catch (UnsupportedFlavorException | IOException ex) { + } + } + }); + } else { + setDropTarget(null); + } + } + + public void updateClassesList() { + List nodes = getASTreeNodes(tagTree); + boolean updateNeeded = false; + for (TreeNode n : nodes) { + if (n.getItem() instanceof ClassesListTreeModel) { + ((ClassesListTreeModel) n.getItem()).update(); + updateNeeded = true; + } + } + + refreshTree(); + + if (updateNeeded) { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + tagTree.updateUI(); + } + }); + } + } + + public void doFilter() { + List nodes = getASTreeNodes(tagTree); + boolean updateNeeded = false; + for (TreeNode n : nodes) { + if (n.getItem() instanceof ClassesListTreeModel) { + ((ClassesListTreeModel) n.getItem()).setFilter(filterField.getText()); + updateNeeded = true; + } + } + + if (updateNeeded) { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + tagTree.updateUI(); + } + }); + } + } + + @Override + public void setVisible(boolean b) { + super.setVisible(b); + if (b) { + if (abcPanel != null) { + abcPanel.initSplits(); + } + if (actionPanel != null) { + actionPanel.initSplits(); + } + + final MainPanel t = this; + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + splitPane1.setDividerLocation(Configuration.guiSplitPane1DividerLocation.get(getWidth() / 3)); + int confDivLoc = Configuration.guiSplitPane2DividerLocation.get(splitPane2.getHeight() * 3 / 5); + if (confDivLoc > splitPane2.getHeight() - 10) { //In older releases, divider location was saved when detailPanel was invisible too + confDivLoc = splitPane2.getHeight() * 3 / 5; + } + splitPane2.setDividerLocation(confDivLoc); + previewPanel.setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(previewPanel.getWidth() / 2)); + + splitPos = splitPane2.getDividerLocation(); + splitsInited = true; + previewPanel.setSplitsInited(); + } + }); + + } + } + + public static void getShapes(List list, List shapes) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getShapes(((Container) t).getSubItems(), shapes); + } + if ((t instanceof DefineShapeTag) + || (t instanceof DefineShape2Tag) + || (t instanceof DefineShape3Tag) + || (t instanceof DefineShape4Tag)) { + shapes.add((Tag) t); + } + } + } + + public static void getFonts(List list, List fonts) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getFonts(((Container) t).getSubItems(), fonts); + } + if ((t instanceof DefineFontTag) + || (t instanceof DefineFont2Tag) + || (t instanceof DefineFont3Tag) + || (t instanceof DefineFont4Tag)) { + fonts.add((Tag) t); + } + } + } + + public static void getMorphShapes(List list, List morphShapes) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getMorphShapes(((Container) t).getSubItems(), morphShapes); + } + if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) { + morphShapes.add((Tag) t); + } + } + } + + public static void getImages(List list, List images) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getImages(((Container) t).getSubItems(), images); + } + if ((t instanceof DefineBitsTag) + || (t instanceof DefineBitsJPEG2Tag) + || (t instanceof DefineBitsJPEG3Tag) + || (t instanceof DefineBitsJPEG4Tag) + || (t instanceof DefineBitsLosslessTag) + || (t instanceof DefineBitsLossless2Tag)) { + images.add((Tag) t); + } + } + } + + public static void getTexts(List list, List texts) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getTexts(((Container) t).getSubItems(), texts); + } + if ((t instanceof DefineTextTag) + || (t instanceof DefineText2Tag) + || (t instanceof DefineEditTextTag)) { + texts.add((Tag) t); + } + } + } + + public static void getSprites(List list, List sprites) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getSprites(((Container) t).getSubItems(), sprites); + } + if (t instanceof DefineSpriteTag) { + sprites.add((Tag) t); + } + } + } + + public static void getButtons(List list, List buttons) { + for (ContainerItem t : list) { + if (t instanceof Container) { + getButtons(((Container) t).getSubItems(), buttons); + } + if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) { + buttons.add((Tag) t); + } + } + } + + public List getSelectedNodes() { + List ret = new ArrayList<>(); + TreePath[] tps = tagTree.getSelectionPaths(); + if (tps == null) { + return ret; + } + for (TreePath tp : tps) { + TagNode te = (TagNode) tp.getLastPathComponent(); + ret.add(te); + } + return ret; + } + + public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { + String oldName = identifier; + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + swf.renameAS2Identifier(oldName, newName); + View.showMessageDialog(null, translate("rename.finished.identifier")); + updateClassesList(); + reload(true); + } + } + } + + public void renameMultiname(List abcList, int multiNameIndex) { + String oldName = ""; + if (abcPanel.abc.constants.getMultiname(multiNameIndex).name_index > 0) { + oldName = abcPanel.abc.constants.getString(abcPanel.abc.constants.getMultiname(multiNameIndex).name_index); + } + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + int mulCount = 0; + for (ABCContainerTag cnt : abcList) { + ABC abc = cnt.getABC(); + for (int m = 1; m < abc.constants.getMultinameCount(); m++) { + int ni = abc.constants.getMultiname(m).name_index; + String n = ""; + if (ni > 0) { + n = abc.constants.getString(ni); + } + if (n.equals(oldName)) { + abc.renameMultiname(m, newName); + mulCount++; + } + } + } + View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", "" + mulCount)); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + reload(true); + abcPanel.hilightScript(abcPanel.swf, abcPanel.decompiledTextArea.getScriptLeaf().getPath().toString()); + } + } + } + + public List getSelected(JTree tree) { + TreeSelectionModel tsm = tree.getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); + ret.add(treeNode); + } + return ret; + } + + public List getAllSubs(JTree tree, TreeNode o) { + TagTreeModel tm = (TagTreeModel) tree.getModel(); + List ret = new ArrayList<>(); + for (int i = 0; i < tm.getChildCount(o); i++) { + TreeNode c = tm.getChild(o, i); + ret.add(c); + ret.addAll(getAllSubs(tree, c)); + } + return ret; + } + + public List getAllSelected(TagTree tree) { + TreeSelectionModel tsm = tree.getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeNode treeNode = (TreeNode) tp.getLastPathComponent(); + ret.add(treeNode); + ret.addAll(getAllSubs(tree, treeNode)); + } + return ret; + } + + public List getASTreeNodes(TagTree tree) { + List result = new ArrayList<>(); + TagTreeModel tm = (TagTreeModel) tree.getModel(); + if (tm == null) { + return result; + } + TreeNode root = tm.getRoot(); + for (int i = 0; i < tm.getChildCount(root); i++) { + // first level node can be SWFNode and SWFBundleNode + TreeNode node = tm.getChild(root, i); + if (node instanceof SWFBundleNode) { + for (int j = 0; j < tm.getChildCount(node); j++) { + // child of SWFBundleNode should be SWFNode + SWFNode swfNode = (SWFNode) tm.getChild(node, j); + result.add(swfNode.scriptsNode); + } + } else if (node instanceof SWFNode) { + SWFNode swfNode = (SWFNode) tm.getChild(root, i); + result.add(swfNode.scriptsNode); + } + } + return result; + } + + public boolean confirmExperimental() { + return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; + } + private SearchDialog searchDialog; + + public boolean hasExportableNodes() { + return !getSelection(getCurrentSwf()).isEmpty(); + } + + private List getSelection(SWF swf) { + List ret = new ArrayList<>(); + List sel = getAllSelected(tagTree); + for (TreeNode d : sel) { + if (d.getItem().getSwf() != swf) { + continue; + } + if (d instanceof ContainerNode) { + ContainerNode n = (ContainerNode) d; + TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); + if (nodeType == TreeNodeType.IMAGE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SHAPE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.AS) { + ret.add(n); + } + if (nodeType == TreeNodeType.MOVIE) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SOUND) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.TEXT) { + ret.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.FONT) { + ret.add((Tag) n.getItem()); + } + } + if (d instanceof FrameNode) { + FrameNode fn = (FrameNode) d; + if (!fn.scriptsNode) { + ret.add(d.getItem()); + } + } + if (d instanceof TreeElement) { + if (((TreeElement) d).isLeaf()) { + TreeElement treeElement = (TreeElement) d; + ret.add((ScriptPack) treeElement.getItem()); + } + } + } + return ret; + } + + public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException { + + List ret = new ArrayList<>(); + List sel = getAllSelected(tagTree); + + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + List as3scripts = new ArrayList<>(); + List images = new ArrayList<>(); + List shapes = new ArrayList<>(); + List morphshapes = new ArrayList<>(); + List movies = new ArrayList<>(); + List sounds = new ArrayList<>(); + List texts = new ArrayList<>(); + List as12scripts = new ArrayList<>(); + List binaryData = new ArrayList<>(); + Map> frames = new HashMap<>(); + List fonts = new ArrayList<>(); + + for (TreeNode d : sel) { + if (d.getItem().getSwf() != swf) { + continue; + } + if (d instanceof ContainerNode) { + ContainerNode n = (ContainerNode) d; + TreeNodeType nodeType = TagTree.getTreeNodeType(n.getItem()); + if (nodeType == TreeNodeType.IMAGE) { + images.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SHAPE) { + shapes.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + morphshapes.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.AS) { + as12scripts.add(n); + } + if (nodeType == TreeNodeType.MOVIE) { + movies.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.SOUND) { + sounds.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + binaryData.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.TEXT) { + texts.add((Tag) n.getItem()); + } + if (nodeType == TreeNodeType.FONT) { + fonts.add((Tag) n.getItem()); + } + } + if (d instanceof FrameNode) { + FrameNode fn = (FrameNode) d; + if (!fn.scriptsNode) { + + FrameNodeItem fni = (FrameNodeItem) d.getItem(); + Tag par = fni.getParent(); + int frame = fni.getFrame() - 1; //Fix to zero based + int parentId = 0; + if (par != null) { + parentId = ((CharacterTag) par).getCharacterId(); + } + if (!frames.containsKey(parentId)) { + frames.put(parentId, new ArrayList()); + } + frames.get(parentId).add(frame); + } + } + if (d instanceof TreeElement) { + if (((TreeElement) d).isLeaf()) { + TreeElement treeElement = (TreeElement) d; + as3scripts.add((ScriptPack) treeElement.getItem()); + } + } + } + + if (selFile == null) { + selFile = selectExportDir(); + if (selFile == null) { + return new ArrayList<>(); + } + } + + final ScriptExportMode scriptMode = export.getValue(ScriptExportMode.class); + ret.addAll(new ImageExporter().exportImages(handler, selFile + File.separator + "images", images, + new ImageExportSettings(export.getValue(ImageExportMode.class)))); + ret.addAll(new ShapeExporter().exportShapes(handler, selFile + File.separator + "shapes", shapes, + new ShapeExportSettings(export.getValue(ShapeExportMode.class)))); + ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile + File.separator + "morphshapes", morphshapes, + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class)))); + ret.addAll(new TextExporter().exportTexts(handler, selFile + File.separator + "texts", texts, + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get()))); + ret.addAll(new MovieExporter().exportMovies(handler, selFile + File.separator + "movies", movies, + new MovieExportSettings(export.getValue(MovieExportMode.class)))); + ret.addAll(new SoundExporter().exportSounds(handler, selFile + File.separator + "sounds", sounds, + new SoundExportSettings(export.getValue(SoundExportMode.class)))); + ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile + File.separator + "binaryData", binaryData, + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)))); + ret.addAll(new FontExporter().exportFonts(handler, selFile + File.separator + "fonts", fonts, + new FontExportSettings(export.getValue(FontExportMode.class)))); + + for (Entry> entry : frames.entrySet()) { + ret.addAll(swf.exportFrames(handler, selFile + File.separator + "frames", entry.getKey(), entry.getValue(), + new FramesExportSettings(export.getValue(FramesExportMode.class)))); + } + List abcList = swf.abcList; + if (abcPanel != null) { + for (int i = 0; i < as3scripts.size(); i++) { + ScriptPack tls = as3scripts.get(i); + Main.startWork(translate("work.exporting") + " " + (i + 1) + "/" + as3scripts.size() + " " + tls.getPath() + " ..."); + ret.add(tls.export(selFile, abcList, scriptMode, Configuration.parallelSpeedUp.get())); + } + } else { + List allNodes = new ArrayList<>(); + List allAs12Scripts = new ArrayList<>(); + + if (abcPanel == null) { + allAs12Scripts = getASTreeNodes(tagTree); + } + for (TreeNode asn : allAs12Scripts) { + allNodes.add(asn); + TagNode.setExport(allNodes, false); + TagNode.setExport(as12scripts, true); + ret.addAll(TagNode.exportNodeAS(handler, allNodes, selFile, scriptMode, null)); + } + } + } + } + return ret; + } + + public SWFList getCurrentSwfList() { + SWF swf = getCurrentSwf(); + if (swf == null) { + return null; + } + + return swf.swfList; + } + + public SWF getCurrentSwf() { + if (swfs == null || swfs.isEmpty()) { + return null; + } + + TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); + if (treeNode == null) { + return swfs.get(0).get(0); + } + + if (treeNode instanceof SWFBundleNode) { + return null; + } + + return treeNode.getItem().getSwf(); + } + + private void clearCache() { + if (abcPanel != null) { + abcPanel.decompiledTextArea.clearScriptCache(); + } + if (actionPanel != null) { + actionPanel.clearCache(); + } + } + + public void gotoDocumentClass(SWF swf) { + if (swf == null) { + return; + } + String documentClass = null; + loopdc: + for (Tag t : swf.tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.length; i++) { + if (sc.tags[i] == 0) { + documentClass = sc.names[i]; + break loopdc; + } + } + } + } + if (documentClass != null) { + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPT3PANEL); + abcPanel.setSwf(swf); + abcPanel.hilightScript(swf, documentClass); + } + } + + public void disableDecompilationChanged() { + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + reload(true); + updateClassesList(); + } + + public void searchAs() { + if (searchDialog == null) { + searchDialog = new SearchDialog(); + } + searchDialog.setVisible(true); + if (searchDialog.result) { + final String txt = searchDialog.searchField.getText(); + if (!txt.isEmpty()) { + final SWF swf = getCurrentSwf(); + + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + boolean found = false; + if (searchDialog.searchInASRadioButton.isSelected()) { + if (swf.isAS3) { + if (abcPanel != null && abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { + found = true; + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPT3PANEL); + } + }); + } + } else { + if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { + found = true; + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + showCard(CARDACTIONSCRIPTPANEL); + } + }); + } + } + } else if (searchDialog.searchInTextsRadioButton.isSelected()) { + if (searchText(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected(), swf)) { + found = true; + } + } + + if (!found) { + View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + + return null; + } + }.execute(); + } + } + } + + private boolean searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { + if ((txt != null) && (!txt.isEmpty())) { + SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); + textSearchPanel.setOptions(ignoreCase, regexp); + List found = new ArrayList<>(); + Pattern pat = null; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (pat.matcher(textTag.getFormattedText()).find()) { + found.add(textTag); + } + } + } + textSearchPanel.setSearchText(txt); + return textSearchPanel.setResults(found); + } + return false; + } + + @Override + public void updateSearchPos(TextTag item) { + setTreeItem(item); + previewPanel.getTextPanel().updateSearchPos(); + } + + public void setTreeItem(TreeItem treeItem) { + TagTreeModel ttm = (TagTreeModel) tagTree.getModel(); + TreePath tp = ttm.getTagPath(treeItem); + if (tp != null) { + tagTree.setSelectionPath(tp); + tagTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void autoDeobfuscateChanged() { + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + reload(true); + updateClassesList(); + } + + public void renameOneIdentifier(final SWF swf) { + if (swf.fileAttributes != null && swf.fileAttributes.actionScript3) { + final int multiName = abcPanel.decompiledTextArea.getMultinameUnderCursor(); + final List abcList = swf.abcList; + if (multiName > 0) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + Main.startWork(translate("work.renaming") + "..."); + renameMultiname(abcList, multiName); + return null; + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + + } else { + View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } else { + final String identifier = actionPanel.getStringUnderCursor(); + if (identifier != null) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + Main.startWork(translate("work.renaming") + "..."); + try { + renameIdentifier(swf, identifier); + } catch (InterruptedException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + } else { + View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + } + + public void exportFla(final SWF swf) { + JFileChooser fc = new JFileChooser(); + String selDir = Configuration.lastOpenDir.get(); + fc.setCurrentDirectory(new File(selDir)); + if (!selDir.endsWith(File.separator)) { + selDir += File.separator; + } + String fileName = new File(swf.file).getName(); + fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; + fc.setSelectedFile(new File(selDir + fileName)); + List flaFilters = new ArrayList<>(); + List xflFilters = new ArrayList<>(); + List versions = new ArrayList<>(); + FileFilter defaultFilter = null; + for (int i = FLAVersion.values().length - 1; i >= 0; i--) { + final FLAVersion v = FLAVersion.values()[i]; + if (!swf.isAS3 && v.minASVersion() > 2) { + //This version does not support AS1/2 + } else { + versions.add(v); + FileFilter f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); + } + + @Override + public String getDescription() { + return translate("filter.fla").replace("%version%", v.applicationName()); + } + }; + if (v == FLAVersion.CS6) { + defaultFilter = f; + fc.setFileFilter(f); + } else { + fc.addChoosableFileFilter(f); + } + flaFilters.add(f); + f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); + } + + @Override + public String getDescription() { + return translate("filter.xfl").replace("%version%", v.applicationName()); + } + }; + fc.addChoosableFileFilter(f); + xflFilters.add(f); + } + } + if (defaultFilter == null) { + defaultFilter = flaFilters.get(0); + } + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showSaveDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File sf = Helper.fixDialogFile(fc.getSelectedFile()); + + Main.startWork(translate("work.exporting.fla") + "..."); + final boolean compressed = flaFilters.contains(fc.getFileFilter()); + if (!compressed) { + if (sf.getName().endsWith(".fla")) { + sf = new File(sf.getAbsolutePath().substring(0, sf.getAbsolutePath().length() - 4) + ".xfl"); + } + } + final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(fc.getFileFilter()) : xflFilters.indexOf(fc.getFileFilter())); + final File selfile = sf; + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + Helper.freeMem(); + try { + if (compressed) { + swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } else { + swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.file).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } + } catch (IOException ex) { + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); + } + Helper.freeMem(); + return null; + } + + @Override + protected void done() { + Main.stopWork(); + if (Configuration.openFolderAfterFlaExport.get()) { + try { + Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); + } catch (IOException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + }.execute(); + } + } + + private Map splitTextRecords(String texts) { + String[] textsArr = texts.split(Helper.newLine + Configuration.textExportSingleFileSeparator.get() + Helper.newLine); + String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine; + Map result = new HashMap<>(); + for (String text : textsArr) { + String[] textArr = text.split(Helper.newLine, 2); + String idLine = textArr[0]; + if (idLine.startsWith("ID:")) { + int id = Integer.parseInt(idLine.substring(3).trim()); + String[] records = textArr[1].split(recordSeparator); + result.put(id, records); + } else { + if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return null; + } + } + } + return result; + } + + private void importTextsSingleFile(File textsFile, SWF swf) { + String texts = Helper.readTextFile(textsFile.getPath()); + Map records = splitTextRecords(texts); + if (records != null) { + for (int characterId : records.keySet()) { + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (textTag.getCharacterId() == characterId) { + String[] currentRecords = records.get(characterId); + String text = textTag.getFormattedText(); + if (!saveText(textTag, text, currentRecords)) { + if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + } + break; + } + } + } + } + } + } + + private void importTextsSingleFileFormatted(File textsFile, SWF swf) { + String texts = Helper.readTextFile(textsFile.getPath()); + Map records = splitTextRecords(texts); + if (records != null) { + for (int characterId : records.keySet()) { + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (textTag.getCharacterId() == characterId) { + String[] currentRecords = records.get(characterId); + if (!saveText(textTag, currentRecords[0], null)) { + if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + } + break; + } + } + } + } + } + } + + private void importTextsMultipleFiles(String folder, SWF swf) { + File textsFolder = new File(Path.combine(folder, TextExporter.TEXT_EXPORT_FOLDER)); + String[] files = textsFolder.list(new FilenameFilter() { + + private Pattern pat = Pattern.compile("\\d+\\.txt", Pattern.CASE_INSENSITIVE); + + @Override + public boolean accept(File dir, String name) { + + return pat.matcher(name).matches(); + } + }); + + for (String fileName : files) { + String texts = Helper.readTextFile(Path.combine(textsFolder.getPath(), fileName)); + int characterId = Integer.parseInt(fileName.split("\\.")[0]); + String recordSeparator = Helper.newLine + Configuration.textExportSingleFileRecordSeparator.get() + Helper.newLine; + boolean formatted = !texts.contains(recordSeparator) && texts.startsWith("[" + Helper.newLine); + if (!formatted) { + String[] records = texts.split(recordSeparator); + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (textTag.getCharacterId() == characterId) { + String text = textTag.getFormattedText(); + if (!saveText(textTag, text, records)) { + if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + } + break; + } + } + } + } else { + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (textTag.getCharacterId() == characterId) { + if (!saveText(textTag, texts, null)) { + if (View.showConfirmDialog(this, translate("error.text.import"), translate("error"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + } + break; + } + } + } + } + } + } + + public void importText(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + File textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); + // try to import formatted texts + if (textsFile.exists()) { + importTextsSingleFileFormatted(textsFile, swf); + } else { + textsFile = new File(Path.combine(selFile, TextExporter.TEXT_EXPORT_FOLDER, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); + // try to import plain texts + if (textsFile.exists()) { + importTextsSingleFile(textsFile, swf); + } else { + importTextsMultipleFiles(selFile, swf); + } + } + + SWF.clearImageCache(); + reload(true); + } + } + + private String selectExportDir() { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("export.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + Main.startWork(translate("work.exporting") + "..."); + final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); + return selFile; + } + return null; + } + + public void export(final boolean onlySel) { + + final SWF swf = getCurrentSwf(); + List sel = getSelection(swf); + if (!onlySel) { + sel = null; + } else { + if (sel.isEmpty()) { + return; + } + } + final ExportDialog export = new ExportDialog(sel); + export.setVisible(true); + if (!export.cancelled) { + final String selFile = selectExportDir(); + if (selFile != null) { + final long timeBefore = System.currentTimeMillis(); + Main.startWork(translate("work.exporting") + "..."); + final ScriptExportMode exportMode = export.getValue(ScriptExportMode.class); + + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + try { + if (onlySel) { + exportSelection(errorHandler, selFile, export); + } else { + swf.exportImages(errorHandler, selFile + File.separator + "images", + new ImageExportSettings(export.getValue(ImageExportMode.class))); + swf.exportShapes(errorHandler, selFile + File.separator + "shapes", + new ShapeExportSettings(export.getValue(ShapeExportMode.class))); + swf.exportMorphShapes(errorHandler, selFile + File.separator + "morphshapes", + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class))); + swf.exportTexts(errorHandler, selFile + File.separator + "texts", + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get())); + swf.exportMovies(errorHandler, selFile + File.separator + "movies", + new MovieExportSettings(export.getValue(MovieExportMode.class))); + swf.exportSounds(errorHandler, selFile + File.separator + "sounds", + new SoundExportSettings(export.getValue(SoundExportMode.class))); + swf.exportBinaryData(errorHandler, selFile + File.separator + "binaryData", + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class))); + swf.exportFonts(errorHandler, selFile + File.separator + "fonts", + new FontExportSettings(export.getValue(FontExportMode.class))); + swf.exportFrames(errorHandler, selFile + File.separator + "frames", 0, null, + new FramesExportSettings(export.getValue(FramesExportMode.class))); + for (CharacterTag c : swf.characters.values()) { + if (c instanceof DefineSpriteTag) { + swf.exportFrames(errorHandler, selFile + File.separator + "frames", c.getCharacterId(), null, + new FramesExportSettings(export.getValue(FramesExportMode.class))); + } + } + swf.exportActionScript(errorHandler, selFile, exportMode, Configuration.parallelSpeedUp.get()); + } + } catch (Exception ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error during export", ex); + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); + } + return null; + } + + @Override + protected void done() { + Main.stopWork(); + long timeAfter = System.currentTimeMillis(); + final long timeMs = timeAfter - timeBefore; + + View.execInEventDispatchLater(new Runnable() { + + @Override + public void run() { + setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + } + }); + } + }.execute(); + + } + } + } + + public void restoreControlFlow(final boolean all) { + Main.startWork(translate("work.restoringControlFlow")); + if ((!all) || confirmExperimental()) { + new CancellableWorker() { + @Override + protected Object doInBackground() throws Exception { + int cnt = 0; + if (all) { + for (ABCContainerTag tag : abcPanel.swf.abcList) { + tag.getABC().restoreControlFlow(); + } + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + if (bi != -1) { + abcPanel.abc.bodies.get(bi).restoreControlFlow(abcPanel.abc.constants, abcPanel.decompiledTextArea.getCurrentTrait(), abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, abcPanel.decompiledTextArea.getCurrentTrait()); + } + return true; + } + + @Override + protected void done() { + Main.stopWork(); + View.showMessageDialog(null, translate("work.restoringControlFlow.complete")); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + abcPanel.reload(); + updateClassesList(); + } + }); + } + }.execute(); + } + } + + public void renameIdentifiers(final SWF swf) { + if (confirmExperimental()) { + final RenameType renameType = new RenameDialog().display(); + if (renameType != null) { + Main.startWork(translate("work.renaming.identifiers") + "..."); + new CancellableWorker() { + @Override + protected Integer doInBackground() throws Exception { + int cnt = swf.deobfuscateIdentifiers(renameType); + return cnt; + } + + @Override + protected void done() { + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + int cnt = get(); + Main.stopWork(); + View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", "" + cnt)); + swf.assignClassesToSymbols(); + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + reload(true); + } catch (Exception ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Error during renaming identifiers", ex); + Main.stopWork(); + View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); + } + } + }); + } + }.execute(); + } + } + } + + public void deobfuscate() { + if (deobfuscationDialog == null) { + deobfuscationDialog = new DeobfuscationDialog(); + } + deobfuscationDialog.setVisible(true); + if (deobfuscationDialog.ok) { + Main.startWork(translate("work.deobfuscating") + "..."); + new CancellableWorker() { + @Override + protected Object doInBackground() throws Exception { + try { + if (deobfuscationDialog.processAllCheckbox.isSelected()) { + for (ABCContainerTag tag : abcPanel.swf.abcList) { + if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { + tag.getABC().removeDeadCode(); + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { + tag.getABC().removeTraps(); + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { + tag.getABC().removeTraps(); + tag.getABC().restoreControlFlow(); + } + } + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); + if (bi != -1) { + if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) { + abcPanel.abc.bodies.get(bi).removeDeadCode(abcPanel.abc.constants, t, abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) { + abcPanel.abc.bodies.get(bi).removeTraps(abcPanel.abc.constants, abcPanel.abc, t, abcPanel.decompiledTextArea.getScriptLeaf().scriptIndex, abcPanel.decompiledTextArea.getClassIndex(), abcPanel.decompiledTextArea.getIsStatic(), ""/*FIXME*/); + } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) { + abcPanel.abc.bodies.get(bi).removeTraps(abcPanel.abc.constants, abcPanel.abc, t, abcPanel.decompiledTextArea.getScriptLeaf().scriptIndex, abcPanel.decompiledTextArea.getClassIndex(), abcPanel.decompiledTextArea.getIsStatic(), ""/*FIXME*/); + abcPanel.abc.bodies.get(bi).restoreControlFlow(abcPanel.abc.constants, t, abcPanel.abc.method_info.get(abcPanel.abc.bodies.get(bi).method_info)); + } + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc, t); + } + } catch (Exception ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Deobfuscation error", ex); + } + return true; + } + + @Override + protected void done() { + Main.stopWork(); + View.showMessageDialog(null, translate("work.deobfuscating.complete")); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + clearCache(); + abcPanel.reload(); + updateClassesList(); + } + }); + } + }.execute(); + } + } + + public void removeNonScripts(SWF swf) { + List tags = new ArrayList<>(swf.tags); + for (Tag tag : tags) { + System.out.println(tag.getClass()); + if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { + swf.removeTag(tag); + } + } + refreshTree(); + } + + public void refreshTree() { + previewPanel.clear(); + showCard(CARDEMPTYPANEL); + TreeItem treeItem = tagTree.getCurrentTreeItem(); + View.refreshTree(tagTree, new TagTreeModel(mainFrame, swfs)); + if (treeItem != null) { + setTreeItem(treeItem); + } + } + + public void refreshDecompiled() { + clearCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + reload(true); + updateClassesList(); + } + + public boolean saveText(TextTag textTag, String formattedText, String[] texts) { + try { + if (textTag.setFormattedText(new MissingCharacterHandler() { + @Override + public boolean handle(FontTag font, char character) { + String fontName = font.getSwf().sourceFontsMap.get(font.getFontId()); + if (fontName == null) { + fontName = font.getFontName(); + } + fontName = FontTag.findInstalledFontName(fontName); + Font f = new Font(fontName, font.getFontStyle(), 18); + if (!f.canDisplay(character)) { + View.showMessageDialog(null, translate("error.font.nocharacter").replace("%char%", "" + character), translate("error"), JOptionPane.ERROR_MESSAGE); + return false; + } + font.addCharacter(character, fontName); + return true; + + } + }, formattedText, texts)) { + textTag.setModified(true); + return true; + } + } catch (ParseException ex) { + View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", "" + ex.line), translate("error"), JOptionPane.ERROR_MESSAGE); + } + return false; + } + + public void removeTag(Tag tag) { + tag.getSwf().removeTag(tag); + refreshTree(); + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_SELECT_BKCOLOR: + Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.swfBackgroundColor); + if (newColor != null) { + View.swfBackgroundColor = newColor; + reload(true); + } + break; + case ACTION_REPLACE: { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof DefineSoundTag) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + fc.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".mp3")) + || (f.getName().toLowerCase().endsWith(".wav")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds"); + } + }); + fc.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".mp3")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds.mp3"); + } + }); + fc.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".wav")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.sounds.wav"); + } + }); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + DefineSoundTag ds = (DefineSoundTag) item; + int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; + if (selfile.getName().toLowerCase().endsWith(".mp3")) { + soundFormat = SoundFormat.FORMAT_MP3; + } + boolean ok = false; + try { + ok = ds.setSound(new FileInputStream(selfile), soundFormat); + } catch (IOException ex) { + //ignore + } + if (!ok) { + View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } else { + reload(true); + } + } + } + if (item instanceof ImageTag) { + ImageTag it = (ImageTag) item; + if (it.importSupported()) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + fc.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return (f.getName().toLowerCase().endsWith(".jpg")) + || (f.getName().toLowerCase().endsWith(".jpeg")) + || (f.getName().toLowerCase().endsWith(".gif")) + || (f.getName().toLowerCase().endsWith(".png")) + || (f.isDirectory()); + } + + @Override + public String getDescription() { + return translate("filter.images"); + } + }); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + SWF swf = it.getSwf(); + if (it instanceof DefineBitsTag) { + DefineBitsJPEG2Tag jpeg2Tag = new DefineBitsJPEG2Tag(swf, it.getOriginalHeaderData(), it.getOriginalData(), it.getPos(), it.getCharacterId(), data); + jpeg2Tag.setModified(true); + swf.tags.set(swf.tags.indexOf(it), jpeg2Tag); + swf.updateCharacters(); + refreshTree(); + setTreeItem(jpeg2Tag); + } else { + it.setImage(data); + } + SWF.clearImageCache(); + } catch (IOException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + } + } + } + if (item instanceof DefineBinaryDataTag) { + DefineBinaryDataTag bt = (DefineBinaryDataTag) item; + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File selfile = Helper.fixDialogFile(fc.getSelectedFile()); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + bt.binaryData = data; + bt.setModified(true); + reload(true); + } + } + } + break; + case ACTION_EXPAND_RECURSIVE: { + TreePath path = tagTree.getSelectionPath(); + if (path == null) { + return; + } + View.expandTreeNodesRecursive(tagTree, path, true); + } + break; + case ACTION_REMOVE_ITEM: + List sel = getSelected(tagTree); + + List tagsToRemove = new ArrayList<>(); + for (TreeNode o : sel) { + TreeItem tag = o.getItem(); + if (tag instanceof Tag) { + tagsToRemove.add((Tag) tag); + } else if (tag instanceof FrameNodeItem) { + FrameNodeItem frame = (FrameNodeItem) tag; + // todo: honfika: remove frame + //tagsToRemove.add(frame.getShowFrameTag()); + } + } + + if (tagsToRemove.size() == 1) { + Tag tag = tagsToRemove.get(0); + if (View.showConfirmDialog(this, translate("message.confirm.remove").replace("%item%", tag.toString()), translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + tag.getSwf().removeTag(tag); + refreshTree(); + } + } else if (tagsToRemove.size() > 1) { + if (View.showConfirmDialog(this, translate("message.confirm.removemultiple").replace("%count%", Integer.toString(tagsToRemove.size())), translate("message.confirm"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { + for (Tag tag : tagsToRemove) { + tag.getSwf().removeTag(tag); + } + refreshTree(); + } + } + break; + case ACTION_CLOSE_SWF: { + Main.closeFile(getCurrentSwfList()); + } + } + if (Main.isWorking()) { + return; + } + + switch (e.getActionCommand()) { + + case MainFrameRibbonMenu.ACTION_EXPORT_SEL: + export(true); + break; + } + } + + private int splitPos = 0; + + public void showDetail(String card) { + CardLayout cl = (CardLayout) (detailPanel.getLayout()); + cl.show(detailPanel, card); + if (card.equals(DETAILCARDEMPTYPANEL)) { + if (detailPanel.isVisible()) { + splitPos = splitPane2.getDividerLocation(); + detailPanel.setVisible(false); + } + } else { + if (!detailPanel.isVisible()) { + detailPanel.setVisible(true); + splitPane2.setDividerLocation(splitPos); + } + } + + } + + public void showCard(String card) { + CardLayout cl = (CardLayout) (displayPanel.getLayout()); + cl.show(displayPanel, card); + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + TreeNode treeNode = (TreeNode) e.getPath().getLastPathComponent(); + TreeItem treeItem = treeNode.getItem(); + if (!(treeItem instanceof SWFList)) { + SWF swf = treeItem.getSwf(); + /*if (swfs.contains(swf.swfList)) { //why? + updateUi(swf); + } else { + updateUi(); + }*/ + updateUi(swf); + } else { + updateUi(); + } + previewPanel.setEditText(false); + reload(false); + } + + public void unloadFlashPlayer() { + if (flashPanel != null) { + try { + flashPanel.close(); + } catch (IOException ex) { + // ignore + } + } + } + + private void stopFlashPlayer() { + if (flashPanel != null) { + if (!flashPanel.isStopped()) { + flashPanel.stopSWF(); + } + } + } + + public boolean isInternalFlashViewerSelected() { + return mainMenu.isInternalFlashViewerSelected(); + } + + public void reload(boolean forceReload) { + TreeNode treeNode = (TreeNode) tagTree.getLastSelectedPathComponent(); + if (treeNode == null) { + return; + } + + if (!forceReload && (treeNode == oldNode)) { + return; + } + + oldNode = treeNode; + + TreeItem tagObj = treeNode.getItem(); + + if (flashPanel != null) { + flashPanel.specialPlayback = false; + } + previewPanel.clear(); + if (soundThread != null) { + soundThread.pause(); + } + stopFlashPlayer(); + if (tagObj instanceof ScriptPack) { + final ScriptPack scriptLeaf = (ScriptPack) tagObj; + final List abcList = scriptLeaf.abc.swf.abcList; + if (setSourceWorker != null) { + setSourceWorker.cancel(true); + setSourceWorker = null; + } + if (!Main.isWorking()) { + Main.startWork(AppStrings.translate("work.decompiling") + "..."); + CancellableWorker worker = new CancellableWorker() { + + @Override + protected Void doInBackground() throws Exception { + int classIndex = -1; + for (Trait t : scriptLeaf.abc.script_info.get(scriptLeaf.scriptIndex).traits.traits) { + if (t instanceof TraitClass) { + classIndex = ((TraitClass) t).class_info; + break; + } + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); + abcPanel.navigator.setABC(abcList, scriptLeaf.abc); + abcPanel.navigator.setClassIndex(classIndex, scriptLeaf.scriptIndex); + abcPanel.setAbc(scriptLeaf.abc); + abcPanel.decompiledTextArea.setScript(scriptLeaf, abcList); + abcPanel.decompiledTextArea.setClassIndex(classIndex); + abcPanel.decompiledTextArea.setNoTrait(); + return null; + } + + @Override + protected void done() { + Main.stopWork(); + + View.execInEventDispatch(new Runnable() { + + @Override + public void run() { + try { + get(); + } catch (CancellationException ex) { + abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("work.canceled")); + } catch (Exception ex) { + abcPanel.decompiledTextArea.setText("//" + AppStrings.translate("decompilationError") + ": " + ex); + } + } + }); + } + }; + worker.execute(); + setSourceWorker = worker; + Main.startWork(translate("work.decompiling") + "...", worker); + } + + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPT3PANEL); + return; + } else { + showDetail(DETAILCARDEMPTYPANEL); + } + + previewPanel.setImageReplaceButtonVisible(false); + + if (treeNode instanceof StringNode) { + showCard(CARDFOLDERPREVIEWPANEL); + showFolderPreview(treeNode); + } else if (treeNode instanceof SWFNode) { + SWFNode swfNode = (SWFNode) treeNode; + SWF swf = swfNode.getItem(); + showCard(CARDPREVIEWPANEL); + if (isInternalFlashViewerSelected()) { + previewPanel.showImagePanel(swf, swf, -1); + } else { + previewPanel.setParametersPanelVisible(false); + if (flashPanel != null) { + previewPanel.showFlashViewerPanel(); + previewPanel.showSwf(swf); + } + } + } else if (tagObj instanceof DefineBinaryDataTag) { + DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tagObj; + showCard(CARDPREVIEWPANEL); + previewPanel.showBinaryPanel(binaryTag.binaryData); + } else if (tagObj instanceof ASMSource) { + showCard(CARDACTIONSCRIPTPANEL); + actionPanel.setSource((ASMSource) tagObj, !forceReload); + } else if (tagObj instanceof ImageTag) { + ImageTag imageTag = (ImageTag) tagObj; + previewPanel.setImageReplaceButtonVisible(imageTag.importSupported()); + showCard(CARDPREVIEWPANEL); + previewPanel.showImagePanel(imageTag.getImage()); + } else if ((tagObj instanceof DrawableTag) && (!(tagObj instanceof TextTag)) && (!(tagObj instanceof FontTag)) && (isInternalFlashViewerSelected())) { + final Tag tag = (Tag) tagObj; + showCard(CARDPREVIEWPANEL); + DrawableTag d = (DrawableTag) tag; + Timelined timelined = null; + if (tagObj instanceof Timelined && !(tagObj instanceof ButtonTag)) { + timelined = (Timelined) tag; + } else { + timelined = makeTimelined(tag); + } + + previewPanel.setParametersPanelVisible(false); + previewPanel.showImagePanel(timelined, tag.getSwf(), -1); + } else if ((tagObj instanceof FontTag) && (isInternalFlashViewerSelected())) { + FontTag fontTag = (FontTag) tagObj; + showCard(CARDPREVIEWPANEL); + showFontTag(fontTag); + } else if ((tagObj instanceof TextTag) && (isInternalFlashViewerSelected())) { + TextTag textTag = (TextTag) tagObj; + showCard(CARDPREVIEWPANEL); + showTextTag(textTag); + } else if (tagObj instanceof FrameNodeItem && ((FrameNodeItem) tagObj).isDisplayed() && (isInternalFlashViewerSelected())) { + showCard(CARDPREVIEWPANEL); + FrameNodeItem fn = (FrameNodeItem) tagObj; + SWF swf = fn.getSwf(); + List controlTags = swf.tags; + int containerId = 0; + RECT rect = swf.displayRect; + int totalFrameCount = swf.frameCount; + Timelined timelined = swf; + if (fn.getParent() instanceof DefineSpriteTag) { + DefineSpriteTag parentSprite = (DefineSpriteTag) fn.getParent(); + controlTags = parentSprite.subTags; + containerId = parentSprite.spriteId; + rect = parentSprite.getRect(); + totalFrameCount = parentSprite.frameCount; + timelined = parentSprite; + } + previewPanel.showImagePanel(timelined, swf, fn.getFrame() - 1); + } else if ((tagObj instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { + showCard(CARDPREVIEWPANEL); + previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); + previewPanel.setImageReplaceButtonVisible(tagObj instanceof DefineSoundTag); + try { + soundThread = new SoundTagPlayer((SoundTag) tagObj, Integer.MAX_VALUE); + previewPanel.setMedia(soundThread); + soundThread.play(); + } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } else if (((tagObj instanceof FrameNodeItem) && ((FrameNodeItem) tagObj).isDisplayed()) || ((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag) || (tagObj instanceof SoundStreamHeadTypeTag)) { + showCard(CARDPREVIEWPANEL); + previewPanel.createAndShowTempSwf(tagObj); + + if (tagObj instanceof TextTag) { + showTextTag((TextTag) tagObj); + } else if (tagObj instanceof FontTag) { + showFontTag((FontTag) tagObj); + } else { + previewPanel.setParametersPanelVisible(false); + } + } else if (tagObj instanceof Tag) { + showCard(CARDPREVIEWPANEL); + previewPanel.showGenericTagPanel((Tag) tagObj); + } else { + showCard(CARDEMPTYPANEL); + } + } + + private void showFontTag(FontTag ft) { + + previewPanel.showFontPanel(ft); + } + + private void showTextTag(TextTag textTag) { + + previewPanel.showTextPanel(textTag); + } + + private void showFolderPreview(TreeNode treeNode) { + folderPreviewPanel.removeAll(); + StringNode stringNode = (StringNode) treeNode; + StringItem item = stringNode.getItem(); + String folderName = item.getName(); + SWF swf = item.swf; + JPanel panel = folderPreviewPanel; + switch (folderName) { + case TagTreeModel.FOLDER_SHAPES: + for (Tag tag : swf.tags) { + if (tag instanceof ShapeTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_MORPHSHAPES: + for (Tag tag : swf.tags) { + if (tag instanceof MorphShapeTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_SPRITES: + for (Tag tag : swf.tags) { + if (tag instanceof DefineSpriteTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_BUTTONS: + for (Tag tag : swf.tags) { + if (tag instanceof ButtonTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_FONTS: + for (Tag tag : swf.tags) { + if (tag instanceof FontTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_FRAMES: + for (TreeNode subNode : treeNode.subNodes) { + FrameNode frameNode = (FrameNode) subNode; + FrameNodeItem fn = frameNode.getItem(); + Component c = PreviewImage.createFolderPreviewImage(this, fn); + if (c != null) { + panel.add(c); + } + } + break; + case TagTreeModel.FOLDER_IMAGES: + for (Tag tag : swf.tags) { + if (tag instanceof ImageTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + case TagTreeModel.FOLDER_TEXTS: + for (Tag tag : swf.tags) { + if (tag instanceof TextTag) { + Component c = PreviewImage.createFolderPreviewImage(this, tag); + if (c != null) { + panel.add(c); + } + } + } + break; + } + panel.revalidate(); + panel.repaint(); + } + + public void expandSwfNodes() { + TreeModel model = tagTree.getModel(); + Object node = model.getRoot(); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + tagTree.expandPath(new TreePath(new Object[]{node, child})); + } + } + + private boolean isFreeing; + + @Override + public boolean isFreeing() { + return isFreeing; + } + + @Override + public void free() { + isFreeing = true; + Helper.emptyObject(mainMenu); + Helper.emptyObject(statusPanel); + Helper.emptyObject(this); + } + + public void setErrorState(ErrorState errorState) { + statusPanel.setErrorState(errorState); + } + + public void timeline() { + final SWF swf = getCurrentSwf(); + if (swf != null) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item instanceof DefineSpriteTag) { + TimelineFrame tf = new TimelineFrame((DefineSpriteTag) item); + tf.setVisible(true); + return; + } + + TimelineFrame tf = new TimelineFrame(swf); + tf.setVisible(true); + } + } + + public static Timelined makeTimelined(final Tag tag) { + + return new Timelined() { + + private Timeline tim; + + @Override + public Timeline getTimeline() { + if (tim != null) { + return tim; + } + tim = new Timeline(tag.getSwf(), new ArrayList(), ((CharacterTag) tag).getCharacterId(), getRect()); + if (tag instanceof MorphShapeTag) { + tim.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; + int framesCnt = tim.frameRate * MORPH_SHAPE_ANIMATION_LENGTH; + for (int i = 0; i < framesCnt; i++) { + Frame f = new Frame(tim); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + ds.ratio = i * 65535 / framesCnt; + f.layers.put(1, ds); + tim.frames.add(f); + } + } else if (tag instanceof FontTag) { + int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); + for (int i = 0; i < pageCount; i++) { + Frame f = new Frame(tim); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + ds.time = i; + f.layers.put(1, ds); + tim.frames.add(f); + } + } else { + Frame f = new Frame(tim); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + tim.frames.add(f); + } + tim.displayRect = getRect(); + return tim; + } + + @Override + public void resetTimeline() { + tim = null; + } + + @Override + public RECT getRect() { + return ((BoundedTag) tag).getRect(); + } + }; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MyCommandButtonUI.java b/src/com/jpexs/decompiler/flash/gui/MyCommandButtonUI.java index 3e6835635..f8c4dbd0f 100644 --- a/src/com/jpexs/decompiler/flash/gui/MyCommandButtonUI.java +++ b/src/com/jpexs/decompiler/flash/gui/MyCommandButtonUI.java @@ -1,95 +1,94 @@ -/* - * Copyright (C) 2014 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.gui; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JComponent; -import javax.swing.SwingUtilities; -import javax.swing.plaf.ComponentUI; -import org.pushingpixels.flamingo.api.common.JCommandButton; -import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; -import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; -import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; -import org.pushingpixels.substance.flamingo.common.ui.SubstanceCommandButtonUI; - -/** - * - * Own CommandButtonUI because original Flamingo UI throws Exception in some cases - * - * @author JPEXS - */ -public class MyCommandButtonUI extends SubstanceCommandButtonUI{ - public static ComponentUI createUI(JComponent comp) { - return new MyCommandButtonUI((JCommandButton)comp); - } - - public MyCommandButtonUI(JCommandButton jcb) { - super(jcb); - } - - @Override - protected void installListeners() { - super.installListeners(); - this.commandButton.removeActionListener(this.disposePopupsActionListener); - this.disposePopupsActionListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if(commandButton == null){ //Added by JPEXS - return; - } - boolean toDismiss = !Boolean.TRUE.equals(commandButton - .getClientProperty(DONT_DISPOSE_POPUPS)); - if (toDismiss) { - JCommandPopupMenu menu = (JCommandPopupMenu) SwingUtilities - .getAncestorOfClass(JCommandPopupMenu.class, - commandButton); - if (menu != null) { - toDismiss = menu.isToDismissOnChildClick(); - } - } - if (toDismiss) { - if (SwingUtilities.getAncestorOfClass(JPopupPanel.class, - commandButton) != null) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - // command button may be cleared if the - // button click resulted in LAF switch - if (commandButton != null) { - // clear the active states - commandButton.getActionModel().setPressed( - false); - commandButton.getActionModel().setRollover( - false); - commandButton.getActionModel().setArmed( - false); - } - } - }); - } - PopupPanelManager.defaultManager().hidePopups(null); - } - } - }; - this.commandButton.addActionListener(disposePopupsActionListener); - } - - - -} +/* + * Copyright (C) 2014 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.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ComponentUI; +import org.pushingpixels.flamingo.api.common.JCommandButton; +import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; +import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; +import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager; +import org.pushingpixels.substance.flamingo.common.ui.SubstanceCommandButtonUI; + +/** + * + * Own CommandButtonUI because original Flamingo UI throws Exception in some + * cases + * + * @author JPEXS + */ +public class MyCommandButtonUI extends SubstanceCommandButtonUI { + + public static ComponentUI createUI(JComponent comp) { + return new MyCommandButtonUI((JCommandButton) comp); + } + + public MyCommandButtonUI(JCommandButton jcb) { + super(jcb); + } + + @Override + protected void installListeners() { + super.installListeners(); + this.commandButton.removeActionListener(this.disposePopupsActionListener); + this.disposePopupsActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (commandButton == null) { //Added by JPEXS + return; + } + boolean toDismiss = !Boolean.TRUE.equals(commandButton + .getClientProperty(DONT_DISPOSE_POPUPS)); + if (toDismiss) { + JCommandPopupMenu menu = (JCommandPopupMenu) SwingUtilities + .getAncestorOfClass(JCommandPopupMenu.class, + commandButton); + if (menu != null) { + toDismiss = menu.isToDismissOnChildClick(); + } + } + if (toDismiss) { + if (SwingUtilities.getAncestorOfClass(JPopupPanel.class, + commandButton) != null) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + // command button may be cleared if the + // button click resulted in LAF switch + if (commandButton != null) { + // clear the active states + commandButton.getActionModel().setPressed( + false); + commandButton.getActionModel().setRollover( + false); + commandButton.getActionModel().setArmed( + false); + } + } + }); + } + PopupPanelManager.defaultManager().hidePopups(null); + } + } + }; + this.commandButton.addActionListener(disposePopupsActionListener); + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 795eea69c..8bfb93479 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1,920 +1,923 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.player.MediaDisplay; -import com.jpexs.decompiler.flash.gui.player.PlayerControls; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.AloneTag; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.FlowLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSplitPane; -import javax.swing.SwingConstants; - -/** - * - * @author JPEXS - */ -public class PreviewPanel extends JSplitPane implements ActionListener { - - private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; - private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; - private static final String GENERIC_TAG_CARD = "GENERICTAG"; - private static final String BINARY_TAG_CARD = "BINARYTAG"; - - private static final String CARDTEXTPANEL = "Text card"; - private static final String CARDFONTPANEL = "Font card"; - - private static final String ACTION_EDIT_GENERIC_TAG = "EDITGENERICTAG"; - private static final String ACTION_SAVE_GENERIC_TAG = "SAVEGENERICTAG"; - private static final String ACTION_CANCEL_GENERIC_TAG = "CANCELGENERICTAG"; - - private static final String ACTION_PREV_FONTS = "PREVFONTS"; - private static final String ACTION_NEXT_FONTS = "NEXTFONTS"; - - private final MainPanel mainPanel; - private final JPanel viewerCards; - - private PlayerControls flashControls; - private final FlashPlayerPanel flashPanel; - private File tempFile; - - private ImagePanel imagePanel; - private PlayerControls imagePlayControls; - - private BinaryPanel binaryPanel; - private GenericTagPanel genericTagPanel; - - private JPanel displayWithPreview; - - // Image tag buttons - private JButton replaceImageButton; - private JButton prevFontsButton; - private JButton nextFontsButton; - - // Binary tag buttons - private JButton replaceBinaryButton; - - // Generic tag buttons - private JButton editButton; - private JButton saveButton; - private JButton cancelButton; - - private JPanel parametersPanel; - private FontPanel fontPanel; - private int fontPageNum; - private TextPanel textPanel; - - private boolean splitsInited; - - public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { - super(JSplitPane.HORIZONTAL_SPLIT); - this.mainPanel = mainPanel; - this.flashPanel = flashPanel; - - addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - if (splitsInited && getRightComponent().isVisible()) { - Configuration.guiPreviewSplitPaneDividerLocation.set((int) pce.getNewValue()); - } - } - }); - - viewerCards = new JPanel(); - viewerCards.setLayout(new CardLayout()); - - viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); - viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); - viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); - viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); - setLeftComponent(viewerCards); - - createParametersPanel(); - - showCardLeft(FLASH_VIEWER_CARD); - } - - private void createParametersPanel() { - displayWithPreview = new JPanel(new CardLayout()); - - textPanel = new TextPanel(mainPanel); - displayWithPreview.add(textPanel, CARDTEXTPANEL); - - fontPanel = new FontPanel(mainPanel); - displayWithPreview.add(fontPanel, CARDFONTPANEL); - - JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); - paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); - //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - parametersPanel = new JPanel(new BorderLayout()); - parametersPanel.add(paramsLabel, BorderLayout.NORTH); - parametersPanel.add(displayWithPreview, BorderLayout.CENTER); - setRightComponent(parametersPanel); - } - - private JPanel createImageButtonsPanel() { - replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); - replaceImageButton.setActionCommand(MainPanel.ACTION_REPLACE); - replaceImageButton.addActionListener(mainPanel); - replaceImageButton.setVisible(false); - - prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); - prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); - prevFontsButton.setActionCommand(ACTION_PREV_FONTS); - prevFontsButton.addActionListener(this); - prevFontsButton.setVisible(false); - - nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); - nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); - nextFontsButton.setActionCommand(ACTION_NEXT_FONTS); - nextFontsButton.addActionListener(this); - nextFontsButton.setVisible(false); - - ButtonsPanel imageButtonsPanel = new ButtonsPanel(); - imageButtonsPanel.add(replaceImageButton); - imageButtonsPanel.add(prevFontsButton); - imageButtonsPanel.add(nextFontsButton); - return imageButtonsPanel; - } - - private JPanel createBinaryButtonsPanel() { - replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); - replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); - replaceBinaryButton.setActionCommand(MainPanel.ACTION_REPLACE); - replaceBinaryButton.addActionListener(mainPanel); - - ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); - binaryButtonsPanel.add(replaceBinaryButton); - return binaryButtonsPanel; - } - - private JPanel createGenericTagButtonsPanel() { - editButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); - editButton.setMargin(new Insets(3, 3, 3, 10)); - editButton.setActionCommand(ACTION_EDIT_GENERIC_TAG); - editButton.addActionListener(this); - saveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - saveButton.setMargin(new Insets(3, 3, 3, 10)); - saveButton.setActionCommand(ACTION_SAVE_GENERIC_TAG); - saveButton.addActionListener(this); - saveButton.setVisible(false); - cancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - cancelButton.setMargin(new Insets(3, 3, 3, 10)); - cancelButton.setActionCommand(ACTION_CANCEL_GENERIC_TAG); - cancelButton.addActionListener(this); - cancelButton.setVisible(false); - - ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); - genericTagButtonsPanel.add(editButton); - genericTagButtonsPanel.add(saveButton); - genericTagButtonsPanel.add(cancelButton); - return genericTagButtonsPanel; - } - - private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { - JPanel pan = new JPanel(new BorderLayout()); - JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); - prevLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - pan.add(prevLabel, BorderLayout.NORTH); - - Component leftComponent; - if (flashPanel != null) { - JPanel flashPlayPanel = new JPanel(new BorderLayout()); - flashPlayPanel.add(flashPanel, BorderLayout.CENTER); - - JPanel bottomPanel = new JPanel(new BorderLayout()); - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton selectColorButton = new JButton(View.getIcon("color16")); - selectColorButton.addActionListener(mainPanel); - selectColorButton.setActionCommand(MainPanel.ACTION_SELECT_BKCOLOR); - selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); - buttonsPanel.add(selectColorButton); - bottomPanel.add(buttonsPanel, BorderLayout.EAST); - - flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH); - - JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); - flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); - flashPlayPanel2.add(flashControls = new PlayerControls(flashPanel), BorderLayout.SOUTH); - leftComponent = flashPlayPanel2; - } else { - JPanel swtPanel = new JPanel(new BorderLayout()); - swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); - swtPanel.setBackground(View.DEFAULT_BACKGROUND_COLOR); - leftComponent = swtPanel; - } - - pan.add(leftComponent, BorderLayout.CENTER); - return pan; - } - - private JPanel createImagesCard() { - JPanel shapesCard = new JPanel(new BorderLayout()); - JPanel previewPanel = new JPanel(new BorderLayout()); - - JPanel previewCnt = new JPanel(new BorderLayout()); - imagePanel = new ImagePanel(); - previewCnt.add(imagePanel, BorderLayout.CENTER); - previewCnt.add(imagePlayControls = new PlayerControls(imagePanel), BorderLayout.SOUTH); - imagePlayControls.setMedia(imagePanel); - previewPanel.add(previewCnt, BorderLayout.CENTER); - JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); - prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); - //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - previewPanel.add(prevIntLabel, BorderLayout.NORTH); - - shapesCard.add(previewPanel, BorderLayout.CENTER); - - shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); - return shapesCard; - } - - private JPanel createBinaryCard() { - JPanel binaryCard = new JPanel(new BorderLayout()); - binaryPanel = new BinaryPanel(); - binaryCard.add(binaryPanel, BorderLayout.CENTER); - binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); - return binaryCard; - } - - private JPanel createGenericTagCard() { - JPanel genericTagCard = new JPanel(new BorderLayout()); - genericTagPanel = new GenericTagTreePanel(); - genericTagCard.add(genericTagPanel, BorderLayout.CENTER); - genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); - return genericTagCard; - } - - private void showCardLeft(String card) { - CardLayout cl = (CardLayout) (viewerCards.getLayout()); - cl.show(viewerCards, card); - } - - private void showCardRight(String card) { - CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); - cl.show(displayWithPreview, card); - } - - public void setSplitsInited() { - splitsInited = true; - } - - public TextPanel getTextPanel() { - return textPanel; - } - - public void setParametersPanelVisible(boolean show) { - parametersPanel.setVisible(show); - } - - public void showFlashViewerPanel() { - parametersPanel.setVisible(false); - showCardLeft(FLASH_VIEWER_CARD); - } - - public void showImagePanel(Timelined timelined, SWF swf, int frame) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setTimelined(timelined, swf, frame); - } - - public void showImagePanel(SerializableImage image) { - showCardLeft(DRAW_PREVIEW_CARD); - parametersPanel.setVisible(false); - imagePlayControls.setMedia(imagePanel); - imagePanel.setImage(image); - } - - public void setMedia(MediaDisplay media) { - imagePlayControls.setMedia(media); - } - - public void showFontPanel(FontTag fontTag) { - fontPageNum = 0; - showFontPage(fontTag); - - showCardRight(CARDFONTPANEL); - parametersPanel.setVisible(true); - setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(getWidth() / 2)); - fontPanel.showFontTag(fontTag); - - int pageCount = getFontPageCount(fontTag); - if (pageCount > 1) { - prevFontsButton.setVisible(true); - nextFontsButton.setVisible(true); - } - } - - private void showFontPage(FontTag fontTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - - public static int getFontPageCount(FontTag fontTag) { - int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (pageCount < 1) { - pageCount = 1; - } - return pageCount; - } - - public void showTextPanel(TextTag textTag) { - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); - } - - showCardRight(CARDTEXTPANEL); - parametersPanel.setVisible(true); - setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(getWidth() / 2)); - textPanel.setText(textTag.getFormattedText()); - } - - public void setEditText(boolean edit) { - textPanel.setEditText(edit); - } - - public void clear() { - imagePanel.stop(); - binaryPanel.setBinaryData(null); - genericTagPanel.clear(); - fontPanel.clear(); - } - - public void showBinaryPanel(byte[] data) { - showCardLeft(BINARY_TAG_CARD); - binaryPanel.setBinaryData(data); - parametersPanel.setVisible(false); - } - - public void showGenericTagPanel(Tag tag) { - showCardLeft(GENERIC_TAG_CARD); - editButton.setVisible(true); - saveButton.setVisible(false); - cancelButton.setVisible(false); - genericTagPanel.setEditMode(false, tag); - parametersPanel.setVisible(false); - } - - public void setImageReplaceButtonVisible(boolean show) { - replaceImageButton.setVisible(show); - prevFontsButton.setVisible(false); - nextFontsButton.setVisible(false); - } - - private static Tag classicTag(Tag t) { - if (t instanceof DefineCompactedFont) { - return ((DefineCompactedFont) t).toClassicFont(); - } - return t; - } - - public void createAndShowTempSwf(TreeItem tagObj) { - SWF swf; - try { - if (tempFile != null) { - tempFile.delete(); - } - tempFile = File.createTempFile("temp", ".swf"); - tempFile.deleteOnExit(); - - Color backgroundColor = View.swfBackgroundColor; - - if (tagObj instanceof FontTag) { //Fonts are always black on white - backgroundColor = View.DEFAULT_BACKGROUND_COLOR; - } - - if (tagObj instanceof FrameNodeItem) { - FrameNodeItem fn = (FrameNodeItem) tagObj; - swf = fn.getSwf(); - if (fn.getParent() == null) { - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - } - } else { - Tag tag = (Tag) tagObj; - swf = tag.getSwf(); - } - - int frameCount = 1; - int frameRate = swf.frameRate; - HashMap videoFrames = new HashMap<>(); - if (tagObj instanceof DefineVideoStreamTag) { - DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; - SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); - frameCount = videoFrames.size(); - } - - List soundFrames = new ArrayList<>(); - if (tagObj instanceof SoundStreamHeadTypeTag) { - soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); - frameCount = soundFrames.size(); - } - - if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; - frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; - } - - if (tagObj instanceof DefineSoundTag) { - frameCount = 1; - } - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - SWFOutputStream sos2 = new SWFOutputStream(baos, 10); - int width = swf.displayRect.Xmax - swf.displayRect.Xmin; - int height = swf.displayRect.Ymax - swf.displayRect.Ymin; - sos2.writeRECT(swf.displayRect); - sos2.writeUI8(0); - sos2.writeUI8(frameRate); - sos2.writeUI16(frameCount); //framecnt - - /*FileAttributesTag fa = new FileAttributesTag(); - sos2.writeTag(fa); - */ - new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); - - if (tagObj instanceof FrameNodeItem) { - FrameNodeItem fn = (FrameNodeItem) tagObj; - Tag parent = fn.getParent(); - List subs = new ArrayList<>(); - if (parent == null) { - subs.addAll(swf.tags); - } else { - if (parent instanceof Container) { - subs = ((Container) parent).getSubItems(); - } - } - List doneCharacters = new ArrayList<>(); - int frameCnt = 1; - for (ContainerItem item : subs) { - if (item instanceof ShowFrameTag) { - frameCnt++; - continue; - } - if (frameCnt > fn.getFrame()) { - break; - } - - if (item instanceof DoActionTag || item instanceof DoInitActionTag) { - // todo: Maybe DoABC tags should be removed, too - continue; - } - - Tag t = (Tag) item; - Set needed = t.getDeepNeededCharacters(swf.characters); - for (int n : needed) { - if (!doneCharacters.contains(n)) { - classicTag(swf.characters.get(n)).writeTag(sos2); - doneCharacters.add(n); - } - } - if (t instanceof CharacterTag) { - int characterId = ((CharacterTag) t).getCharacterId(); - if (!doneCharacters.contains(characterId)) { - doneCharacters.add(((CharacterTag) t).getCharacterId()); - } - } - classicTag(t).writeTag(sos2); - - if (parent != null) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; - int chid = pot.getCharacterId(); - int depth = pot.getDepth(); - MATRIX mat = pot.getMatrix(); - if (mat == null) { - mat = new MATRIX(); - } - mat = Helper.deepCopy(mat); - if (parent instanceof BoundedTag) { - RECT r = ((BoundedTag) parent).getRect(); - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX += width / 2; - mat.translateY += height / 2; - } - new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); - - } - } - } - new ShowFrameTag(swf).writeTag(sos2); - } else { - - if (tagObj instanceof DefineBitsTag) { - JPEGTablesTag jtt = swf.jtt; - if (jtt != null) { - jtt.writeTag(sos2); - } - } else if (tagObj instanceof AloneTag) { - } else { - Set needed = ((Tag) tagObj).getDeepNeededCharacters(swf.characters); - for (int n : needed) { - classicTag(swf.characters.get(n)).writeTag(sos2); - } - } - - classicTag((Tag) tagObj).writeTag(sos2); - - int chtId = 0; - if (tagObj instanceof CharacterTag) { - chtId = ((CharacterTag) tagObj).getCharacterId(); - } - - MATRIX mat = new MATRIX(); - mat.hasRotate = false; - mat.hasScale = false; - mat.translateX = 0; - mat.translateY = 0; - if (tagObj instanceof BoundedTag) { - RECT r = ((BoundedTag) tagObj).getRect(); - mat.translateX = -r.Xmin; - mat.translateY = -r.Ymin; - mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; - mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; - } else { - mat.translateX = width / 4; - mat.translateY = height / 4; - } - if (tagObj instanceof FontTag) { - - int countGlyphsTotal = ((FontTag) tagObj).getGlyphShapeTable().size(); - int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); - int fontId = ((FontTag) tagObj).getFontId(); - int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); - int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); - int x = 0; - int y = 1; - int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; - countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); - for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { - if (x >= cols) { - x = 0; - y++; - } - List rec = new ArrayList<>(); - TEXTRECORD tr = new TEXTRECORD(); - int textHeight = height / rows; - tr.fontId = fontId; - tr.styleFlagsHasFont = true; - tr.textHeight = textHeight; - tr.glyphEntries = new GLYPHENTRY[1]; - tr.styleFlagsHasColor = true; - tr.textColor = new RGB(0, 0, 0); - tr.glyphEntries[0] = new GLYPHENTRY(); - tr.glyphEntries[0].glyphAdvance = 0; - tr.glyphEntries[0].glyphIndex = f; - rec.add(tr); - mat.translateX = x * width / cols; - mat.translateY = y * height / rows; - new DefineTextTag(swf, 999 + f, new RECT(0, width, 0, height), new MATRIX(), rec).writeTag(sos2); - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, mat, null, 0, null, 0, null).writeTag(sos2); - x++; - } - new ShowFrameTag(swf).writeTag(sos2); - } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { - new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof SoundStreamHeadTypeTag) { - for (SoundStreamBlockTag blk : soundFrames) { - blk.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - } else if (tagObj instanceof DefineSoundTag) { - ExportAssetsTag ea = new ExportAssetsTag(swf); - DefineSoundTag ds = (DefineSoundTag) tagObj; - ea.tags.add(ds.soundId); - ea.names.add("my_define_sound"); - ea.writeTag(sos2); - List actions; - DoActionTag doa; - - doa = new DoActionTag(swf, new byte[0], new byte[0], 0); - actions = ASMParser.parse(0, 0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, 0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push 9999 0.0 2 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, 0, false, - "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" - + "StopSounds\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\" 0.0 \"Sound\"\n" - + "NewObject\n" - + "SetMember\n" - + "Push \"my_define_sound\" 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"attachSound\"\n" - + "CallMethod\n" - + "Pop\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"onSoundComplete\"\n" - + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" - + "Push 0.0 register1 \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "}\n" - + "SetMember\n" - + "Push \"_root\"\n" - + "GetVariable\n" - + "Push \"execParam\"\n" - + "GetMember\n" - + "Push 1 \"_root\"\n" - + "GetVariable\n" - + "Push \"my_sound\"\n" - + "GetMember\n" - + "Push \"start\"\n" - + "CallMethod\n" - + "Pop\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - actions = ASMParser.parse(0, 0, false, - "StopSounds\n" - + "Stop", swf.version, false); - doa.setActions(actions); - doa.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - - new ShowFrameTag(swf).writeTag(sos2); - if (flashPanel != null) { - flashPanel.specialPlayback = true; - } - } else if (tagObj instanceof DefineVideoStreamTag) { - - new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - List frs = new ArrayList<>(videoFrames.values()); - Collections.sort(frs, new Comparator() { - @Override - public int compare(VideoFrameTag o1, VideoFrameTag o2) { - return o1.frameNum - o2.frameNum; - } - }); - boolean first = true; - int ratio = 0; - for (VideoFrameTag f : frs) { - if (!first) { - ratio++; - new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); - } - f.writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - first = false; - } - } else { - new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); - new ShowFrameTag(swf).writeTag(sos2); - } - - }//not showframe - - new EndTag(swf).writeTag(sos2); - data = baos.toByteArray(); - } - - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); - sos.write("FWS".getBytes()); - sos.write(swf.version); - sos.writeUI32(sos.getPos() + data.length + 4); - sos.write(data); - fos.flush(); - } - - if (flashPanel != null) { - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); - } - showFlashViewerPanel(); - } catch (IOException | com.jpexs.decompiler.flash.action.parser.ParseException ex) { - Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void showSwf(SWF swf) { - Color backgroundColor = View.DEFAULT_BACKGROUND_COLOR; - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); - break; - } - } - - if (tempFile != null) { - tempFile.delete(); - } - try { - tempFile = File.createTempFile("temp", ".swf"); - tempFile.deleteOnExit(); - swf.saveTo(new BufferedOutputStream(new FileOutputStream(tempFile))); - flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); - } catch (IOException iex) { - Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_EDIT_GENERIC_TAG: { - TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof Tag) { - editButton.setVisible(false); - saveButton.setVisible(true); - cancelButton.setVisible(true); - //genericTagPanel.generateEditControls((Tag) item, false); - genericTagPanel.setEditMode(true, (Tag) item); - } - } - break; - case ACTION_SAVE_GENERIC_TAG: { - genericTagPanel.save(); - mainPanel.refreshTree(); - mainPanel.setTreeItem(genericTagPanel.getTag()); - editButton.setVisible(true); - saveButton.setVisible(false); - cancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - break; - case ACTION_CANCEL_GENERIC_TAG: { - editButton.setVisible(true); - saveButton.setVisible(false); - cancelButton.setVisible(false); - genericTagPanel.setEditMode(false, null); - } - break; - case ACTION_PREV_FONTS: { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + pageCount - 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - break; - case ACTION_NEXT_FONTS: { - FontTag fontTag = fontPanel.getFontTag(); - int pageCount = getFontPageCount(fontTag); - fontPageNum = (fontPageNum + 1) % pageCount; - if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); - } - } - break; - } - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.player.MediaDisplay; +import com.jpexs.decompiler.flash.gui.player.PlayerControls; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.AloneTag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.SwingConstants; + +/** + * + * @author JPEXS + */ +public class PreviewPanel extends JSplitPane implements ActionListener { + + private static final String FLASH_VIEWER_CARD = "FLASHVIEWER"; + private static final String DRAW_PREVIEW_CARD = "DRAWPREVIEW"; + private static final String GENERIC_TAG_CARD = "GENERICTAG"; + private static final String BINARY_TAG_CARD = "BINARYTAG"; + + private static final String CARDTEXTPANEL = "Text card"; + private static final String CARDFONTPANEL = "Font card"; + + private static final String ACTION_EDIT_GENERIC_TAG = "EDITGENERICTAG"; + private static final String ACTION_SAVE_GENERIC_TAG = "SAVEGENERICTAG"; + private static final String ACTION_CANCEL_GENERIC_TAG = "CANCELGENERICTAG"; + + private static final String ACTION_PREV_FONTS = "PREVFONTS"; + private static final String ACTION_NEXT_FONTS = "NEXTFONTS"; + + private final MainPanel mainPanel; + private final JPanel viewerCards; + + private PlayerControls flashControls; + private final FlashPlayerPanel flashPanel; + private File tempFile; + + private ImagePanel imagePanel; + private PlayerControls imagePlayControls; + + private BinaryPanel binaryPanel; + private GenericTagPanel genericTagPanel; + + private JPanel displayWithPreview; + + // Image tag buttons + private JButton replaceImageButton; + private JButton prevFontsButton; + private JButton nextFontsButton; + + // Binary tag buttons + private JButton replaceBinaryButton; + + // Generic tag buttons + private JButton editButton; + private JButton saveButton; + private JButton cancelButton; + + private JPanel parametersPanel; + private FontPanel fontPanel; + private int fontPageNum; + private TextPanel textPanel; + + private boolean splitsInited; + + public PreviewPanel(MainPanel mainPanel, FlashPlayerPanel flashPanel) { + super(JSplitPane.HORIZONTAL_SPLIT); + this.mainPanel = mainPanel; + this.flashPanel = flashPanel; + + addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (splitsInited && getRightComponent().isVisible()) { + Configuration.guiPreviewSplitPaneDividerLocation.set((int) pce.getNewValue()); + } + } + }); + + viewerCards = new JPanel(); + viewerCards.setLayout(new CardLayout()); + + viewerCards.add(createFlashPlayerPanel(flashPanel), FLASH_VIEWER_CARD); + viewerCards.add(createImagesCard(), DRAW_PREVIEW_CARD); + viewerCards.add(createBinaryCard(), BINARY_TAG_CARD); + viewerCards.add(createGenericTagCard(), GENERIC_TAG_CARD); + setLeftComponent(viewerCards); + + createParametersPanel(); + + showCardLeft(FLASH_VIEWER_CARD); + } + + private void createParametersPanel() { + displayWithPreview = new JPanel(new CardLayout()); + + textPanel = new TextPanel(mainPanel); + displayWithPreview.add(textPanel, CARDTEXTPANEL); + + fontPanel = new FontPanel(mainPanel); + displayWithPreview.add(fontPanel, CARDFONTPANEL); + + JLabel paramsLabel = new HeaderLabel(mainPanel.translate("parameters")); + paramsLabel.setHorizontalAlignment(SwingConstants.CENTER); + //paramsLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + parametersPanel = new JPanel(new BorderLayout()); + parametersPanel.add(paramsLabel, BorderLayout.NORTH); + parametersPanel.add(displayWithPreview, BorderLayout.CENTER); + setRightComponent(parametersPanel); + } + + private JPanel createImageButtonsPanel() { + replaceImageButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceImageButton.setMargin(new Insets(3, 3, 3, 10)); + replaceImageButton.setActionCommand(MainPanel.ACTION_REPLACE); + replaceImageButton.addActionListener(mainPanel); + replaceImageButton.setVisible(false); + + prevFontsButton = new JButton(mainPanel.translate("button.prev"), View.getIcon("prev16")); + prevFontsButton.setMargin(new Insets(3, 3, 3, 10)); + prevFontsButton.setActionCommand(ACTION_PREV_FONTS); + prevFontsButton.addActionListener(this); + prevFontsButton.setVisible(false); + + nextFontsButton = new JButton(mainPanel.translate("button.next"), View.getIcon("next16")); + nextFontsButton.setMargin(new Insets(3, 3, 3, 10)); + nextFontsButton.setActionCommand(ACTION_NEXT_FONTS); + nextFontsButton.addActionListener(this); + nextFontsButton.setVisible(false); + + ButtonsPanel imageButtonsPanel = new ButtonsPanel(); + imageButtonsPanel.add(replaceImageButton); + imageButtonsPanel.add(prevFontsButton); + imageButtonsPanel.add(nextFontsButton); + return imageButtonsPanel; + } + + private JPanel createBinaryButtonsPanel() { + replaceBinaryButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("edit16")); + replaceBinaryButton.setMargin(new Insets(3, 3, 3, 10)); + replaceBinaryButton.setActionCommand(MainPanel.ACTION_REPLACE); + replaceBinaryButton.addActionListener(mainPanel); + + ButtonsPanel binaryButtonsPanel = new ButtonsPanel(); + binaryButtonsPanel.add(replaceBinaryButton); + return binaryButtonsPanel; + } + + private JPanel createGenericTagButtonsPanel() { + editButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); + editButton.setMargin(new Insets(3, 3, 3, 10)); + editButton.setActionCommand(ACTION_EDIT_GENERIC_TAG); + editButton.addActionListener(this); + saveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + saveButton.setMargin(new Insets(3, 3, 3, 10)); + saveButton.setActionCommand(ACTION_SAVE_GENERIC_TAG); + saveButton.addActionListener(this); + saveButton.setVisible(false); + cancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + cancelButton.setMargin(new Insets(3, 3, 3, 10)); + cancelButton.setActionCommand(ACTION_CANCEL_GENERIC_TAG); + cancelButton.addActionListener(this); + cancelButton.setVisible(false); + + ButtonsPanel genericTagButtonsPanel = new ButtonsPanel(); + genericTagButtonsPanel.add(editButton); + genericTagButtonsPanel.add(saveButton); + genericTagButtonsPanel.add(cancelButton); + return genericTagButtonsPanel; + } + + private JPanel createFlashPlayerPanel(FlashPlayerPanel flashPanel) { + JPanel pan = new JPanel(new BorderLayout()); + JLabel prevLabel = new HeaderLabel(mainPanel.translate("swfpreview")); + prevLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + + pan.add(prevLabel, BorderLayout.NORTH); + + Component leftComponent; + if (flashPanel != null) { + JPanel flashPlayPanel = new JPanel(new BorderLayout()); + flashPlayPanel.add(flashPanel, BorderLayout.CENTER); + + JPanel bottomPanel = new JPanel(new BorderLayout()); + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton selectColorButton = new JButton(View.getIcon("color16")); + selectColorButton.addActionListener(mainPanel); + selectColorButton.setActionCommand(MainPanel.ACTION_SELECT_BKCOLOR); + selectColorButton.setToolTipText(AppStrings.translate("button.selectbkcolor.hint")); + buttonsPanel.add(selectColorButton); + bottomPanel.add(buttonsPanel, BorderLayout.EAST); + + flashPlayPanel.add(bottomPanel, BorderLayout.SOUTH); + + JPanel flashPlayPanel2 = new JPanel(new BorderLayout()); + flashPlayPanel2.add(flashPlayPanel, BorderLayout.CENTER); + flashPlayPanel2.add(flashControls = new PlayerControls(flashPanel), BorderLayout.SOUTH); + leftComponent = flashPlayPanel2; + } else { + JPanel swtPanel = new JPanel(new BorderLayout()); + swtPanel.add(new JLabel("
" + mainPanel.translate("notavailonthisplatform") + "
", JLabel.CENTER), BorderLayout.CENTER); + swtPanel.setBackground(View.DEFAULT_BACKGROUND_COLOR); + leftComponent = swtPanel; + } + + pan.add(leftComponent, BorderLayout.CENTER); + return pan; + } + + private JPanel createImagesCard() { + JPanel shapesCard = new JPanel(new BorderLayout()); + JPanel previewPanel = new JPanel(new BorderLayout()); + + JPanel previewCnt = new JPanel(new BorderLayout()); + imagePanel = new ImagePanel(); + previewCnt.add(imagePanel, BorderLayout.CENTER); + previewCnt.add(imagePlayControls = new PlayerControls(imagePanel), BorderLayout.SOUTH); + imagePlayControls.setMedia(imagePanel); + previewPanel.add(previewCnt, BorderLayout.CENTER); + JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); + prevIntLabel.setHorizontalAlignment(SwingConstants.CENTER); + //prevIntLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + previewPanel.add(prevIntLabel, BorderLayout.NORTH); + + shapesCard.add(previewPanel, BorderLayout.CENTER); + + shapesCard.add(createImageButtonsPanel(), BorderLayout.SOUTH); + return shapesCard; + } + + private JPanel createBinaryCard() { + JPanel binaryCard = new JPanel(new BorderLayout()); + binaryPanel = new BinaryPanel(); + binaryCard.add(binaryPanel, BorderLayout.CENTER); + binaryCard.add(createBinaryButtonsPanel(), BorderLayout.SOUTH); + return binaryCard; + } + + private JPanel createGenericTagCard() { + JPanel genericTagCard = new JPanel(new BorderLayout()); + genericTagPanel = new GenericTagTreePanel(); + genericTagCard.add(genericTagPanel, BorderLayout.CENTER); + genericTagCard.add(createGenericTagButtonsPanel(), BorderLayout.SOUTH); + return genericTagCard; + } + + private void showCardLeft(String card) { + CardLayout cl = (CardLayout) (viewerCards.getLayout()); + cl.show(viewerCards, card); + } + + private void showCardRight(String card) { + CardLayout cl = (CardLayout) (displayWithPreview.getLayout()); + cl.show(displayWithPreview, card); + } + + public void setSplitsInited() { + splitsInited = true; + } + + public TextPanel getTextPanel() { + return textPanel; + } + + public void setParametersPanelVisible(boolean show) { + parametersPanel.setVisible(show); + } + + public void showFlashViewerPanel() { + parametersPanel.setVisible(false); + showCardLeft(FLASH_VIEWER_CARD); + } + + public void showImagePanel(Timelined timelined, SWF swf, int frame) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setTimelined(timelined, swf, frame); + } + + public void showImagePanel(SerializableImage image) { + showCardLeft(DRAW_PREVIEW_CARD); + parametersPanel.setVisible(false); + imagePlayControls.setMedia(imagePanel); + imagePanel.setImage(image); + } + + public void setMedia(MediaDisplay media) { + imagePlayControls.setMedia(media); + } + + public void showFontPanel(FontTag fontTag) { + fontPageNum = 0; + showFontPage(fontTag); + + showCardRight(CARDFONTPANEL); + parametersPanel.setVisible(true); + setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(getWidth() / 2)); + fontPanel.showFontTag(fontTag); + + int pageCount = getFontPageCount(fontTag); + if (pageCount > 1) { + prevFontsButton.setVisible(true); + nextFontsButton.setVisible(true); + } + } + + private void showFontPage(FontTag fontTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + + public static int getFontPageCount(FontTag fontTag) { + int pageCount = (fontTag.getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (pageCount < 1) { + pageCount = 1; + } + return pageCount; + } + + public void showTextPanel(TextTag textTag) { + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0); + } + + showCardRight(CARDTEXTPANEL); + parametersPanel.setVisible(true); + setDividerLocation(Configuration.guiPreviewSplitPaneDividerLocation.get(getWidth() / 2)); + textPanel.setText(textTag.getFormattedText()); + } + + public void setEditText(boolean edit) { + textPanel.setEditText(edit); + } + + public void clear() { + imagePanel.stop(); + binaryPanel.setBinaryData(null); + genericTagPanel.clear(); + fontPanel.clear(); + } + + public void showBinaryPanel(byte[] data) { + showCardLeft(BINARY_TAG_CARD); + binaryPanel.setBinaryData(data); + parametersPanel.setVisible(false); + } + + public void showGenericTagPanel(Tag tag) { + showCardLeft(GENERIC_TAG_CARD); + editButton.setVisible(true); + saveButton.setVisible(false); + cancelButton.setVisible(false); + genericTagPanel.setEditMode(false, tag); + parametersPanel.setVisible(false); + } + + public void setImageReplaceButtonVisible(boolean show) { + replaceImageButton.setVisible(show); + prevFontsButton.setVisible(false); + nextFontsButton.setVisible(false); + } + + private static Tag classicTag(Tag t) { + if (t instanceof DefineCompactedFont) { + return ((DefineCompactedFont) t).toClassicFont(); + } + return t; + } + + public void createAndShowTempSwf(TreeItem tagObj) { + SWF swf; + try { + if (tempFile != null) { + tempFile.delete(); + } + tempFile = File.createTempFile("temp", ".swf"); + tempFile.deleteOnExit(); + + Color backgroundColor = View.swfBackgroundColor; + + if (tagObj instanceof FontTag) { //Fonts are always black on white + backgroundColor = View.DEFAULT_BACKGROUND_COLOR; + } + + if (tagObj instanceof FrameNodeItem) { + FrameNodeItem fn = (FrameNodeItem) tagObj; + swf = fn.getSwf(); + if (fn.getParent() == null) { + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + } + } else { + Tag tag = (Tag) tagObj; + swf = tag.getSwf(); + } + + int frameCount = 1; + int frameRate = swf.frameRate; + HashMap videoFrames = new HashMap<>(); + if (tagObj instanceof DefineVideoStreamTag) { + DefineVideoStreamTag vs = (DefineVideoStreamTag) tagObj; + SWF.populateVideoFrames(vs.getCharacterId(), swf.tags, videoFrames); + frameCount = videoFrames.size(); + } + + List soundFrames = new ArrayList<>(); + if (tagObj instanceof SoundStreamHeadTypeTag) { + soundFrames = ((SoundStreamHeadTypeTag) tagObj).getBlocks(); + frameCount = soundFrames.size(); + } + + if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + frameRate = MainPanel.MORPH_SHAPE_ANIMATION_FRAME_RATE; + frameCount = MainPanel.MORPH_SHAPE_ANIMATION_LENGTH * frameRate; + } + + if (tagObj instanceof DefineSoundTag) { + frameCount = 1; + } + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SWFOutputStream sos2 = new SWFOutputStream(baos, 10); + int width = swf.displayRect.Xmax - swf.displayRect.Xmin; + int height = swf.displayRect.Ymax - swf.displayRect.Ymin; + sos2.writeRECT(swf.displayRect); + sos2.writeUI8(0); + sos2.writeUI8(frameRate); + sos2.writeUI16(frameCount); //framecnt + + /*FileAttributesTag fa = new FileAttributesTag(); + sos2.writeTag(fa); + */ + new SetBackgroundColorTag(swf, new RGB(backgroundColor)).writeTag(sos2); + + if (tagObj instanceof FrameNodeItem) { + FrameNodeItem fn = (FrameNodeItem) tagObj; + Tag parent = fn.getParent(); + List subs = new ArrayList<>(); + if (parent == null) { + subs.addAll(swf.tags); + } else { + if (parent instanceof Container) { + subs = ((Container) parent).getSubItems(); + } + } + List doneCharacters = new ArrayList<>(); + int frameCnt = 1; + for (ContainerItem item : subs) { + if (item instanceof ShowFrameTag) { + frameCnt++; + continue; + } + if (frameCnt > fn.getFrame()) { + break; + } + + if (item instanceof DoActionTag || item instanceof DoInitActionTag) { + // todo: Maybe DoABC tags should be removed, too + continue; + } + + Tag t = (Tag) item; + Set needed = new HashSet<>(); + t.getNeededCharactersDeep(needed); + for (int n : needed) { + if (!doneCharacters.contains(n)) { + classicTag(swf.characters.get(n)).writeTag(sos2); + doneCharacters.add(n); + } + } + if (t instanceof CharacterTag) { + int characterId = ((CharacterTag) t).getCharacterId(); + if (!doneCharacters.contains(characterId)) { + doneCharacters.add(((CharacterTag) t).getCharacterId()); + } + } + classicTag(t).writeTag(sos2); + + if (parent != null) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; + int chid = pot.getCharacterId(); + int depth = pot.getDepth(); + MATRIX mat = pot.getMatrix(); + if (mat == null) { + mat = new MATRIX(); + } + mat = Helper.deepCopy(mat); + if (parent instanceof BoundedTag) { + RECT r = ((BoundedTag) parent).getRect(); + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX += width / 2; + mat.translateY += height / 2; + } + new PlaceObject2Tag(swf, false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null).writeTag(sos2); + + } + } + } + new ShowFrameTag(swf).writeTag(sos2); + } else { + + if (tagObj instanceof DefineBitsTag) { + JPEGTablesTag jtt = swf.jtt; + if (jtt != null) { + jtt.writeTag(sos2); + } + } else if (tagObj instanceof AloneTag) { + } else { + Set needed = new HashSet<>(); + ((Tag) tagObj).getNeededCharactersDeep(needed); + for (int n : needed) { + classicTag(swf.characters.get(n)).writeTag(sos2); + } + } + + classicTag((Tag) tagObj).writeTag(sos2); + + int chtId = 0; + if (tagObj instanceof CharacterTag) { + chtId = ((CharacterTag) tagObj).getCharacterId(); + } + + MATRIX mat = new MATRIX(); + mat.hasRotate = false; + mat.hasScale = false; + mat.translateX = 0; + mat.translateY = 0; + if (tagObj instanceof BoundedTag) { + RECT r = ((BoundedTag) tagObj).getRect(); + mat.translateX = -r.Xmin; + mat.translateY = -r.Ymin; + mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2; + mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2; + } else { + mat.translateX = width / 4; + mat.translateY = height / 4; + } + if (tagObj instanceof FontTag) { + + int countGlyphsTotal = ((FontTag) tagObj).getGlyphShapeTable().size(); + int countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal); + int fontId = ((FontTag) tagObj).getFontId(); + int cols = (int) Math.ceil(Math.sqrt(countGlyphs)); + int rows = (int) Math.ceil(((float) countGlyphs) / ((float) cols)); + int x = 0; + int y = 1; + int firstGlyphIndex = fontPageNum * SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW; + countGlyphs = Math.min(SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW, countGlyphsTotal - firstGlyphIndex); + for (int f = firstGlyphIndex; f < firstGlyphIndex + countGlyphs; f++) { + if (x >= cols) { + x = 0; + y++; + } + List rec = new ArrayList<>(); + TEXTRECORD tr = new TEXTRECORD(); + int textHeight = height / rows; + tr.fontId = fontId; + tr.styleFlagsHasFont = true; + tr.textHeight = textHeight; + tr.glyphEntries = new GLYPHENTRY[1]; + tr.styleFlagsHasColor = true; + tr.textColor = new RGB(0, 0, 0); + tr.glyphEntries[0] = new GLYPHENTRY(); + tr.glyphEntries[0].glyphAdvance = 0; + tr.glyphEntries[0].glyphIndex = f; + rec.add(tr); + mat.translateX = x * width / cols; + mat.translateY = y * height / rows; + new DefineTextTag(swf, 999 + f, new RECT(0, width, 0, height), new MATRIX(), rec).writeTag(sos2); + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1 + f, 999 + f, mat, null, 0, null, 0, null).writeTag(sos2); + x++; + } + new ShowFrameTag(swf).writeTag(sos2); + } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + for (int ratio = 0; ratio < 65536; ratio += 65536 / frameCount) { + new PlaceObject2Tag(swf, false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof SoundStreamHeadTypeTag) { + for (SoundStreamBlockTag blk : soundFrames) { + blk.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + } else if (tagObj instanceof DefineSoundTag) { + ExportAssetsTag ea = new ExportAssetsTag(swf); + DefineSoundTag ds = (DefineSoundTag) tagObj; + ea.tags.add(ds.soundId); + ea.names.add("my_define_sound"); + ea.writeTag(sos2); + List actions; + DoActionTag doa; + + doa = new DoActionTag(swf, new byte[0], new byte[0], 0); + actions = ASMParser.parse(0, 0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\"\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, 0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"start\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push 9999 0.0 2 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, 0, false, + "ConstantPool \"_root\" \"my_sound\" \"Sound\" \"my_define_sound\" \"attachSound\" \"onSoundComplete\" \"start\" \"execParam\"\n" + + "StopSounds\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\" 0.0 \"Sound\"\n" + + "NewObject\n" + + "SetMember\n" + + "Push \"my_define_sound\" 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"attachSound\"\n" + + "CallMethod\n" + + "Pop\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"onSoundComplete\"\n" + + "DefineFunction2 \"\" 0 2 false true true false true false true false false {\n" + + "Push 0.0 register1 \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "}\n" + + "SetMember\n" + + "Push \"_root\"\n" + + "GetVariable\n" + + "Push \"execParam\"\n" + + "GetMember\n" + + "Push 1 \"_root\"\n" + + "GetVariable\n" + + "Push \"my_sound\"\n" + + "GetMember\n" + + "Push \"start\"\n" + + "CallMethod\n" + + "Pop\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + actions = ASMParser.parse(0, 0, false, + "StopSounds\n" + + "Stop", swf.version, false); + doa.setActions(actions); + doa.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + + new ShowFrameTag(swf).writeTag(sos2); + if (flashPanel != null) { + flashPanel.specialPlayback = true; + } + } else if (tagObj instanceof DefineVideoStreamTag) { + + new PlaceObject2Tag(swf, false, false, false, false, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + List frs = new ArrayList<>(videoFrames.values()); + Collections.sort(frs, new Comparator() { + @Override + public int compare(VideoFrameTag o1, VideoFrameTag o2) { + return o1.frameNum - o2.frameNum; + } + }); + boolean first = true; + int ratio = 0; + for (VideoFrameTag f : frs) { + if (!first) { + ratio++; + new PlaceObject2Tag(swf, false, false, false, true, false, false, false, true, 1, 0, null, null, ratio, null, 0, null).writeTag(sos2); + } + f.writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + first = false; + } + } else { + new PlaceObject2Tag(swf, false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null).writeTag(sos2); + new ShowFrameTag(swf).writeTag(sos2); + } + + }//not showframe + + new EndTag(swf).writeTag(sos2); + data = baos.toByteArray(); + } + + try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile))) { + SWFOutputStream sos = new SWFOutputStream(fos, Math.max(10, swf.version)); + sos.write("FWS".getBytes()); + sos.write(swf.version); + sos.writeUI32(sos.getPos() + data.length + 4); + sos.write(data); + fos.flush(); + } + + if (flashPanel != null) { + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, frameRate); + } + showFlashViewerPanel(); + } catch (IOException | com.jpexs.decompiler.flash.action.parser.ParseException ex) { + Logger.getLogger(MainPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void showSwf(SWF swf) { + Color backgroundColor = View.DEFAULT_BACKGROUND_COLOR; + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + backgroundColor = ((SetBackgroundColorTag) t).backgroundColor.toColor(); + break; + } + } + + if (tempFile != null) { + tempFile.delete(); + } + try { + tempFile = File.createTempFile("temp", ".swf"); + tempFile.deleteOnExit(); + swf.saveTo(new BufferedOutputStream(new FileOutputStream(tempFile))); + flashPanel.displaySWF(tempFile.getAbsolutePath(), backgroundColor, swf.frameRate); + } catch (IOException iex) { + Logger.getLogger(PreviewPanel.class.getName()).log(Level.SEVERE, "Cannot create tempfile", iex); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_EDIT_GENERIC_TAG: { + TreeItem item = mainPanel.tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof Tag) { + editButton.setVisible(false); + saveButton.setVisible(true); + cancelButton.setVisible(true); + //genericTagPanel.generateEditControls((Tag) item, false); + genericTagPanel.setEditMode(true, (Tag) item); + } + } + break; + case ACTION_SAVE_GENERIC_TAG: { + genericTagPanel.save(); + mainPanel.refreshTree(); + mainPanel.setTreeItem(genericTagPanel.getTag()); + editButton.setVisible(true); + saveButton.setVisible(false); + cancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + break; + case ACTION_CANCEL_GENERIC_TAG: { + editButton.setVisible(true); + saveButton.setVisible(false); + cancelButton.setVisible(false); + genericTagPanel.setEditMode(false, null); + } + break; + case ACTION_PREV_FONTS: { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + pageCount - 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + break; + case ACTION_NEXT_FONTS: { + FontTag fontTag = fontPanel.getFontTag(); + int pageCount = getFontPageCount(fontTag); + fontPageNum = (fontPageNum + 1) % pageCount; + if (mainPanel.isInternalFlashViewerSelected() /*|| ft instanceof GFxDefineCompactedFont*/) { + imagePanel.setTimelined(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum); + } + } + break; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java b/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java index df7d6d87d..9d0e4b56a 100644 --- a/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java +++ b/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java @@ -1,460 +1,461 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; -import com.jpexs.decompiler.flash.gui.abc.treenodes.ClassesListNode; -import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; -import com.jpexs.decompiler.flash.gui.treenodes.SWFBundleNode; -import com.jpexs.decompiler.flash.gui.treenodes.SWFContainerNode; -import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; -import com.jpexs.decompiler.flash.gui.treenodes.StringNode; -import com.jpexs.decompiler.flash.gui.treenodes.TagTreeRoot; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.StringItem; -import com.jpexs.decompiler.flash.treeitems.TreeElementItem; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.treenodes.FrameNode; -import com.jpexs.decompiler.flash.treenodes.TagNode; -import com.jpexs.decompiler.flash.treenodes.TreeNode; -import com.jpexs.helpers.Helper; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.swing.event.TreeModelListener; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - -public class TagTreeModel implements TreeModel { - - public static final String FOLDER_TEXTS = "texts"; - public static final String FOLDER_IMAGES = "images"; - public static final String FOLDER_MOVIES = "movies"; - public static final String FOLDER_SOUNDS = "sounds"; - public static final String FOLDER_BINARY_DATA = "binaryData"; - public static final String FOLDER_FONTS = "fonts"; - public static final String FOLDER_SPRITES = "sprites"; - public static final String FOLDER_SHAPES = "shapes"; - public static final String FOLDER_MORPHSHAPES = "morphshapes"; - public static final String FOLDER_BUTTONS = "buttons"; - public static final String FOLDER_FRAMES = "frames"; - public static final String FOLDER_OTHERS = "others"; - public static final String FOLDER_SCRIPTS = "scripts"; - private final TagTreeRoot root = new TagTreeRoot(); - private final List swfs; - private final Map swfToSwfNode; - private final MainFrame mainFrame; - - public TagTreeModel(MainFrame mainFrame, List swfs) { - this.mainFrame = mainFrame; - this.swfs = new ArrayList<>(); - swfToSwfNode = new HashMap<>(); - for (SWFList swfList : swfs) { - if (swfList.isBundle) { - SWFBundleNode bundleNode = new SWFBundleNode(swfList, swfList.name); - for (SWF swf : swfList) { - bundleNode.swfs.add(createSwfNode(swf)); - } - this.swfs.add(bundleNode); - } else { - SWF swf = swfList.get(0); - this.swfs.add(createSwfNode(swf)); - } - } - } - - private SWFNode createSwfNode(SWF swf) { - ClassesListTreeModel classTreeModel = new ClassesListTreeModel(swf); - SWFNode swfNode = new SWFNode(swf, swf.getShortFileName()); - swfNode.list = createTagList(swf.tags, null, swf, swfNode, classTreeModel); - swfToSwfNode.put(swf, swfNode); - return swfNode; - } - - private String translate(String key) { - return mainFrame.translate(key); - } - - private List createTagList(List list, Tag parent, SWF swf, SWFNode swfNode, ClassesListTreeModel classTreeModel) { - boolean hasAbc = swf.abcList != null && !swf.abcList.isEmpty(); - - List ret = new ArrayList<>(); - List frames = new ArrayList<>(); - List shapes = new ArrayList<>(); - List morphShapes = new ArrayList<>(); - List sprites = new ArrayList<>(); - List buttons = new ArrayList<>(); - List images = new ArrayList<>(); - List fonts = new ArrayList<>(); - List texts = new ArrayList<>(); - List movies = new ArrayList<>(); - List sounds = new ArrayList<>(); - List binaryData = new ArrayList<>(); - List others = new ArrayList<>(); - - List actionScript = SWF.createASTagList(list, null); - List actionScriptTags = new ArrayList<>(); - SWF.getTagsFromTreeNodes(actionScript, actionScriptTags); - - int frameCnt = 0; - for (Tag t : list) { - TreeNodeType ttype = TagTree.getTreeNodeType(t); - switch (ttype) { - case SHOW_FRAME: - ShowFrameTag showFrameTag = (ShowFrameTag) t; - frames.add(new FrameNode(new FrameNodeItem(t.getSwf(), ++frameCnt, parent, showFrameTag, true), showFrameTag.innerTags, false)); - break; - case SHAPE: - shapes.add(new TagNode(t)); - break; - case MORPH_SHAPE: - morphShapes.add(new TagNode(t)); - break; - case SPRITE: - sprites.add(new TagNode(t)); - break; - case BUTTON: - buttons.add(new TagNode(t)); - break; - case IMAGE: - images.add(new TagNode(t)); - break; - case FONT: - fonts.add(new TagNode(t)); - break; - case TEXT: - texts.add(new TagNode(t)); - break; - case MOVIE: - movies.add(new TagNode(t)); - break; - case SOUND: - sounds.add(new TagNode(t)); - break; - case BINARY_DATA: - TagNode bt; - binaryData.add(bt = new TagNode(t)); - - DefineBinaryDataTag b=(DefineBinaryDataTag)t; - - try { - SWF bswf=new SWF(new ByteArrayInputStream(b.binaryData),Configuration.parallelSpeedUp.get()); - bswf.fileTitle = "(SWF Data)"; - SWFNode snode=createSwfNode(bswf); - snode.binaryData = b; - bt.subNodes.add(snode); - } catch (IOException | InterruptedException ex) { - //ignore - } - break; - default: - if (!actionScriptTags.contains(t) && !ShowFrameTag.isNestedTagType(t.getId())) { - others.add(new TagNode(t)); - } - break; - } - } - - for (int i = 0; i < sounds.size(); i++) { - if (sounds.get(i).getItem() instanceof SoundStreamHeadTypeTag) { - List blocks = ((SoundStreamHeadTypeTag) sounds.get(i).getItem()).getBlocks(); - if (blocks.isEmpty()) { - sounds.remove(i); - i--; - } - } - } - - for (TreeNode n : sprites) { - Tag tag = n.getItem() instanceof Tag ? (Tag) n.getItem() : null; - n.subNodes = createSubTagList(((DefineSpriteTag) n.getItem()).subTags, tag, swf, actionScriptTags); - } - - StringNode textsNode = new StringNode(new StringItem(translate("node.texts"), FOLDER_TEXTS, swf)); - textsNode.subNodes.addAll(texts); - - StringNode imagesNode = new StringNode(new StringItem(translate("node.images"), FOLDER_IMAGES, swf)); - imagesNode.subNodes.addAll(images); - - StringNode moviesNode = new StringNode(new StringItem(translate("node.movies"), FOLDER_MOVIES, swf)); - moviesNode.subNodes.addAll(movies); - - StringNode soundsNode = new StringNode(new StringItem(translate("node.sounds"), FOLDER_SOUNDS, swf)); - soundsNode.subNodes.addAll(sounds); - - StringNode binaryDataNode = new StringNode(new StringItem(translate("node.binaryData"), FOLDER_BINARY_DATA, swf)); - binaryDataNode.subNodes.addAll(binaryData); - - StringNode fontsNode = new StringNode(new StringItem(translate("node.fonts"), FOLDER_FONTS, swf)); - fontsNode.subNodes.addAll(fonts); - - StringNode spritesNode = new StringNode(new StringItem(translate("node.sprites"), FOLDER_SPRITES, swf)); - spritesNode.subNodes.addAll(sprites); - - StringNode shapesNode = new StringNode(new StringItem(translate("node.shapes"), FOLDER_SHAPES, swf)); - shapesNode.subNodes.addAll(shapes); - - StringNode morphShapesNode = new StringNode(new StringItem(translate("node.morphshapes"), FOLDER_MORPHSHAPES, swf)); - morphShapesNode.subNodes.addAll(morphShapes); - - StringNode buttonsNode = new StringNode(new StringItem(translate("node.buttons"), FOLDER_BUTTONS, swf)); - buttonsNode.subNodes.addAll(buttons); - - StringNode framesNode = new StringNode(new StringItem(translate("node.frames"), FOLDER_FRAMES, swf)); - framesNode.subNodes.addAll(frames); - - StringNode otherNode = new StringNode(new StringItem(translate("node.others"), FOLDER_OTHERS, swf)); - otherNode.subNodes.addAll(others); - - TreeNode actionScriptNode; - if (hasAbc) { - actionScriptNode = new ClassesListNode(classTreeModel); - } else { - actionScriptNode = new StringNode(new StringItem(translate("node.scripts"), FOLDER_SCRIPTS, swf)); - actionScriptNode.subNodes.addAll(actionScript); - } - swfNode.scriptsNode = actionScriptNode; - - if (!shapesNode.subNodes.isEmpty()) { - ret.add(shapesNode); - } - if (!morphShapesNode.subNodes.isEmpty()) { - ret.add(morphShapesNode); - } - if (!spritesNode.subNodes.isEmpty()) { - ret.add(spritesNode); - } - if (!textsNode.subNodes.isEmpty()) { - ret.add(textsNode); - } - if (!imagesNode.subNodes.isEmpty()) { - ret.add(imagesNode); - } - if (!moviesNode.subNodes.isEmpty()) { - ret.add(moviesNode); - } - if (!soundsNode.subNodes.isEmpty()) { - ret.add(soundsNode); - } - if (!buttonsNode.subNodes.isEmpty()) { - ret.add(buttonsNode); - } - if (!fontsNode.subNodes.isEmpty()) { - ret.add(fontsNode); - } - if (!binaryDataNode.subNodes.isEmpty()) { - ret.add(binaryDataNode); - } - if (!framesNode.subNodes.isEmpty()) { - ret.add(framesNode); - } - if (!otherNode.subNodes.isEmpty()) { - ret.add(otherNode); - } - - if ((!actionScriptNode.subNodes.isEmpty()) || hasAbc) { - ret.add(actionScriptNode); - } - - return ret; - } - - private List createSubTagList(List list, Tag parent, SWF swf, List actionScriptTags) { - List ret = new ArrayList<>(); - List frames = new ArrayList<>(); - List others = new ArrayList<>(); - - int frameCnt = 0; - for (Tag t : list) { - TreeNodeType ttype = TagTree.getTreeNodeType(t); - switch (ttype) { - case SHOW_FRAME: - ShowFrameTag showFrameTag = (ShowFrameTag) t; - frames.add(new FrameNode(new FrameNodeItem(t.getSwf(), ++frameCnt, parent, showFrameTag, true), showFrameTag.innerTags, false)); - break; - default: - if (!actionScriptTags.contains(t) && !ShowFrameTag.isNestedTagType(t.getId())) { - if (!(t instanceof SoundStreamHeadTypeTag)) { - others.add(new TagNode(t)); - } - } - break; - } - } - - ret.addAll(frames); - - if (!others.isEmpty()) { - StringNode otherNode = new StringNode(new StringItem(translate("node.others"), FOLDER_OTHERS, swf)); - otherNode.subNodes.addAll(others); - ret.add(otherNode); - } - - return ret; - } - - private List searchTag(TreeItem obj, TreeNode parent, List path) { - List ret = null; - int cnt = getChildCount(parent); - for (int i = 0; i < cnt; i++) { - TreeNode n = getChild(parent, i); - List newPath = new ArrayList<>(); - newPath.addAll(path); - newPath.add(n); - - if (n instanceof TreeElement) { - TreeElement te = (TreeElement) n; - TreeElementItem it = te.getItem(); - if (obj == it) { - return newPath; - } - } - if (n instanceof TreeNode) { - TreeNode nd = (TreeNode) n; - if (obj instanceof StringItem && nd.getItem() instanceof StringItem) { - // StringItems are always recreated, so compare them by name - StringItem nds = (StringItem) nd.getItem(); - StringItem objs = (StringItem) obj; - if (objs.getName().equals(nds.getName())) { - return newPath; - } - } else { - if (nd.getItem() == obj) { - return newPath; - } - } - } - ret = searchTag(obj, n, newPath); - if (ret != null) { - return ret; - } - } - return ret; - } - - public SWFNode getSwfNode(SWF swf) { - return swfToSwfNode.get(swf); - } - - public TreePath getTagPath(TreeItem obj) { - List path = new ArrayList<>(); - path.add(getRoot()); - path = searchTag(obj, getRoot(), path); - if (path == null) { - return null; - } - TreePath tp = new TreePath(path.toArray(new Object[path.size()])); - return tp; - } - - @Override - public TreeNode getRoot() { - return root; - } - - @Override - public TreeNode getChild(Object parent, int index) { - TreeNode parentNode = (TreeNode) parent; - if (parent instanceof TreeElement) { - return ((TreeElement) parent).getChild(index); - } else { - if (parentNode.getItem() instanceof ClassesListTreeModel) { - ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); - return clt.getChild(clt.getRoot(), index); - } - } - if (parent == root) { - return swfs.get(index); - } else if (parent instanceof SWFBundleNode) { - return ((SWFBundleNode) parent).swfs.get(index); - } else if (parent instanceof SWFNode) { - return ((SWFNode) parent).list.get(index); - } - return parentNode.subNodes.get(index); - } - - @Override - public int getChildCount(Object parent) { - TreeNode parentNode = (TreeNode) parent; - if (parent == root) { - return swfs.size(); - } else if (parent instanceof TreeElement) { - return ((TreeElement) parent).getChildCount(); - } else if (parent instanceof SWFBundleNode) { - return ((SWFBundleNode) parent).swfs.size(); - } else if (parent instanceof SWFNode) { - return ((SWFNode) parent).list.size(); - } else { - if (parentNode.getItem() instanceof ClassesListTreeModel) { - ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); - return clt.getChildCount(clt.getRoot()); - } - return parentNode.subNodes.size(); - } - } - - @Override - public boolean isLeaf(Object node) { - return (getChildCount(node) == 0); - } - - @Override - public void valueForPathChanged(TreePath path, Object newValue) { - } - - @Override - public int getIndexOfChild(Object parent, Object child) { - TreeNode parentNode = (TreeNode) parent; - if (parent == root) { - return swfs.indexOf(child); - } else if (parent instanceof TreeElement) { - return ((TreeElement) parent).getIndexOfChild((TreeElement) child); - } else if (parent instanceof SWFBundleNode) { - return ((SWFBundleNode) parent).swfs.indexOf(child); - } else if (parent instanceof SWFNode) { - return ((SWFNode) parent).list.indexOf(child); - } else { - if (parentNode.getItem() instanceof ClassesListTreeModel) { - ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); - return clt.getIndexOfChild(clt.getRoot(), child); - } - return parentNode.subNodes.indexOf(child); - } - } - - @Override - public void addTreeModelListener(TreeModelListener l) { - } - - @Override - public void removeTreeModelListener(TreeModelListener l) { - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; +import com.jpexs.decompiler.flash.gui.abc.treenodes.ClassesListNode; +import com.jpexs.decompiler.flash.gui.abc.treenodes.TreeElement; +import com.jpexs.decompiler.flash.gui.treenodes.SWFBundleNode; +import com.jpexs.decompiler.flash.gui.treenodes.SWFContainerNode; +import com.jpexs.decompiler.flash.gui.treenodes.SWFNode; +import com.jpexs.decompiler.flash.gui.treenodes.StringNode; +import com.jpexs.decompiler.flash.gui.treenodes.TagTreeRoot; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FrameNodeItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.StringItem; +import com.jpexs.decompiler.flash.treeitems.TreeElementItem; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.treenodes.FrameNode; +import com.jpexs.decompiler.flash.treenodes.TagNode; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +public class TagTreeModel implements TreeModel { + + public static final String FOLDER_TEXTS = "texts"; + public static final String FOLDER_IMAGES = "images"; + public static final String FOLDER_MOVIES = "movies"; + public static final String FOLDER_SOUNDS = "sounds"; + public static final String FOLDER_BINARY_DATA = "binaryData"; + public static final String FOLDER_FONTS = "fonts"; + public static final String FOLDER_SPRITES = "sprites"; + public static final String FOLDER_SHAPES = "shapes"; + public static final String FOLDER_MORPHSHAPES = "morphshapes"; + public static final String FOLDER_BUTTONS = "buttons"; + public static final String FOLDER_FRAMES = "frames"; + public static final String FOLDER_OTHERS = "others"; + public static final String FOLDER_SCRIPTS = "scripts"; + private final TagTreeRoot root = new TagTreeRoot(); + private final List swfs; + private final Map swfToSwfNode; + private final MainFrame mainFrame; + + public TagTreeModel(MainFrame mainFrame, List swfs) { + this.mainFrame = mainFrame; + this.swfs = new ArrayList<>(); + swfToSwfNode = new HashMap<>(); + for (SWFList swfList : swfs) { + if (swfList.isBundle) { + SWFBundleNode bundleNode = new SWFBundleNode(swfList, swfList.name); + for (SWF swf : swfList) { + bundleNode.swfs.add(createSwfNode(swf)); + } + this.swfs.add(bundleNode); + } else { + SWF swf = swfList.get(0); + this.swfs.add(createSwfNode(swf)); + } + } + } + + private SWFNode createSwfNode(SWF swf) { + ClassesListTreeModel classTreeModel = new ClassesListTreeModel(swf); + SWFNode swfNode = new SWFNode(swf, swf.getShortFileName()); + swfNode.list = createTagList(swf.tags, null, swf, swfNode, classTreeModel); + swfToSwfNode.put(swf, swfNode); + return swfNode; + } + + private String translate(String key) { + return mainFrame.translate(key); + } + + private List createTagList(List list, Tag parent, SWF swf, SWFNode swfNode, ClassesListTreeModel classTreeModel) { + boolean hasAbc = swf.abcList != null && !swf.abcList.isEmpty(); + + List ret = new ArrayList<>(); + List frames = new ArrayList<>(); + List shapes = new ArrayList<>(); + List morphShapes = new ArrayList<>(); + List sprites = new ArrayList<>(); + List buttons = new ArrayList<>(); + List images = new ArrayList<>(); + List fonts = new ArrayList<>(); + List texts = new ArrayList<>(); + List movies = new ArrayList<>(); + List sounds = new ArrayList<>(); + List binaryData = new ArrayList<>(); + List others = new ArrayList<>(); + + List actionScript = SWF.createASTagList(list, null); + List actionScriptTags = new ArrayList<>(); + SWF.getTagsFromTreeNodes(actionScript, actionScriptTags); + + for (Tag t : list) { + TreeNodeType ttype = TagTree.getTreeNodeType(t); + switch (ttype) { + case SHAPE: + shapes.add(new TagNode(t)); + break; + case MORPH_SHAPE: + morphShapes.add(new TagNode(t)); + break; + case SPRITE: + sprites.add(new TagNode(t)); + break; + case BUTTON: + buttons.add(new TagNode(t)); + break; + case IMAGE: + images.add(new TagNode(t)); + break; + case FONT: + fonts.add(new TagNode(t)); + break; + case TEXT: + texts.add(new TagNode(t)); + break; + case MOVIE: + movies.add(new TagNode(t)); + break; + case SOUND: + sounds.add(new TagNode(t)); + break; + case BINARY_DATA: + TagNode bt; + binaryData.add(bt = new TagNode(t)); + + DefineBinaryDataTag b = (DefineBinaryDataTag) t; + + try { + SWF bswf = new SWF(new ByteArrayInputStream(b.binaryData), Configuration.parallelSpeedUp.get()); + bswf.fileTitle = "(SWF Data)"; + SWFNode snode = createSwfNode(bswf); + snode.binaryData = b; + bt.subNodes.add(snode); + } catch (IOException | InterruptedException ex) { + //ignore + } + break; + default: + if (!actionScriptTags.contains(t) && !ShowFrameTag.isNestedTagType(t.getId())) { + others.add(new TagNode(t)); + } + break; + } + } + + Timeline timeline = swf.getTimeline(); + for (int i = 0; i < timeline.getFrameCount(); i++) { + frames.add(new FrameNode(new FrameNodeItem(swf, i + 1, parent, true), timeline.frames.get(i).innerTags, false)); + } + + for (int i = 0; i < sounds.size(); i++) { + if (sounds.get(i).getItem() instanceof SoundStreamHeadTypeTag) { + List blocks = ((SoundStreamHeadTypeTag) sounds.get(i).getItem()).getBlocks(); + if (blocks.isEmpty()) { + sounds.remove(i); + i--; + } + } + } + + for (TreeNode n : sprites) { + Tag tag = n.getItem() instanceof Tag ? (Tag) n.getItem() : null; + n.subNodes = createSubTagList(((DefineSpriteTag) n.getItem()).subTags, tag, swf, actionScriptTags); + } + + StringNode textsNode = new StringNode(new StringItem(translate("node.texts"), FOLDER_TEXTS, swf)); + textsNode.subNodes.addAll(texts); + + StringNode imagesNode = new StringNode(new StringItem(translate("node.images"), FOLDER_IMAGES, swf)); + imagesNode.subNodes.addAll(images); + + StringNode moviesNode = new StringNode(new StringItem(translate("node.movies"), FOLDER_MOVIES, swf)); + moviesNode.subNodes.addAll(movies); + + StringNode soundsNode = new StringNode(new StringItem(translate("node.sounds"), FOLDER_SOUNDS, swf)); + soundsNode.subNodes.addAll(sounds); + + StringNode binaryDataNode = new StringNode(new StringItem(translate("node.binaryData"), FOLDER_BINARY_DATA, swf)); + binaryDataNode.subNodes.addAll(binaryData); + + StringNode fontsNode = new StringNode(new StringItem(translate("node.fonts"), FOLDER_FONTS, swf)); + fontsNode.subNodes.addAll(fonts); + + StringNode spritesNode = new StringNode(new StringItem(translate("node.sprites"), FOLDER_SPRITES, swf)); + spritesNode.subNodes.addAll(sprites); + + StringNode shapesNode = new StringNode(new StringItem(translate("node.shapes"), FOLDER_SHAPES, swf)); + shapesNode.subNodes.addAll(shapes); + + StringNode morphShapesNode = new StringNode(new StringItem(translate("node.morphshapes"), FOLDER_MORPHSHAPES, swf)); + morphShapesNode.subNodes.addAll(morphShapes); + + StringNode buttonsNode = new StringNode(new StringItem(translate("node.buttons"), FOLDER_BUTTONS, swf)); + buttonsNode.subNodes.addAll(buttons); + + StringNode framesNode = new StringNode(new StringItem(translate("node.frames"), FOLDER_FRAMES, swf)); + framesNode.subNodes.addAll(frames); + + StringNode otherNode = new StringNode(new StringItem(translate("node.others"), FOLDER_OTHERS, swf)); + otherNode.subNodes.addAll(others); + + TreeNode actionScriptNode; + if (hasAbc) { + actionScriptNode = new ClassesListNode(classTreeModel); + } else { + actionScriptNode = new StringNode(new StringItem(translate("node.scripts"), FOLDER_SCRIPTS, swf)); + actionScriptNode.subNodes.addAll(actionScript); + } + swfNode.scriptsNode = actionScriptNode; + + if (!shapesNode.subNodes.isEmpty()) { + ret.add(shapesNode); + } + if (!morphShapesNode.subNodes.isEmpty()) { + ret.add(morphShapesNode); + } + if (!spritesNode.subNodes.isEmpty()) { + ret.add(spritesNode); + } + if (!textsNode.subNodes.isEmpty()) { + ret.add(textsNode); + } + if (!imagesNode.subNodes.isEmpty()) { + ret.add(imagesNode); + } + if (!moviesNode.subNodes.isEmpty()) { + ret.add(moviesNode); + } + if (!soundsNode.subNodes.isEmpty()) { + ret.add(soundsNode); + } + if (!buttonsNode.subNodes.isEmpty()) { + ret.add(buttonsNode); + } + if (!fontsNode.subNodes.isEmpty()) { + ret.add(fontsNode); + } + if (!binaryDataNode.subNodes.isEmpty()) { + ret.add(binaryDataNode); + } + if (!framesNode.subNodes.isEmpty()) { + ret.add(framesNode); + } + if (!otherNode.subNodes.isEmpty()) { + ret.add(otherNode); + } + + if ((!actionScriptNode.subNodes.isEmpty()) || hasAbc) { + ret.add(actionScriptNode); + } + + return ret; + } + + private List createSubTagList(List list, Tag parent, SWF swf, List actionScriptTags) { + List ret = new ArrayList<>(); + List frames = new ArrayList<>(); + List others = new ArrayList<>(); + + for (Tag t : list) { + TreeNodeType ttype = TagTree.getTreeNodeType(t); + switch (ttype) { + default: + if (!actionScriptTags.contains(t) && !ShowFrameTag.isNestedTagType(t.getId())) { + if (!(t instanceof SoundStreamHeadTypeTag)) { + others.add(new TagNode(t)); + } + } + break; + } + } + + if (parent instanceof Timelined) { + Timeline timeline = ((Timelined) parent).getTimeline(); + for (int i = 0; i < timeline.getFrameCount(); i++) { + frames.add(new FrameNode(new FrameNodeItem(swf, i + 1, parent, true), timeline.frames.get(i).innerTags, false)); + } + } + + ret.addAll(frames); + + if (!others.isEmpty()) { + StringNode otherNode = new StringNode(new StringItem(translate("node.others"), FOLDER_OTHERS, swf)); + otherNode.subNodes.addAll(others); + ret.add(otherNode); + } + + return ret; + } + + private List searchTag(TreeItem obj, TreeNode parent, List path) { + List ret = null; + int cnt = getChildCount(parent); + for (int i = 0; i < cnt; i++) { + TreeNode n = getChild(parent, i); + List newPath = new ArrayList<>(); + newPath.addAll(path); + newPath.add(n); + + if (n instanceof TreeElement) { + TreeElement te = (TreeElement) n; + TreeElementItem it = te.getItem(); + if (obj == it) { + return newPath; + } + } + if (n instanceof TreeNode) { + TreeNode nd = (TreeNode) n; + if (obj instanceof StringItem && nd.getItem() instanceof StringItem) { + // StringItems are always recreated, so compare them by name + StringItem nds = (StringItem) nd.getItem(); + StringItem objs = (StringItem) obj; + if (objs.getName().equals(nds.getName())) { + return newPath; + } + } else { + if (nd.getItem() == obj) { + return newPath; + } + } + } + ret = searchTag(obj, n, newPath); + if (ret != null) { + return ret; + } + } + return ret; + } + + public SWFNode getSwfNode(SWF swf) { + return swfToSwfNode.get(swf); + } + + public TreePath getTagPath(TreeItem obj) { + List path = new ArrayList<>(); + path.add(getRoot()); + path = searchTag(obj, getRoot(), path); + if (path == null) { + return null; + } + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + return tp; + } + + @Override + public TreeNode getRoot() { + return root; + } + + @Override + public TreeNode getChild(Object parent, int index) { + TreeNode parentNode = (TreeNode) parent; + if (parent instanceof TreeElement) { + return ((TreeElement) parent).getChild(index); + } else { + if (parentNode.getItem() instanceof ClassesListTreeModel) { + ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); + return clt.getChild(clt.getRoot(), index); + } + } + if (parent == root) { + return swfs.get(index); + } else if (parent instanceof SWFBundleNode) { + return ((SWFBundleNode) parent).swfs.get(index); + } else if (parent instanceof SWFNode) { + return ((SWFNode) parent).list.get(index); + } + return parentNode.subNodes.get(index); + } + + @Override + public int getChildCount(Object parent) { + TreeNode parentNode = (TreeNode) parent; + if (parent == root) { + return swfs.size(); + } else if (parent instanceof TreeElement) { + return ((TreeElement) parent).getChildCount(); + } else if (parent instanceof SWFBundleNode) { + return ((SWFBundleNode) parent).swfs.size(); + } else if (parent instanceof SWFNode) { + return ((SWFNode) parent).list.size(); + } else { + if (parentNode.getItem() instanceof ClassesListTreeModel) { + ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); + return clt.getChildCount(clt.getRoot()); + } + return parentNode.subNodes.size(); + } + } + + @Override + public boolean isLeaf(Object node) { + return (getChildCount(node) == 0); + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + TreeNode parentNode = (TreeNode) parent; + if (parent == root) { + return swfs.indexOf(child); + } else if (parent instanceof TreeElement) { + return ((TreeElement) parent).getIndexOfChild((TreeElement) child); + } else if (parent instanceof SWFBundleNode) { + return ((SWFBundleNode) parent).swfs.indexOf(child); + } else if (parent instanceof SWFNode) { + return ((SWFNode) parent).list.indexOf(child); + } else { + if (parentNode.getItem() instanceof ClassesListTreeModel) { + ClassesListTreeModel clt = (ClassesListTreeModel) parentNode.getItem(); + return clt.getIndexOfChild(clt.getRoot(), child); + } + return parentNode.subNodes.indexOf(child); + } + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/View.java b/src/com/jpexs/decompiler/flash/gui/View.java index c7530a82d..f5121f1ae 100644 --- a/src/com/jpexs/decompiler/flash/gui/View.java +++ b/src/com/jpexs/decompiler/flash/gui/View.java @@ -1,502 +1,501 @@ -/* - * Copyright (C) 2010-2014 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.gui; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.TexturePaint; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRootPane; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.plaf.FontUIResource; -import javax.swing.plaf.basic.BasicColorChooserUI; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; -import org.pushingpixels.substance.api.ComponentState; -import org.pushingpixels.substance.api.SubstanceColorScheme; -import org.pushingpixels.substance.api.SubstanceConstants; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; -import org.pushingpixels.substance.api.fonts.FontPolicy; -import org.pushingpixels.substance.api.fonts.FontSet; -import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; -import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; - -/** - * Contains methods for GUI - * - * @author JPEXS - */ -public class View { - - public static final Color DEFAULT_BACKGROUND_COLOR = new Color(217, 231, 250); - public static Color swfBackgroundColor = DEFAULT_BACKGROUND_COLOR; - - private static final BufferedImage transparentTexture; - public static final TexturePaint transparentPaint; - - private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); - private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); - - static { - transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); - Graphics g = transparentTexture.getGraphics(); - g.setColor(transparentColor1); - g.fillRect(0, 0, 16, 16); - g.setColor(transparentColor2); - g.fillRect(0, 0, 8, 8); - g.fillRect(8, 8, 8, 8); - transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); - } - - /** - * Sets windows Look and Feel - */ - public static void setLookAndFeel() { - - //Save default font for Chinese characters - final Font defaultFont = (new JLabel()).getFont(); - try { - - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { - } - - execInEventDispatch(new Runnable() { - @Override - public void run() { - - try { - UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); - UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title - UIManager.put("Tree.expandedIcon", getIcon("expand16")); - UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); - UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); - UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); - UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); - UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); - UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); - UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); - UIManager.put("TextField.background", Color.WHITE); - UIManager.put("FormattedTextField.background", Color.WHITE); - UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); - - - FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); - final FontSet fs = pol.getFontSet("Substance", null); - - //Restore default font for chinese characters - SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { - - private final FontSet fontSet = new FontSet() { - - private FontUIResource controlFont; - private FontUIResource menuFont; - private FontUIResource titleFont; - private FontUIResource windowTitleFont; - private FontUIResource smallFont; - private FontUIResource messageFont; - - @Override - public FontUIResource getControlFont() { - if (controlFont == null) { - FontUIResource f = fs.getControlFont(); - controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return controlFont; - } - - @Override - public FontUIResource getMenuFont() { - if (menuFont == null) { - FontUIResource f = fs.getMenuFont(); - menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return menuFont; - } - - @Override - public FontUIResource getTitleFont() { - if (titleFont == null) { - FontUIResource f = fs.getTitleFont(); - titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return titleFont; - } - - @Override - public FontUIResource getWindowTitleFont() { - if (windowTitleFont == null) { - FontUIResource f = fs.getWindowTitleFont(); - windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return windowTitleFont; - } - - @Override - public FontUIResource getSmallFont() { - if (smallFont == null) { - FontUIResource f = fs.getSmallFont(); - smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return smallFont; - } - - @Override - public FontUIResource getMessageFont() { - if (messageFont == null) { - FontUIResource f = fs.getMessageFont(); - messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); - } - return messageFont; - } - }; - - @Override - public FontSet getFontSet(String string, UIDefaults uid) { - return fontSet; - } - }); - } catch (UnsupportedLookAndFeelException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - }); - - UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); - - JFrame.setDefaultLookAndFeelDecorated(true); - JDialog.setDefaultLookAndFeelDecorated(true); - } - - /** - * Loads image from resources - * - * @param name Name of the image - * @return loaded Image - */ - public static BufferedImage loadImage(String name) { - java.net.URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); - try { - return ImageIO.read(imageURL); - } catch (IOException ex) { - return null; - } - } - - /** - * Sets icon of specified frame to ASDec icon - * - * @param f Frame to set icon in - */ - public static void setWindowIcon(Window f) { - java.util.List images = new ArrayList<>(); - images.add(loadImage("icon16")); - images.add(loadImage("icon32")); - images.add(loadImage("icon48")); - images.add(loadImage("icon256")); - f.setIconImages(images); - } - - /** - * Centers specified frame on the screen - * - * @param f Frame to center on the screen - */ - public static void centerScreen(Window f) { - centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) - } - - public static void centerScreen(Window f, int screen) { - - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] allDevices = env.getScreenDevices(); - int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; - - if (screen < allDevices.length && screen > -1) { - topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; - screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; - } else { - topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; - topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; - - screenX = allDevices[0].getDefaultConfiguration().getBounds().width; - screenY = allDevices[0].getDefaultConfiguration().getBounds().height; - } - - windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; - windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; - - f.setLocation(windowPosX, windowPosY); - } - - public static ImageIcon getIcon(String name) { - return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); - } - private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; - - public static void installEscapeCloseOperation(final JDialog dialog) { - Action dispatchClosing = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent event) { - dialog.dispatchEvent(new WindowEvent( - dialog, WindowEvent.WINDOW_CLOSING)); - } - }; - JRootPane root = dialog.getRootPane(); - root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( - escapeStroke, dispatchWindowClosingActionMapKey); - root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); - } - - public static ImageWrapperResizableIcon getResizableIcon(String resource) { - return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); - } - - public static MyResizableIcon getMyResizableIcon(String resource) { - try { - return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); - } catch (IOException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - return null; - } - } - - public static void execInEventDispatch(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - try { - SwingUtilities.invokeAndWait(r); - } catch (InterruptedException ex) { - } catch (InvocationTargetException ex) { - Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - public static void execInEventDispatchLater(Runnable r) { - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { - final int[] ret = new int[1]; - execInEventDispatch(new Runnable() { - @Override - public void run() { - ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); - } - }); - return ret[0]; - } - - public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType) { - return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); - } - - public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { - final int ret[] = new int[1]; - execInEventDispatch(new Runnable() { - @Override - public void run() { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); - } - }); - return ret[0]; - } - - public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { - - JLabel warLabel = new JLabel(message); - final JPanel warPanel = new JPanel(new BorderLayout()); - warPanel.add(warLabel, BorderLayout.CENTER); - JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); - donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); - warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); - - if (donotShowAgainCheckBox.isSelected()) { - return defaultOption; - } - - final int ret[] = new int[1]; - execInEventDispatch(new Runnable() { - @Override - public void run() { - ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); - } - }); - showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); - return ret[0]; - } - - public static void showMessageDialog(final Component parentComponent, final Object message, final String title, final int messageType) { - execInEventDispatch(new Runnable() { - @Override - public void run() { - JOptionPane.showMessageDialog(parentComponent, message, title, messageType); - } - }); - } - - public static void showMessageDialog(final Component parentComponent, final Object message) { - execInEventDispatch(new Runnable() { - @Override - public void run() { - JOptionPane.showMessageDialog(parentComponent, message); - } - }); - } - - public static String showInputDialog(final Object message, final Object initialSelection) { - final String[] ret = new String[1]; - execInEventDispatch(new Runnable() { - @Override - public void run() { - ret[0] = JOptionPane.showInputDialog(message, initialSelection); - } - }); - return ret[0]; - } - - public static SubstanceColorScheme getColorScheme() { - return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); - } - - public static void refreshTree(JTree tree, TreeModel model) { - List> expandedNodes = getExpandedNodes(tree); - tree.setModel(model); - expandTreeNodes(tree, expandedNodes); - } - - private static List> getExpandedNodes(JTree tree) { - List> expandedNodes = new ArrayList<>(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - List pathAsStringList = new ArrayList<>(); - for (Object pathCompnent : path.getPath()) { - pathAsStringList.add(pathCompnent.toString()); - } - expandedNodes.add(pathAsStringList); - } - } - return expandedNodes; - } - - private static void expandTreeNodes(JTree tree, List> pathsToExpand) { - for (List pathAsStringList : pathsToExpand) { - expandTreeNode(tree, pathAsStringList); - } - } - - private static void expandTreeNode(JTree tree, List pathAsStringList) { - TreeModel model = tree.getModel(); - Object node = model.getRoot(); - - if (pathAsStringList.isEmpty()) { - return; - } - if (!pathAsStringList.get(0).equals(node.toString())) { - return; - } - - List path = new ArrayList<>(); - path.add(node); - - for (int i = 1; i < pathAsStringList.size(); i++) { - String name = pathAsStringList.get(i); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - if (child.toString().equals(name)) { - node = child; - path.add(node); - break; - } - } - } - - TreePath tp = new TreePath(path.toArray(new Object[path.size()])); - tree.expandPath(tp); - } - - public static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { - TreeModel model = tree.getModel(); - - Object node = parent.getLastPathComponent(); - int childCount = model.getChildCount(node); - for (int j = 0; j < childCount; j++) { - Object child = model.getChild(node, j); - TreePath path = parent.pathByAddingChild(child); - expandTreeNodesRecursive(tree, path, expand); - } - - if (expand) { - tree.expandPath(parent); - } else { - tree.collapsePath(parent); - } - } -} +/* + * Copyright (C) 2010-2014 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.gui; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.TexturePaint; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.basic.BasicColorChooserUI; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.SubstanceColorScheme; +import org.pushingpixels.substance.api.SubstanceConstants; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.fonts.FontPolicy; +import org.pushingpixels.substance.api.fonts.FontSet; +import org.pushingpixels.substance.api.skin.SubstanceOfficeBlue2007LookAndFeel; +import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; + +/** + * Contains methods for GUI + * + * @author JPEXS + */ +public class View { + + public static final Color DEFAULT_BACKGROUND_COLOR = new Color(217, 231, 250); + public static Color swfBackgroundColor = DEFAULT_BACKGROUND_COLOR; + + private static final BufferedImage transparentTexture; + public static final TexturePaint transparentPaint; + + private static final Color transparentColor1 = new Color(0x99, 0x99, 0x99); + private static final Color transparentColor2 = new Color(0x66, 0x66, 0x66); + + static { + transparentTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = transparentTexture.getGraphics(); + g.setColor(transparentColor1); + g.fillRect(0, 0, 16, 16); + g.setColor(transparentColor2); + g.fillRect(0, 0, 8, 8); + g.fillRect(8, 8, 8, 8); + transparentPaint = new TexturePaint(View.transparentTexture, new Rectangle(0, 0, transparentTexture.getWidth(), transparentTexture.getHeight())); + } + + /** + * Sets windows Look and Feel + */ + public static void setLookAndFeel() { + + //Save default font for Chinese characters + final Font defaultFont = (new JLabel()).getFont(); + try { + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + } + + execInEventDispatch(new Runnable() { + @Override + public void run() { + + try { + UIManager.setLookAndFeel(new SubstanceOfficeBlue2007LookAndFeel()); + UIManager.put(SubstanceLookAndFeel.COLORIZATION_FACTOR, 0.999);//This works for not changing labels color and not changing Dialogs title + UIManager.put("Tree.expandedIcon", getIcon("expand16")); + UIManager.put("Tree.collapsedIcon", getIcon("collapse16")); + UIManager.put("ColorChooserUI", BasicColorChooserUI.class.getName()); + UIManager.put("ColorChooser.swatchesRecentSwatchSize", new Dimension(20, 20)); + UIManager.put("ColorChooser.swatchesSwatchSize", new Dimension(20, 20)); + UIManager.put("RibbonApplicationMenuPopupPanelUI", MyRibbonApplicationMenuPopupPanelUI.class.getName()); + UIManager.put("RibbonApplicationMenuButtonUI", MyRibbonApplicationMenuButtonUI.class.getName()); + UIManager.put("ProgressBarUI", MyProgressBarUI.class.getName()); + UIManager.put("TextField.background", Color.WHITE); + UIManager.put("FormattedTextField.background", Color.WHITE); + UIManager.put("CommandButtonUI", MyCommandButtonUI.class.getName()); + + FontPolicy pol = SubstanceLookAndFeel.getFontPolicy(); + final FontSet fs = pol.getFontSet("Substance", null); + + //Restore default font for chinese characters + SubstanceLookAndFeel.setFontPolicy(new FontPolicy() { + + private final FontSet fontSet = new FontSet() { + + private FontUIResource controlFont; + private FontUIResource menuFont; + private FontUIResource titleFont; + private FontUIResource windowTitleFont; + private FontUIResource smallFont; + private FontUIResource messageFont; + + @Override + public FontUIResource getControlFont() { + if (controlFont == null) { + FontUIResource f = fs.getControlFont(); + controlFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return controlFont; + } + + @Override + public FontUIResource getMenuFont() { + if (menuFont == null) { + FontUIResource f = fs.getMenuFont(); + menuFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return menuFont; + } + + @Override + public FontUIResource getTitleFont() { + if (titleFont == null) { + FontUIResource f = fs.getTitleFont(); + titleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return titleFont; + } + + @Override + public FontUIResource getWindowTitleFont() { + if (windowTitleFont == null) { + FontUIResource f = fs.getWindowTitleFont(); + windowTitleFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return windowTitleFont; + } + + @Override + public FontUIResource getSmallFont() { + if (smallFont == null) { + FontUIResource f = fs.getSmallFont(); + smallFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return smallFont; + } + + @Override + public FontUIResource getMessageFont() { + if (messageFont == null) { + FontUIResource f = fs.getMessageFont(); + messageFont = new FontUIResource(defaultFont.getName(), f.getStyle(), f.getSize()); + } + return messageFont; + } + }; + + @Override + public FontSet getFontSet(String string, UIDefaults uid) { + return fontSet; + } + }); + } catch (UnsupportedLookAndFeelException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + }); + + UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, SubstanceConstants.TabContentPaneBorderKind.SINGLE_FULL); + + JFrame.setDefaultLookAndFeelDecorated(true); + JDialog.setDefaultLookAndFeelDecorated(true); + } + + /** + * Loads image from resources + * + * @param name Name of the image + * @return loaded Image + */ + public static BufferedImage loadImage(String name) { + java.net.URL imageURL = View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + name + ".png"); + try { + return ImageIO.read(imageURL); + } catch (IOException ex) { + return null; + } + } + + /** + * Sets icon of specified frame to ASDec icon + * + * @param f Frame to set icon in + */ + public static void setWindowIcon(Window f) { + java.util.List images = new ArrayList<>(); + images.add(loadImage("icon16")); + images.add(loadImage("icon32")); + images.add(loadImage("icon48")); + images.add(loadImage("icon256")); + f.setIconImages(images); + } + + /** + * Centers specified frame on the screen + * + * @param f Frame to center on the screen + */ + public static void centerScreen(Window f) { + centerScreen(f, 0); // todo, set screen to the currently active screen instead of the first screen in a multi screen setup, (maybe by using the screen where the main window is now classic or ribbon?) + } + + public static void centerScreen(Window f, int screen) { + + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] allDevices = env.getScreenDevices(); + int topLeftX, topLeftY, screenX, screenY, windowPosX, windowPosY; + + if (screen < allDevices.length && screen > -1) { + topLeftX = allDevices[screen].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[screen].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[screen].getDefaultConfiguration().getBounds().width; + screenY = allDevices[screen].getDefaultConfiguration().getBounds().height; + } else { + topLeftX = allDevices[0].getDefaultConfiguration().getBounds().x; + topLeftY = allDevices[0].getDefaultConfiguration().getBounds().y; + + screenX = allDevices[0].getDefaultConfiguration().getBounds().width; + screenY = allDevices[0].getDefaultConfiguration().getBounds().height; + } + + windowPosX = ((screenX - f.getWidth()) / 2) + topLeftX; + windowPosY = ((screenY - f.getHeight()) / 2) + topLeftY; + + f.setLocation(windowPosX, windowPosY); + } + + public static ImageIcon getIcon(String name) { + return new ImageIcon(View.class.getClassLoader().getResource("com/jpexs/decompiler/flash/gui/graphics/" + name + ".png")); + } + private static final KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + private static final String dispatchWindowClosingActionMapKey = "com.jpexs.dispatch:WINDOW_CLOSING"; + + public static void installEscapeCloseOperation(final JDialog dialog) { + Action dispatchClosing = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent event) { + dialog.dispatchEvent(new WindowEvent( + dialog, WindowEvent.WINDOW_CLOSING)); + } + }; + JRootPane root = dialog.getRootPane(); + root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + escapeStroke, dispatchWindowClosingActionMapKey); + root.getActionMap().put(dispatchWindowClosingActionMapKey, dispatchClosing); + } + + public static ImageWrapperResizableIcon getResizableIcon(String resource) { + return ImageWrapperResizableIcon.getIcon(View.class.getResource("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"), new Dimension(256, 256)); + } + + public static MyResizableIcon getMyResizableIcon(String resource) { + try { + return new MyResizableIcon(ImageIO.read(View.class.getResourceAsStream("/com/jpexs/decompiler/flash/gui/graphics/" + resource + ".png"))); + } catch (IOException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + public static void execInEventDispatch(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException ex) { + } catch (InvocationTargetException ex) { + Logger.getLogger(View.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + public static void execInEventDispatchLater(Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } + + public static int showOptionDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageType, final Icon icon, final Object[] options, final Object initialValue) { + final int[] ret = new int[1]; + execInEventDispatch(new Runnable() { + @Override + public void run() { + ret[0] = JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue); + } + }); + return ret[0]; + } + + public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType) { + return showConfirmDialog(parentComponent, message, title, optionType, JOptionPane.PLAIN_MESSAGE); + } + + public static int showConfirmDialog(final Component parentComponent, final Object message, final String title, final int optionType, final int messageTyp) { + final int ret[] = new int[1]; + execInEventDispatch(new Runnable() { + @Override + public void run() { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageTyp); + } + }); + return ret[0]; + } + + public static int showConfirmDialog(final Component parentComponent, String message, final String title, final int optionType, final int messageTyp, ConfigurationItem showAgainConfig, int defaultOption) { + + JLabel warLabel = new JLabel(message); + final JPanel warPanel = new JPanel(new BorderLayout()); + warPanel.add(warLabel, BorderLayout.CENTER); + JCheckBox donotShowAgainCheckBox = new JCheckBox(AppStrings.translate("message.confirm.donotshowagain")); + donotShowAgainCheckBox.setSelected(!showAgainConfig.get()); + warPanel.add(donotShowAgainCheckBox, BorderLayout.SOUTH); + + if (donotShowAgainCheckBox.isSelected()) { + return defaultOption; + } + + final int ret[] = new int[1]; + execInEventDispatch(new Runnable() { + @Override + public void run() { + ret[0] = JOptionPane.showConfirmDialog(parentComponent, warPanel, title, optionType, messageTyp); + } + }); + showAgainConfig.set(!donotShowAgainCheckBox.isSelected()); + return ret[0]; + } + + public static void showMessageDialog(final Component parentComponent, final Object message, final String title, final int messageType) { + execInEventDispatch(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(parentComponent, message, title, messageType); + } + }); + } + + public static void showMessageDialog(final Component parentComponent, final Object message) { + execInEventDispatch(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(parentComponent, message); + } + }); + } + + public static String showInputDialog(final Object message, final Object initialSelection) { + final String[] ret = new String[1]; + execInEventDispatch(new Runnable() { + @Override + public void run() { + ret[0] = JOptionPane.showInputDialog(message, initialSelection); + } + }); + return ret[0]; + } + + public static SubstanceColorScheme getColorScheme() { + return SubstanceColorSchemeUtilities.getActiveColorScheme(new JButton(), ComponentState.ENABLED); + } + + public static void refreshTree(JTree tree, TreeModel model) { + List> expandedNodes = getExpandedNodes(tree); + tree.setModel(model); + expandTreeNodes(tree, expandedNodes); + } + + private static List> getExpandedNodes(JTree tree) { + List> expandedNodes = new ArrayList<>(); + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + List pathAsStringList = new ArrayList<>(); + for (Object pathCompnent : path.getPath()) { + pathAsStringList.add(pathCompnent.toString()); + } + expandedNodes.add(pathAsStringList); + } + } + return expandedNodes; + } + + private static void expandTreeNodes(JTree tree, List> pathsToExpand) { + for (List pathAsStringList : pathsToExpand) { + expandTreeNode(tree, pathAsStringList); + } + } + + private static void expandTreeNode(JTree tree, List pathAsStringList) { + TreeModel model = tree.getModel(); + Object node = model.getRoot(); + + if (pathAsStringList.isEmpty()) { + return; + } + if (!pathAsStringList.get(0).equals(node.toString())) { + return; + } + + List path = new ArrayList<>(); + path.add(node); + + for (int i = 1; i < pathAsStringList.size(); i++) { + String name = pathAsStringList.get(i); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + if (child.toString().equals(name)) { + node = child; + path.add(node); + break; + } + } + } + + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + tree.expandPath(tp); + } + + public static void expandTreeNodesRecursive(JTree tree, TreePath parent, boolean expand) { + TreeModel model = tree.getModel(); + + Object node = parent.getLastPathComponent(); + int childCount = model.getChildCount(node); + for (int j = 0; j < childCount; j++) { + Object child = model.getChild(node, j); + TreePath path = parent.pathByAddingChild(child); + expandTreeNodesRecursive(tree, path, expand); + } + + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java index 60f236d03..6b2c42df0 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ABCPanel.java @@ -1,822 +1,822 @@ -/* - * Copyright (C) 2010-2014 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.gui.abc; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; -import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; -import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; -import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; -import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; -import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; -import com.jpexs.decompiler.flash.abc.types.ABCException; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.MethodInfo; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.Namespace; -import com.jpexs.decompiler.flash.abc.types.ValueKind; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; -import com.jpexs.decompiler.flash.abc.types.traits.Traits; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.gui.HeaderLabel; -import com.jpexs.decompiler.flash.gui.Main; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.SearchListener; -import com.jpexs.decompiler.flash.gui.SearchPanel; -import com.jpexs.decompiler.flash.gui.TagTreeModel; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; -import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.treenodes.TreeNode; -import com.jpexs.decompiler.graph.CompilationException; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.border.BevelBorder; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableColumnModel; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableModel; -import javax.swing.tree.TreePath; -import jsyntaxpane.DefaultSyntaxKit; - -public class ABCPanel extends JPanel implements ItemListener, ActionListener, SearchListener, Freed { - - private MainPanel mainPanel; - public TraitsList navigator; - public ABC abc; - public SWF swf; - public JComboBox abcComboBox; - public int listIndex = -1; - public DecompiledEditorPane decompiledTextArea; - public JScrollPane decompiledScrollPane; - public JSplitPane splitPane; - //public JSplitPane splitPaneTreeVSNavigator; - //public JSplitPane splitPaneTreeNavVSDecompiledDetail; - private JTable constantTable; - public JComboBox constantTypeList; - public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); - public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); - public DetailPanel detailPanel; - public JPanel navPanel; - public JTabbedPane tabbedPane; - public SearchPanel searchPanel; - private NewTraitDialog newTraitDialog; - public JLabel scriptNameLabel; - - static final String ACTION_SAVE_DECOMPILED = "SAVEDECOMPILED"; - static final String ACTION_EDIT_DECOMPILED = "EDITDECOMPILED"; - static final String ACTION_CANCEL_DECOMPILED = "CANCELDECOMPILED"; - - public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); - public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); - public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); - public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); - - static final String ACTION_ADD_TRAIT = "ADDTRAIT"; - - public boolean search(String txt, boolean ignoreCase, boolean regexp) { - if ((txt != null) && (!txt.isEmpty())) { - searchPanel.setOptions(ignoreCase, regexp); - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeNode scriptsNode = ttm.getSwfNode(mainPanel.getCurrentSwf()).scriptsNode; - final List found = new ArrayList<>(); - if (scriptsNode.getItem() instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode.getItem(); - List> allpacks = clModel.getList(); - final Pattern pat = regexp - ? Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0) - : Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); - int pos = 0; - for (final MyEntry item : allpacks) { - pos++; - String workText = AppStrings.translate("work.searching"); - String decAdd = ""; - if (!decompiledTextArea.isCached(item.value)) { - decAdd = ", " + AppStrings.translate("work.decompiling"); - } - - try { - CancellableWorker worker = new CancellableWorker() { - - @Override - public Void doInBackground() throws Exception { - decompiledTextArea.cacheScriptPack(item.value, swf.abcList); - if (pat.matcher(decompiledTextArea.getCachedText(item.value)).find()) { - ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); - searchResult.scriptPack = item.value; - searchResult.classPath = item.key; - found.add(searchResult); - } - return null; - } - }; - worker.execute(); - Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", worker); - worker.get(); - } catch (InterruptedException ex) { - break; - } catch (ExecutionException ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - - System.gc(); - Main.stopWork(); - searchPanel.setSearchText(txt); - return searchPanel.setResults(found); - } - return false; - } - - private JTable autoResizeColWidth(final JTable table, final TableModel model) { - View.execInEventDispatch(new Runnable() { - @Override - public void run() { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - table.setModel(model); - - int margin = 5; - - for (int i = 0; i < table.getColumnCount(); i++) { - int vColIndex = i; - DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); - TableColumn col = colModel.getColumn(vColIndex); - int width; - - // Get width of column header - TableCellRenderer renderer = col.getHeaderRenderer(); - - if (renderer == null) { - renderer = table.getTableHeader().getDefaultRenderer(); - } - - Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); - - width = comp.getPreferredSize().width; - - // Get maximum width of column data - for (int r = 0; r < table.getRowCount(); r++) { - renderer = table.getCellRenderer(r, vColIndex); - comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, - r, vColIndex); - width = Math.max(width, comp.getPreferredSize().width); - } - - // Add margin - width += 2 * margin; - - // Set the width - col.setPreferredWidth(width); - } - - ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( - SwingConstants.LEFT); - - // table.setAutoCreateRowSorter(true); - table.getTableHeader().setReorderingAllowed(false); - } - }); - - return table; - } - - public void setAbc(ABC abc) { - this.abc = abc; - updateConstList(); - } - - public void updateConstList() { - switch (constantTypeList.getSelectedIndex()) { - case 0: - autoResizeColWidth(constantTable, new UIntTableModel(abc)); - break; - case 1: - autoResizeColWidth(constantTable, new IntTableModel(abc)); - break; - case 2: - autoResizeColWidth(constantTable, new DoubleTableModel(abc)); - break; - case 3: - autoResizeColWidth(constantTable, new DecimalTableModel(abc)); - break; - case 4: - autoResizeColWidth(constantTable, new StringTableModel(abc)); - break; - case 5: - autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); - break; - case 6: - autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); - break; - case 7: - autoResizeColWidth(constantTable, new MultinameTableModel(abc)); - break; - } - //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); - //colModel.getColumn(0).setMaxWidth(50); - } - - public void clearSwf() { - this.swf = null; - this.abc = null; - constantTable.setModel(new DefaultTableModel()); - abcComboBox.setModel(new ABCComboBoxModel(new ArrayList())); - navigator.clearABC(); - } - - public void setSwf(SWF swf) { - if (this.swf != swf) { - this.swf = swf; - listIndex = -1; - switchAbc(0); // todo honika: do we need this? - abcComboBox.setModel(new ABCComboBoxModel(swf.abcList)); - if (swf.abcList.size() > 0) { - this.abc = swf.abcList.get(0).getABC(); - } - - navigator.setABC(swf.abcList, abc); - } - } - - public void switchAbc(int index) { - listIndex = index; - - if (index != -1) { - this.abc = swf.abcList.get(index).getABC(); - } - updateConstList(); - } - - public void initSplits() { - //splitPaneTreeVSNavigator.setDividerLocation(splitPaneTreeVSNavigator.getHeight() / 2); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - //splitPaneTreeNavVSDecompiledDetail.setDividerLocation(splitPaneTreeNavVSDecompiledDetail.getWidth() * 1 / 3); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); - } - - splitPane.setDividerLocation(Configuration.guiAvm2SplitPaneDividerLocation.get(splitPane.getWidth() * 1 / 2)); - - } - - private boolean isFreeing; - - @Override - public boolean isFreeing() { - return isFreeing; - } - - @Override - public void free() { - isFreeing = true; - Helper.emptyObject(this); - } - - public ABCPanel(MainPanel mainPanel) { - DefaultSyntaxKit.initKit(); - - this.mainPanel = mainPanel; - setLayout(new BorderLayout()); - - decompiledTextArea = new DecompiledEditorPane(this); - - searchPanel = new SearchPanel<>(new FlowLayout(), this); - - decompiledScrollPane = new JScrollPane(decompiledTextArea); - - JPanel iconDecPanel = new JPanel(); - iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); - JPanel iconsPanel = new JPanel(); - iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); - - JButton newTraitButton = new JButton(View.getIcon("traitadd16")); - newTraitButton.setMargin(new Insets(5, 5, 5, 5)); - newTraitButton.addActionListener(this); - newTraitButton.setActionCommand(ACTION_ADD_TRAIT); - newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); - iconsPanel.add(newTraitButton); - - scriptNameLabel = new JLabel("-"); - scriptNameLabel.setAlignmentX(0); - iconsPanel.setAlignmentX(0); - decompiledScrollPane.setAlignmentX(0); - iconDecPanel.add(scriptNameLabel); - iconDecPanel.add(iconsPanel); - iconDecPanel.add(decompiledScrollPane); - - JPanel decButtonsPan = new JPanel(new FlowLayout()); - decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); - decButtonsPan.add(editDecompiledButton); - decButtonsPan.add(experimentalLabel); - decButtonsPan.add(saveDecompiledButton); - decButtonsPan.add(cancelDecompiledButton); - - editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); - - saveDecompiledButton.addActionListener(this); - saveDecompiledButton.setActionCommand(ACTION_SAVE_DECOMPILED); - editDecompiledButton.addActionListener(this); - editDecompiledButton.setActionCommand(ACTION_EDIT_DECOMPILED); - - cancelDecompiledButton.addActionListener(this); - cancelDecompiledButton.setActionCommand(ACTION_CANCEL_DECOMPILED); - saveDecompiledButton.setVisible(false); - cancelDecompiledButton.setVisible(false); - decButtonsPan.setAlignmentX(0); - - JPanel decPanel = new JPanel(new BorderLayout()); - decPanel.add(searchPanel, BorderLayout.NORTH); - decPanel.add(iconDecPanel, BorderLayout.CENTER); - decPanel.add(decButtonsPan, BorderLayout.SOUTH); - detailPanel = new DetailPanel(this); - JPanel panB = new JPanel(); - panB.setLayout(new BorderLayout()); - panB.add(decPanel, BorderLayout.CENTER); - panB.add(decLabel, BorderLayout.NORTH); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); - splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - panB, detailPanel); - splitPane.setResizeWeight(0.5); - splitPane.setContinuousLayout(true); - - splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - if (!directEditing) { - Configuration.guiAvm2SplitPaneDividerLocation.set((int) pce.getNewValue()); - } - } - }); - decompiledTextArea.setContentType("text/actionscript"); - decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); - - JPanel pan2 = new JPanel(); - pan2.setLayout(new BorderLayout()); - pan2.add((abcComboBox = new JComboBox<>(new ABCComboBoxModel(new ArrayList()))), BorderLayout.NORTH); - - navigator = new TraitsList(this); - - navPanel = new JPanel(new BorderLayout()); - JPanel navIconsPanel = new JPanel(); - navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); - final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); - sortButton.setMargin(new Insets(3, 3, 3, 3)); - navIconsPanel.add(sortButton); - navPanel.add(navIconsPanel, BorderLayout.SOUTH); - navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); - sortButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - navigator.setSorted(sortButton.isSelected()); - navigator.updateUI(); - } - }); - - Main.startWork(AppStrings.translate("work.buildingscripttree") + "..."); - - /* splitPaneTreeVSNavigator = new JSplitPane(JSplitPane.VERTICAL_SPLIT, - treePanel, - navPanel); - splitPaneTreeVSNavigator.setResizeWeight(0.5); - splitPaneTreeVSNavigator.setContinuousLayout(true);*/ - tabbedPane = new JTabbedPane(); - tabbedPane.addTab(AppStrings.translate("traits"), navPanel); - //tabbedPane.setTabPlacement(JTabbedPane.BOTTOM); - - //pan2.add(tabbedPane, BorderLayout.CENTER); - abcComboBox.addItemListener(this); - /* - - splitPaneTreeNavVSDecompiledDetail = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - pan2, - splitPaneDecompiledVSDetail); - splitPaneTreeNavVSDecompiledDetail.setResizeWeight(0); - splitPaneTreeNavVSDecompiledDetail.setContinuousLayout(true); - //pan2.setPreferredSize(new Dimension(300, 200)); - - - - - - add(splitPaneTreeNavVSDecompiledDetail, BorderLayout.CENTER);*/ - add(splitPane, BorderLayout.CENTER); - - JPanel panConstants = new JPanel(); - panConstants.setLayout(new BorderLayout()); - - constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); - constantTable = new JTable(); - if (abc != null) { - autoResizeColWidth(constantTable, new UIntTableModel(abc)); - } - constantTable.setAutoCreateRowSorter(true); - - final ABCPanel t = this; - constantTable.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 2) { - if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME - int rowIndex = constantTable.getSelectedRow(); - if (rowIndex == -1) { - return; - } - int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); - if (multinameIndex > 0) { - UsageFrame usageFrame = new UsageFrame(t.swf.abcList, abc, multinameIndex, t); - usageFrame.setVisible(true); - } - } - } - } - }); - constantTypeList.addItemListener(this); - panConstants.add(constantTypeList, BorderLayout.NORTH); - panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); - tabbedPane.addTab(AppStrings.translate("constants"), panConstants); - } - - public void reload() { - switchAbc(listIndex); - decompiledTextArea.clearScriptCache(); - decompiledTextArea.reloadClass(); - detailPanel.methodTraitPanel.methodCodePanel.clear(); - } - - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getSource() == abcComboBox) { - int index = ((JComboBox) e.getSource()).getSelectedIndex(); - if (index == -1) { - return; - } - switchAbc(index - 1); - } - if (e.getSource() == constantTypeList) { - int index = ((JComboBox) e.getSource()).getSelectedIndex(); - if (index == -1) { - return; - } - updateConstList(); - } - } - - public void display() { - setVisible(true); - } - - public void hilightScript(SWF swf, String name) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - TreeNode scriptsNode = ttm.getSwfNode(swf).scriptsNode; - if (scriptsNode.getItem() instanceof ClassesListTreeModel) { - ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode.getItem(); - ScriptPack pack = null; - for (MyEntry item : clModel.getList()) { - if (item.key.toString().equals(name)) { - pack = item.value; - break; - } - } - if (pack != null) { - hilightScript(pack); - } - } - } - - public void hilightScript(ScriptPack pack) { - TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); - final TreePath tp = ttm.getTagPath(pack); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - mainPanel.tagTree.setSelectionPath(tp); - mainPanel.tagTree.scrollPathToVisible(tp); - } - }); - - } - - @Override - public void updateSearchPos(ABCPanelSearchResult item) { - ScriptPack pack = item.scriptPack; - setAbc(pack.abc); - decompiledTextArea.setScript(pack, swf.abcList); - hilightScript(pack); - decompiledTextArea.setCaretPosition(0); - - searchPanel.showQuickFindDialog(decompiledTextArea); - } - - public String lastDecompiled = null; - public boolean directEditing = false; - private int detWidth = 0; - private int detsp = 0; - - public void setDecompiledEditMode(boolean val) { - if (val) { - lastDecompiled = decompiledTextArea.getText(); - decompiledTextArea.setEditable(true); - saveDecompiledButton.setVisible(true); - editDecompiledButton.setVisible(false); - experimentalLabel.setVisible(false); - cancelDecompiledButton.setVisible(true); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(View.getIcon("editing16")); - directEditing = true; - detWidth = detailPanel.getWidth(); - detsp = splitPane.getDividerLocation(); - detailPanel.setVisible(false); - } else { - decompiledTextArea.setText(lastDecompiled); - decompiledTextArea.setEditable(false); - saveDecompiledButton.setVisible(false); - editDecompiledButton.setVisible(true); - experimentalLabel.setVisible(true); - cancelDecompiledButton.setVisible(false); - decompiledTextArea.getCaret().setVisible(true); - decLabel.setIcon(null); - directEditing = false; - detailPanel.setVisible(true); - detailPanel.setSize(detailPanel.getHeight(), detWidth); - splitPane.setDividerLocation(detsp); - } - decompiledTextArea.ignoreCarret = directEditing; - - decompiledTextArea.requestFocusInWindow(); - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case ACTION_EDIT_DECOMPILED: - File swc = Configuration.getPlayerSWC(); - final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; - if (swc == null) { - if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { - - java.awt.Desktop desktop = null; - if (java.awt.Desktop.isDesktopSupported()) { - desktop = java.awt.Desktop.getDesktop(); - if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) { - try { - java.net.URI uri = new java.net.URI(adobePage); - desktop.browse(uri); - } catch (URISyntaxException | IOException ex) { - } - } else { - desktop = null; - } - } - - int ret = 0; - do { - ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); - swc = Configuration.getPlayerSWC(); - } while (ret == JOptionPane.OK_OPTION && swc == null); - } - } - if (swc != null) { - if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { - setDecompiledEditMode(true); - } - } - break; - case ACTION_CANCEL_DECOMPILED: - setDecompiledEditMode(false); - break; - case ACTION_SAVE_DECOMPILED: - ScriptPack pack = decompiledTextArea.getScriptLeaf(); - String scriptName = pack.getPathScriptName() + ".as"; - int oldIndex = pack.scriptIndex; - int newIndex = abc.script_info.size(); - String documentClass = ""; - loopt: - for (Tag t : swf.tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t; - for (int i = 0; i < sc.tags.length; i++) { - if (sc.tags[i] == 0) { - documentClass = sc.names[i]; - break loopt; - } - } - } - } - boolean isDocumentClass = documentClass.equals(pack.getPath().toString()); - - try { - abc.script_info.get(oldIndex).delete(abc, true); - ActionScriptParser.compile(decompiledTextArea.getText(), abc,new ArrayList(), isDocumentClass, scriptName); - //Move newly added script to its position - abc.script_info.set(oldIndex, abc.script_info.get(newIndex)); - abc.script_info.remove(newIndex); - abc.pack(); //removes old classes/methods - ((Tag) abc.parentTag).setModified(true); - lastDecompiled = decompiledTextArea.getText(); - mainPanel.updateClassesList(); - List> packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex); - if(!packs.isEmpty()){ - hilightScript(swf,packs.get(0).key.toString()); - } - //decompiledTextArea.setClassIndex(-1); - //navigator.setClassIndex(-1, oldIndex); - setDecompiledEditMode(false); - View.showMessageDialog(this, AppStrings.translate("message.action.saved")); - //reload(); - } catch (ParseException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - ex.printStackTrace(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - decompiledTextArea.gotoLine((int) ex.line); - } catch (CompilationException ex) { - abc.script_info.get(oldIndex).delete(abc, false); - ex.printStackTrace(); - View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - decompiledTextArea.gotoLine((int) ex.line); - } catch (IOException | InterruptedException ex) { - //ignore - } - break; - case ACTION_ADD_TRAIT: - int class_index = decompiledTextArea.getClassIndex(); - if (class_index < 0) { - return; - } - if (newTraitDialog == null) { - newTraitDialog = new NewTraitDialog(); - } - int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); - - Trait t = null; - int kind; - int nskind; - String name = null; - boolean isStatic; - Multiname m; - - boolean again = false; - loopm: - do { - if (again) { - View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); - } - again = false; - if (!newTraitDialog.display()) { - return; - } - kind = newTraitDialog.getTraitType(); - nskind = newTraitDialog.getNamespaceKind(); - name = newTraitDialog.getTraitName(); - isStatic = newTraitDialog.getStatic(); - m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), -1, -1, new ArrayList()); - int mid = abc.constants.getMultinameId(m); - if (mid == 0) { - break; - } - for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - - for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { - if (tr.name_index == mid) { - again = true; - break; - } - } - } while (again); - switch (kind) { - case Trait.TRAIT_GETTER: - case Trait.TRAIT_SETTER: - case Trait.TRAIT_METHOD: - TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); - MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); - int method_info = abc.addMethodInfo(mi); - tm.method_info = method_info; - MethodBody body = new MethodBody(); - body.method_info = method_info; - body.init_scope_depth = 1; - body.max_regs = 1; - body.max_scope_depth = 1; - body.max_stack = 1; - body.exceptions = new ABCException[0]; - AVM2Code code = new AVM2Code(); - code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0], new byte[0])); - code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0], new byte[0])); - code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0], new byte[0])); - body.code = code; - Traits traits = new Traits(); - traits.traits = new ArrayList<>(); - body.traits = traits; - abc.addMethodBody(body); - mi.setBody(body); - t = tm; - break; - case Trait.TRAIT_SLOT: - case Trait.TRAIT_CONST: - TraitSlotConst ts = new TraitSlotConst(); - ts.type_index = int_type; - ts.value_kind = ValueKind.CONSTANT_Int; - ts.value_index = abc.constants.getIntId(0, true); - t = ts; - break; - } - if (t != null) { - t.kindType = kind; - t.name_index = abc.constants.getMultinameId(m, true); - int traitId; - if (isStatic) { - traitId = abc.class_info.get(class_index).static_traits.addTrait(t); - } else { - traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); - } - reload(); - decompiledTextArea.gotoTrait(traitId); - } - - break; - } - } -} +/* + * Copyright (C) 2010-2014 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.gui.abc; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; +import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocal0Ins; +import com.jpexs.decompiler.flash.abc.avm2.instructions.other.ReturnVoidIns; +import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns; +import com.jpexs.decompiler.flash.abc.avm2.parser.ParseException; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; +import com.jpexs.decompiler.flash.abc.types.ABCException; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.MethodInfo; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.Namespace; +import com.jpexs.decompiler.flash.abc.types.ValueKind; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; +import com.jpexs.decompiler.flash.abc.types.traits.Traits; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.HeaderLabel; +import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.SearchListener; +import com.jpexs.decompiler.flash.gui.SearchPanel; +import com.jpexs.decompiler.flash.gui.TagTreeModel; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DecimalTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.DoubleTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.IntTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.MultinameTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceSetTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.NamespaceTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.StringTableModel; +import com.jpexs.decompiler.flash.gui.abc.tablemodels.UIntTableModel; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.treenodes.TreeNode; +import com.jpexs.decompiler.graph.CompilationException; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.BevelBorder; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; +import javax.swing.tree.TreePath; +import jsyntaxpane.DefaultSyntaxKit; + +public class ABCPanel extends JPanel implements ItemListener, ActionListener, SearchListener, Freed { + + private MainPanel mainPanel; + public TraitsList navigator; + public ABC abc; + public SWF swf; + public JComboBox abcComboBox; + public int listIndex = -1; + public DecompiledEditorPane decompiledTextArea; + public JScrollPane decompiledScrollPane; + public JSplitPane splitPane; + //public JSplitPane splitPaneTreeVSNavigator; + //public JSplitPane splitPaneTreeNavVSDecompiledDetail; + private JTable constantTable; + public JComboBox constantTypeList; + public JLabel asmLabel = new HeaderLabel(AppStrings.translate("panel.disassembled")); + public JLabel decLabel = new HeaderLabel(AppStrings.translate("panel.decompiled")); + public DetailPanel detailPanel; + public JPanel navPanel; + public JTabbedPane tabbedPane; + public SearchPanel searchPanel; + private NewTraitDialog newTraitDialog; + public JLabel scriptNameLabel; + + static final String ACTION_SAVE_DECOMPILED = "SAVEDECOMPILED"; + static final String ACTION_EDIT_DECOMPILED = "EDITDECOMPILED"; + static final String ACTION_CANCEL_DECOMPILED = "CANCELDECOMPILED"; + + public JLabel experimentalLabel = new JLabel(AppStrings.translate("action.edit.experimental")); + public JButton editDecompiledButton = new JButton(AppStrings.translate("button.edit"), View.getIcon("edit16")); + public JButton saveDecompiledButton = new JButton(AppStrings.translate("button.save"), View.getIcon("save16")); + public JButton cancelDecompiledButton = new JButton(AppStrings.translate("button.cancel"), View.getIcon("cancel16")); + + static final String ACTION_ADD_TRAIT = "ADDTRAIT"; + + public boolean search(String txt, boolean ignoreCase, boolean regexp) { + if ((txt != null) && (!txt.isEmpty())) { + searchPanel.setOptions(ignoreCase, regexp); + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeNode scriptsNode = ttm.getSwfNode(mainPanel.getCurrentSwf()).scriptsNode; + final List found = new ArrayList<>(); + if (scriptsNode.getItem() instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode.getItem(); + List> allpacks = clModel.getList(); + final Pattern pat = regexp + ? Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0) + : Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + int pos = 0; + for (final MyEntry item : allpacks) { + pos++; + String workText = AppStrings.translate("work.searching"); + String decAdd = ""; + if (!decompiledTextArea.isCached(item.value)) { + decAdd = ", " + AppStrings.translate("work.decompiling"); + } + + try { + CancellableWorker worker = new CancellableWorker() { + + @Override + public Void doInBackground() throws Exception { + decompiledTextArea.cacheScriptPack(item.value, swf.abcList); + if (pat.matcher(decompiledTextArea.getCachedText(item.value)).find()) { + ABCPanelSearchResult searchResult = new ABCPanelSearchResult(); + searchResult.scriptPack = item.value; + searchResult.classPath = item.key; + found.add(searchResult); + } + return null; + } + }; + worker.execute(); + Main.startWork(workText + " \"" + txt + "\"" + decAdd + " - (" + pos + "/" + allpacks.size() + ") " + item.key.toString() + "... ", worker); + worker.get(); + } catch (InterruptedException ex) { + break; + } catch (ExecutionException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + System.gc(); + Main.stopWork(); + searchPanel.setSearchText(txt); + return searchPanel.setResults(found); + } + return false; + } + + private JTable autoResizeColWidth(final JTable table, final TableModel model) { + View.execInEventDispatch(new Runnable() { + @Override + public void run() { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.setModel(model); + + int margin = 5; + + for (int i = 0; i < table.getColumnCount(); i++) { + int vColIndex = i; + DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel(); + TableColumn col = colModel.getColumn(vColIndex); + int width; + + // Get width of column header + TableCellRenderer renderer = col.getHeaderRenderer(); + + if (renderer == null) { + renderer = table.getTableHeader().getDefaultRenderer(); + } + + Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); + + width = comp.getPreferredSize().width; + + // Get maximum width of column data + for (int r = 0; r < table.getRowCount(); r++) { + renderer = table.getCellRenderer(r, vColIndex); + comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, + r, vColIndex); + width = Math.max(width, comp.getPreferredSize().width); + } + + // Add margin + width += 2 * margin; + + // Set the width + col.setPreferredWidth(width); + } + + ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment( + SwingConstants.LEFT); + + // table.setAutoCreateRowSorter(true); + table.getTableHeader().setReorderingAllowed(false); + } + }); + + return table; + } + + public void setAbc(ABC abc) { + this.abc = abc; + updateConstList(); + } + + public void updateConstList() { + switch (constantTypeList.getSelectedIndex()) { + case 0: + autoResizeColWidth(constantTable, new UIntTableModel(abc)); + break; + case 1: + autoResizeColWidth(constantTable, new IntTableModel(abc)); + break; + case 2: + autoResizeColWidth(constantTable, new DoubleTableModel(abc)); + break; + case 3: + autoResizeColWidth(constantTable, new DecimalTableModel(abc)); + break; + case 4: + autoResizeColWidth(constantTable, new StringTableModel(abc)); + break; + case 5: + autoResizeColWidth(constantTable, new NamespaceTableModel(abc)); + break; + case 6: + autoResizeColWidth(constantTable, new NamespaceSetTableModel(abc)); + break; + case 7: + autoResizeColWidth(constantTable, new MultinameTableModel(abc)); + break; + } + //DefaultTableColumnModel colModel = (DefaultTableColumnModel) constantTable.getColumnModel(); + //colModel.getColumn(0).setMaxWidth(50); + } + + public void clearSwf() { + this.swf = null; + this.abc = null; + constantTable.setModel(new DefaultTableModel()); + abcComboBox.setModel(new ABCComboBoxModel(new ArrayList())); + navigator.clearABC(); + } + + public void setSwf(SWF swf) { + if (this.swf != swf) { + this.swf = swf; + listIndex = -1; + switchAbc(0); // todo honika: do we need this? + abcComboBox.setModel(new ABCComboBoxModel(swf.abcList)); + if (swf.abcList.size() > 0) { + this.abc = swf.abcList.get(0).getABC(); + } + + navigator.setABC(swf.abcList, abc); + } + } + + public void switchAbc(int index) { + listIndex = index; + + if (index != -1) { + this.abc = swf.abcList.get(index).getABC(); + } + updateConstList(); + } + + public void initSplits() { + //splitPaneTreeVSNavigator.setDividerLocation(splitPaneTreeVSNavigator.getHeight() / 2); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + //splitPaneTreeNavVSDecompiledDetail.setDividerLocation(splitPaneTreeNavVSDecompiledDetail.getWidth() * 1 / 3); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } + + splitPane.setDividerLocation(Configuration.guiAvm2SplitPaneDividerLocation.get(splitPane.getWidth() * 1 / 2)); + + } + + private boolean isFreeing; + + @Override + public boolean isFreeing() { + return isFreeing; + } + + @Override + public void free() { + isFreeing = true; + Helper.emptyObject(this); + } + + public ABCPanel(MainPanel mainPanel) { + DefaultSyntaxKit.initKit(); + + this.mainPanel = mainPanel; + setLayout(new BorderLayout()); + + decompiledTextArea = new DecompiledEditorPane(this); + + searchPanel = new SearchPanel<>(new FlowLayout(), this); + + decompiledScrollPane = new JScrollPane(decompiledTextArea); + + JPanel iconDecPanel = new JPanel(); + iconDecPanel.setLayout(new BoxLayout(iconDecPanel, BoxLayout.Y_AXIS)); + JPanel iconsPanel = new JPanel(); + iconsPanel.setLayout(new BoxLayout(iconsPanel, BoxLayout.X_AXIS)); + + JButton newTraitButton = new JButton(View.getIcon("traitadd16")); + newTraitButton.setMargin(new Insets(5, 5, 5, 5)); + newTraitButton.addActionListener(this); + newTraitButton.setActionCommand(ACTION_ADD_TRAIT); + newTraitButton.setToolTipText(AppStrings.translate("button.addtrait")); + iconsPanel.add(newTraitButton); + + scriptNameLabel = new JLabel("-"); + scriptNameLabel.setAlignmentX(0); + iconsPanel.setAlignmentX(0); + decompiledScrollPane.setAlignmentX(0); + iconDecPanel.add(scriptNameLabel); + iconDecPanel.add(iconsPanel); + iconDecPanel.add(decompiledScrollPane); + + JPanel decButtonsPan = new JPanel(new FlowLayout()); + decButtonsPan.setBorder(new BevelBorder(BevelBorder.RAISED)); + decButtonsPan.add(editDecompiledButton); + decButtonsPan.add(experimentalLabel); + decButtonsPan.add(saveDecompiledButton); + decButtonsPan.add(cancelDecompiledButton); + + editDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + saveDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + cancelDecompiledButton.setMargin(new Insets(3, 3, 3, 10)); + + saveDecompiledButton.addActionListener(this); + saveDecompiledButton.setActionCommand(ACTION_SAVE_DECOMPILED); + editDecompiledButton.addActionListener(this); + editDecompiledButton.setActionCommand(ACTION_EDIT_DECOMPILED); + + cancelDecompiledButton.addActionListener(this); + cancelDecompiledButton.setActionCommand(ACTION_CANCEL_DECOMPILED); + saveDecompiledButton.setVisible(false); + cancelDecompiledButton.setVisible(false); + decButtonsPan.setAlignmentX(0); + + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(searchPanel, BorderLayout.NORTH); + decPanel.add(iconDecPanel, BorderLayout.CENTER); + decPanel.add(decButtonsPan, BorderLayout.SOUTH); + detailPanel = new DetailPanel(this); + JPanel panB = new JPanel(); + panB.setLayout(new BorderLayout()); + panB.add(decPanel, BorderLayout.CENTER); + panB.add(decLabel, BorderLayout.NORTH); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + //decLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); + splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + panB, detailPanel); + splitPane.setResizeWeight(0.5); + splitPane.setContinuousLayout(true); + + splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (!directEditing) { + Configuration.guiAvm2SplitPaneDividerLocation.set((int) pce.getNewValue()); + } + } + }); + decompiledTextArea.setContentType("text/actionscript"); + decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize())); + + JPanel pan2 = new JPanel(); + pan2.setLayout(new BorderLayout()); + pan2.add((abcComboBox = new JComboBox<>(new ABCComboBoxModel(new ArrayList()))), BorderLayout.NORTH); + + navigator = new TraitsList(this); + + navPanel = new JPanel(new BorderLayout()); + JPanel navIconsPanel = new JPanel(); + navIconsPanel.setLayout(new BoxLayout(navIconsPanel, BoxLayout.X_AXIS)); + final JToggleButton sortButton = new JToggleButton(View.getIcon("sort16")); + sortButton.setMargin(new Insets(3, 3, 3, 3)); + navIconsPanel.add(sortButton); + navPanel.add(navIconsPanel, BorderLayout.SOUTH); + navPanel.add(new JScrollPane(navigator), BorderLayout.CENTER); + sortButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + navigator.setSorted(sortButton.isSelected()); + navigator.updateUI(); + } + }); + + Main.startWork(AppStrings.translate("work.buildingscripttree") + "..."); + + /* splitPaneTreeVSNavigator = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + treePanel, + navPanel); + splitPaneTreeVSNavigator.setResizeWeight(0.5); + splitPaneTreeVSNavigator.setContinuousLayout(true);*/ + tabbedPane = new JTabbedPane(); + tabbedPane.addTab(AppStrings.translate("traits"), navPanel); + //tabbedPane.setTabPlacement(JTabbedPane.BOTTOM); + + //pan2.add(tabbedPane, BorderLayout.CENTER); + abcComboBox.addItemListener(this); + /* + + splitPaneTreeNavVSDecompiledDetail = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + pan2, + splitPaneDecompiledVSDetail); + splitPaneTreeNavVSDecompiledDetail.setResizeWeight(0); + splitPaneTreeNavVSDecompiledDetail.setContinuousLayout(true); + //pan2.setPreferredSize(new Dimension(300, 200)); + + + + + + add(splitPaneTreeNavVSDecompiledDetail, BorderLayout.CENTER);*/ + add(splitPane, BorderLayout.CENTER); + + JPanel panConstants = new JPanel(); + panConstants.setLayout(new BorderLayout()); + + constantTypeList = new JComboBox<>(new String[]{"UINT", "INT", "DOUBLE", "DECIMAL", "STRING", "NAMESPACE", "NAMESPACESET", "MULTINAME"}); + constantTable = new JTable(); + if (abc != null) { + autoResizeColWidth(constantTable, new UIntTableModel(abc)); + } + constantTable.setAutoCreateRowSorter(true); + + final ABCPanel t = this; + constantTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + if (constantTypeList.getSelectedIndex() == 7) { //MULTINAME + int rowIndex = constantTable.getSelectedRow(); + if (rowIndex == -1) { + return; + } + int multinameIndex = constantTable.convertRowIndexToModel(rowIndex); + if (multinameIndex > 0) { + UsageFrame usageFrame = new UsageFrame(t.swf.abcList, abc, multinameIndex, t); + usageFrame.setVisible(true); + } + } + } + } + }); + constantTypeList.addItemListener(this); + panConstants.add(constantTypeList, BorderLayout.NORTH); + panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER); + tabbedPane.addTab(AppStrings.translate("constants"), panConstants); + } + + public void reload() { + switchAbc(listIndex); + decompiledTextArea.clearScriptCache(); + decompiledTextArea.reloadClass(); + detailPanel.methodTraitPanel.methodCodePanel.clear(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getSource() == abcComboBox) { + int index = ((JComboBox) e.getSource()).getSelectedIndex(); + if (index == -1) { + return; + } + switchAbc(index - 1); + } + if (e.getSource() == constantTypeList) { + int index = ((JComboBox) e.getSource()).getSelectedIndex(); + if (index == -1) { + return; + } + updateConstList(); + } + } + + public void display() { + setVisible(true); + } + + public void hilightScript(SWF swf, String name) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + TreeNode scriptsNode = ttm.getSwfNode(swf).scriptsNode; + if (scriptsNode.getItem() instanceof ClassesListTreeModel) { + ClassesListTreeModel clModel = (ClassesListTreeModel) scriptsNode.getItem(); + ScriptPack pack = null; + for (MyEntry item : clModel.getList()) { + if (item.key.toString().equals(name)) { + pack = item.value; + break; + } + } + if (pack != null) { + hilightScript(pack); + } + } + } + + public void hilightScript(ScriptPack pack) { + TagTreeModel ttm = (TagTreeModel) mainPanel.tagTree.getModel(); + final TreePath tp = ttm.getTagPath(pack); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainPanel.tagTree.setSelectionPath(tp); + mainPanel.tagTree.scrollPathToVisible(tp); + } + }); + + } + + @Override + public void updateSearchPos(ABCPanelSearchResult item) { + ScriptPack pack = item.scriptPack; + setAbc(pack.abc); + decompiledTextArea.setScript(pack, swf.abcList); + hilightScript(pack); + decompiledTextArea.setCaretPosition(0); + + searchPanel.showQuickFindDialog(decompiledTextArea); + } + + public String lastDecompiled = null; + public boolean directEditing = false; + private int detWidth = 0; + private int detsp = 0; + + public void setDecompiledEditMode(boolean val) { + if (val) { + lastDecompiled = decompiledTextArea.getText(); + decompiledTextArea.setEditable(true); + saveDecompiledButton.setVisible(true); + editDecompiledButton.setVisible(false); + experimentalLabel.setVisible(false); + cancelDecompiledButton.setVisible(true); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(View.getIcon("editing16")); + directEditing = true; + detWidth = detailPanel.getWidth(); + detsp = splitPane.getDividerLocation(); + detailPanel.setVisible(false); + } else { + decompiledTextArea.setText(lastDecompiled); + decompiledTextArea.setEditable(false); + saveDecompiledButton.setVisible(false); + editDecompiledButton.setVisible(true); + experimentalLabel.setVisible(true); + cancelDecompiledButton.setVisible(false); + decompiledTextArea.getCaret().setVisible(true); + decLabel.setIcon(null); + directEditing = false; + detailPanel.setVisible(true); + detailPanel.setSize(detailPanel.getHeight(), detWidth); + splitPane.setDividerLocation(detsp); + } + decompiledTextArea.ignoreCarret = directEditing; + + decompiledTextArea.requestFocusInWindow(); + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case ACTION_EDIT_DECOMPILED: + File swc = Configuration.getPlayerSWC(); + final String adobePage = "http://www.adobe.com/support/flashplayer/downloads.html"; + if (swc == null) { + if (View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.needed").replace("%adobehomepage%", adobePage), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION) { + + java.awt.Desktop desktop = null; + if (java.awt.Desktop.isDesktopSupported()) { + desktop = java.awt.Desktop.getDesktop(); + if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) { + try { + java.net.URI uri = new java.net.URI(adobePage); + desktop.browse(uri); + } catch (URISyntaxException | IOException ex) { + } + } else { + desktop = null; + } + } + + int ret = 0; + do { + ret = View.showConfirmDialog(this, AppStrings.translate("message.action.playerglobal.place").replace("%libpath%", Configuration.getFlashLibPath().getAbsolutePath()), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); + swc = Configuration.getPlayerSWC(); + } while (ret == JOptionPane.OK_OPTION && swc == null); + } + } + if (swc != null) { + if (View.showConfirmDialog(null, AppStrings.translate("message.confirm.experimental.function"), AppStrings.translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, Configuration.warningExperimentalAS3Edit, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION) { + setDecompiledEditMode(true); + } + } + break; + case ACTION_CANCEL_DECOMPILED: + setDecompiledEditMode(false); + break; + case ACTION_SAVE_DECOMPILED: + ScriptPack pack = decompiledTextArea.getScriptLeaf(); + String scriptName = pack.getPathScriptName() + ".as"; + int oldIndex = pack.scriptIndex; + int newIndex = abc.script_info.size(); + String documentClass = ""; + loopt: + for (Tag t : swf.tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.length; i++) { + if (sc.tags[i] == 0) { + documentClass = sc.names[i]; + break loopt; + } + } + } + } + boolean isDocumentClass = documentClass.equals(pack.getPath().toString()); + + try { + abc.script_info.get(oldIndex).delete(abc, true); + ActionScriptParser.compile(decompiledTextArea.getText(), abc, new ArrayList(), isDocumentClass, scriptName); + //Move newly added script to its position + abc.script_info.set(oldIndex, abc.script_info.get(newIndex)); + abc.script_info.remove(newIndex); + abc.pack(); //removes old classes/methods + ((Tag) abc.parentTag).setModified(true); + lastDecompiled = decompiledTextArea.getText(); + mainPanel.updateClassesList(); + List> packs = abc.script_info.get(oldIndex).getPacks(abc, oldIndex); + if (!packs.isEmpty()) { + hilightScript(swf, packs.get(0).key.toString()); + } + //decompiledTextArea.setClassIndex(-1); + //navigator.setClassIndex(-1, oldIndex); + setDecompiledEditMode(false); + View.showMessageDialog(this, AppStrings.translate("message.action.saved")); + //reload(); + } catch (ParseException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + ex.printStackTrace(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + decompiledTextArea.gotoLine((int) ex.line); + } catch (CompilationException ex) { + abc.script_info.get(oldIndex).delete(abc, false); + ex.printStackTrace(); + View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + decompiledTextArea.gotoLine((int) ex.line); + } catch (IOException | InterruptedException ex) { + //ignore + } + break; + case ACTION_ADD_TRAIT: + int class_index = decompiledTextArea.getClassIndex(); + if (class_index < 0) { + return; + } + if (newTraitDialog == null) { + newTraitDialog = new NewTraitDialog(); + } + int void_type = abc.constants.getPublicQnameId("void", true);//abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("void"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + int int_type = abc.constants.getPublicQnameId("int", true); //abc.constants.forceGetMultinameId(new Multiname(Multiname.QNAME, abc.constants.forceGetStringId("int"), abc.constants.forceGetNamespaceId(new Namespace(Namespace.KIND_PACKAGE, abc.constants.forceGetStringId("")), 0), -1, -1, new ArrayList())); + + Trait t = null; + int kind; + int nskind; + String name = null; + boolean isStatic; + Multiname m; + + boolean again = false; + loopm: + do { + if (again) { + View.showMessageDialog(null, AppStrings.translate("error.trait.exists").replace("%name%", name), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE); + } + again = false; + if (!newTraitDialog.display()) { + return; + } + kind = newTraitDialog.getTraitType(); + nskind = newTraitDialog.getNamespaceKind(); + name = newTraitDialog.getTraitName(); + isStatic = newTraitDialog.getStatic(); + m = new Multiname(Multiname.QNAME, abc.constants.getStringId(name, true), abc.constants.getNamespaceId(new Namespace(nskind, abc.constants.getStringId("", true)), 0, true), -1, -1, new ArrayList()); + int mid = abc.constants.getMultinameId(m); + if (mid == 0) { + break; + } + for (Trait tr : abc.class_info.get(class_index).static_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + + for (Trait tr : abc.instance_info.get(class_index).instance_traits.traits) { + if (tr.name_index == mid) { + again = true; + break; + } + } + } while (again); + switch (kind) { + case Trait.TRAIT_GETTER: + case Trait.TRAIT_SETTER: + case Trait.TRAIT_METHOD: + TraitMethodGetterSetter tm = new TraitMethodGetterSetter(); + MethodInfo mi = new MethodInfo(new int[0], void_type, abc.constants.getStringId(name, true), 0, new ValueKind[0], new int[0]); + int method_info = abc.addMethodInfo(mi); + tm.method_info = method_info; + MethodBody body = new MethodBody(); + body.method_info = method_info; + body.init_scope_depth = 1; + body.max_regs = 1; + body.max_scope_depth = 1; + body.max_stack = 1; + body.exceptions = new ABCException[0]; + AVM2Code code = new AVM2Code(); + code.code.add(new AVM2Instruction(0, new GetLocal0Ins(), new int[0], new byte[0])); + code.code.add(new AVM2Instruction(0, new PushScopeIns(), new int[0], new byte[0])); + code.code.add(new AVM2Instruction(0, new ReturnVoidIns(), new int[0], new byte[0])); + body.code = code; + Traits traits = new Traits(); + traits.traits = new ArrayList<>(); + body.traits = traits; + abc.addMethodBody(body); + mi.setBody(body); + t = tm; + break; + case Trait.TRAIT_SLOT: + case Trait.TRAIT_CONST: + TraitSlotConst ts = new TraitSlotConst(); + ts.type_index = int_type; + ts.value_kind = ValueKind.CONSTANT_Int; + ts.value_index = abc.constants.getIntId(0, true); + t = ts; + break; + } + if (t != null) { + t.kindType = kind; + t.name_index = abc.constants.getMultinameId(m, true); + int traitId; + if (isStatic) { + traitId = abc.class_info.get(class_index).static_traits.addTrait(t); + } else { + traitId = abc.class_info.get(class_index).static_traits.traits.size() + abc.instance_info.get(class_index).instance_traits.addTrait(t); + } + reload(); + decompiledTextArea.gotoTrait(traitId); + } + + break; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java index 4362b1cde..5d6344a5c 100644 --- a/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/player/FlashPlayerPanel.java @@ -1,487 +1,487 @@ -/* - * Copyright (C) 2010-2014 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.gui.player; - -import com.jpexs.decompiler.flash.gui.FlashUnsupportedException; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.utf8.Utf8Helper; -import com.sun.jna.Native; -import com.sun.jna.Platform; -import com.sun.jna.WString; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.SHELLEXECUTEINFO; -import com.sun.jna.platform.win32.Shell32; -import com.sun.jna.platform.win32.WinDef; -import com.sun.jna.platform.win32.WinNT.HANDLE; -import com.sun.jna.platform.win32.WinUser; -import com.sun.jna.ptr.IntByReference; -import java.awt.Color; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Panel; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class FlashPlayerPanel extends Panel implements Closeable, MediaDisplay { - - private boolean executed = false; - private String flash; - private HANDLE pipe; - private HANDLE process; - private WinDef.HWND hwnd; - private WinDef.HWND hwndFrame; - private Component frame; - private boolean stopped = true; - private static final int CMD_PLAY = 1; - private static final int CMD_RESIZE = 2; - private static final int CMD_BGCOLOR = 3; - private static final int CMD_CURRENT_FRAME = 4; - private static final int CMD_TOTAL_FRAMES = 5; - private static final int CMD_PAUSE = 6; - private static final int CMD_RESUME = 7; - private static final int CMD_PLAYING = 8; - private static final int CMD_REWIND = 9; - private static final int CMD_GOTO = 10; - private static final int CMD_CALL = 11; - private static final int CMD_GETVARIABLE = 12; - private static final int CMD_SETVARIABLE = 13; - private static final int PIPE_TIMEOUT_MS = 1000; - private int frameRate; - public boolean specialPlayback = false; - private boolean specialPlaying = false; - - public synchronized String getVariable(String name) throws IOException { - if (pipe != null) { - writeToPipe(new byte[]{CMD_GETVARIABLE}); - int nameLen = name.getBytes().length; - writeToPipe(new byte[]{(byte) ((nameLen >> 8) & 0xff), (byte) (nameLen & 0xff)}); - writeToPipe(name.getBytes()); - byte res[] = new byte[2]; - readFromPipe(res); - int retLen = ((res[0] & 0xff) << 8) + (res[1] & 0xff); - res = new byte[retLen]; - readFromPipe(res); - String ret = new String(res, 0, retLen); - return ret; - } - return null; - } - - public synchronized void setVariable(String name, String value) throws IOException { - if (pipe != null) { - writeToPipe(new byte[]{CMD_SETVARIABLE}); - int nameLen = name.getBytes().length; - writeToPipe(new byte[]{(byte) ((nameLen >> 8) & 0xff), (byte) (nameLen & 0xff)}); - writeToPipe(name.getBytes()); - - int valLen = value.getBytes().length; - writeToPipe(new byte[]{(byte) ((valLen >> 8) & 0xff), (byte) (valLen & 0xff)}); - writeToPipe(value.getBytes()); - } - } - - public synchronized String call(String callString) throws IOException { - if (pipe != null) { - writeToPipe(new byte[]{CMD_CALL}); - int callLen = callString.getBytes().length; - writeToPipe(new byte[]{(byte) ((callLen >> 8) & 0xff), (byte) (callLen & 0xff)}); - writeToPipe(callString.getBytes()); - - byte res[] = new byte[2]; - readFromPipe(res); - int retLen = ((res[0] & 0xff) << 8) + (res[1] & 0xff); - res = new byte[retLen]; - readFromPipe(res); - String ret = new String(res, 0, retLen); - return ret; - } - return null; - } - - private synchronized void resize() throws IOException { - if (pipe != null) { - writeToPipe(new byte[]{CMD_RESIZE}); - writeToPipe(new byte[]{ - (byte) (getWidth() / 256), (byte) (getWidth() % 256), - (byte) (getHeight() / 256), (byte) (getHeight() % 256),}); - } - } - - private int __getCurrentFrame() throws IOException { - byte[] res = new byte[2]; - writeToPipe(new byte[]{CMD_CURRENT_FRAME}); - readFromPipe(res); - return ((res[0] & 0xff) << 8) + (res[1] & 0xff); - } - - @Override - public synchronized int getCurrentFrame() { - try { - if (specialPlayback) { - if (!specialPlaying) { - return specialPosition; - } - String posStr = getVariable("_root.my_sound.position"); - if (posStr != null) { - return Integer.parseInt(posStr); - } - } - return __getCurrentFrame(); - } catch (IOException ex) { - return 0; - } - } - - @Override - public synchronized int getTotalFrames() { - try { - if (specialPlayback) { - String durStr = getVariable("_root.my_sound.duration"); - if (durStr != null) { - return Integer.parseInt(durStr); - } - } - byte[] res = new byte[2]; - writeToPipe(new byte[]{CMD_TOTAL_FRAMES}); - readFromPipe(res); - return ((res[0] & 0xff) << 8) + (res[1] & 0xff); - } catch (IOException ex) { - return 0; - } - } - - @Override - public synchronized void setBackground(Color color) { - try { - writeToPipe(new byte[]{CMD_BGCOLOR}); - writeToPipe(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()}); - } catch (IOException ex) { - } - } - - public FlashPlayerPanel(Component frame) { - if (!Platform.isWindows()) { - throw new FlashUnsupportedException(); - } - this.frame = frame; - addComponentListener(new ComponentListener() { - @Override - public void componentResized(ComponentEvent e) { - try { - resize(); - } catch (IOException ex) { - } - } - - @Override - public void componentMoved(ComponentEvent e) { - } - - @Override - public void componentShown(ComponentEvent e) { - componentResized(e); - } - - @Override - public void componentHidden(ComponentEvent e) { - } - }); - } - - private synchronized void execute() { - if (!executed) { - hwnd = new WinDef.HWND(); - hwnd.setPointer(Native.getComponentPointer(this)); - - hwndFrame = new WinDef.HWND(); - hwndFrame.setPointer(Native.getComponentPointer(frame)); - - startFlashPlayer(); - - executed = true; - } - } - - private void restartFlashPlayer() { - Kernel32.INSTANCE.TerminateProcess(process, 0); - Kernel32.INSTANCE.CloseHandle(pipe); - startFlashPlayer(); - } - - private void startFlashPlayer() { - String path = Utf8Helper.urlDecode(FlashPlayerPanel.class.getProtectionDomain().getCodeSource().getLocation().getPath()); - String appDir = new File(path).getParentFile().getAbsolutePath(); - if (!appDir.endsWith("\\")) { - appDir += "\\"; - } - String exePath = appDir + "lib\\FlashPlayer.exe"; - File f = new File(exePath); - if (!f.exists()) { - Logger.getLogger(FlashPlayerPanel.class.getName()).log(Level.SEVERE, "FlashPlayer.exe not found: " + f.getPath()); - return; - } - - String pipeName = "\\\\.\\pipe\\ffdec_flashplayer_" + hwnd.getPointer().hashCode(); - pipe = Kernel32.INSTANCE.CreateNamedPipe(pipeName, Kernel32.PIPE_ACCESS_DUPLEX, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); - - SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO(); - sei.fMask = 0x00000040; - sei.lpFile = new WString(exePath); - sei.lpParameters = new WString(hwnd.getPointer().hashCode() + " " + hwndFrame.getPointer().hashCode()); - sei.nShow = WinUser.SW_NORMAL; - Shell32.INSTANCE.ShellExecuteEx(sei); - process = sei.hProcess; - - Kernel32.INSTANCE.ConnectNamedPipe(pipe, null); - } - - public synchronized void stopSWF() { - displaySWF("-", null, 1); - stopped = true; - } - - public synchronized boolean isStopped() { - return stopped; - } - - public synchronized void displaySWF(String flash, Color bgColor, int frameRate) { - try { - this.flash = flash; - repaint(); - this.frameRate = frameRate; - execute(); - if (bgColor != null) { - setBackground(bgColor); - } - resize(); - if (pipe != null) { - writeToPipe(new byte[]{CMD_PLAY}); - writeToPipe(new byte[]{(byte) flash.getBytes().length}); - writeToPipe(flash.getBytes()); - } - stopped = false; - specialPlaying = false; - specialPosition = 0; - if (specialPlayback) { - play(); - } - } catch (IOException ex) { - } - } - - @Override - public void close() throws IOException { - Kernel32.INSTANCE.CloseHandle(pipe); - Kernel32.INSTANCE.TerminateProcess(process, 0); - } - - @Override - public void paint(Graphics g) { - if (flash != null) { - execute(); - } - super.paint(g); - } - private int specialPosition = 0; - - private synchronized void __pause() throws IOException { - writeToPipe(new byte[]{CMD_PAUSE}); - } - - @Override - public void pause() { - try { - if (specialPlayback) { - specialPosition = getCurrentFrame(); - __gotoFrame(3); - __play(); - specialPlaying = false; - return; - } - __pause(); - } catch (IOException ex) { - } - } - - @Override - public void rewind() { - try { - if (specialPlayback) { - boolean plays = specialPlaying; - pause(); - specialPosition = 0; - if (plays) { - play(); - } - - return; - } - writeToPipe(new byte[]{CMD_REWIND}); - } catch (IOException ex) { - } - } - - private synchronized void __play() throws IOException { - writeToPipe(new byte[]{CMD_RESUME}); - } - - @Override - public void play() { - try { - if (specialPlayback) { - double p = (((double) specialPosition) / 1000.0); - setVariable("_root.execParam", "" + p); - __gotoFrame(1); - __play(); - specialPlaying = true; - return; - } - __play(); - } catch (IOException ex) { - } - } - - @Override - public boolean isPlaying() { - try { - if (specialPlayback) { - return specialPlaying; - } - writeToPipe(new byte[]{CMD_PLAYING}); - byte[] res = new byte[1]; - readFromPipe(res); - return res[0] == 1; - } catch (IOException ex) { - return false; - } - } - - private synchronized void __gotoFrame(int frame) throws IOException { - writeToPipe(new byte[]{CMD_GOTO}); - writeToPipe(new byte[]{(byte) ((frame >> 8) & 0xff), (byte) (frame & 0xff)}); - } - - @Override - public void gotoFrame(int frame) { - try { - if (specialPlayback) { - if (specialPlaying) { - pause(); - specialPosition = frame; - play(); - } else { - specialPosition = frame; - } - return; - } - __gotoFrame(frame); - } catch (IOException ex) { - } - } - - @Override - public int getFrameRate() { - if (specialPlayback) { - return 1000; - } - return frameRate; - } - - @Override - public boolean isLoaded() { - return !isStopped(); - } - - private synchronized boolean writeToPipe(final byte[] data) throws IOException { - final IntByReference ibr = new IntByReference(); - int result = -1; - try { - result = CancellableWorker.call(new Callable() { - @Override - public Integer call() throws Exception { - boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null); - if (!result) { - return Kernel32.INSTANCE.GetLastError(); - } - if (ibr.getValue() != data.length) { - return -1; - } - return 0; - } - }, PIPE_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException ex) { - // ignore - } - if (result != 0) { - if (result == Kernel32.ERROR_NO_DATA || result == -1) { - restartFlashPlayer(); - throw new IOException("Pipe write error."); - } else { - // System.out.println("pipe write failed. datalength: " + data.length + " error:" + result); - } - } - return result == 0; - } - - private synchronized boolean readFromPipe(final byte[] res) throws IOException { - final IntByReference ibr = new IntByReference(); - int result = -1; - try { - result = CancellableWorker.call(new Callable() { - @Override - public Integer call() throws Exception { - int read = 0; - while (read < res.length) { - byte[] data = new byte[res.length - read]; - boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null); - if (!result) { - return Kernel32.INSTANCE.GetLastError(); - } - int readNow = ibr.getValue(); - System.arraycopy(data, 0, res, read, readNow); - read += readNow; - } - return 0; - } - }, PIPE_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException ex) { - // ignore - } - if (result != 0) { - if (result == Kernel32.ERROR_BROKEN_PIPE || result == -1) { - restartFlashPlayer(); - throw new IOException("Pipe read error."); - } else { - // System.out.println("pipe read failed. result: " + result + " datalength: " + res.length + " received: " + ibr.getValue() + " error: " + result); - } - } - return result == 0; - } -} +/* + * Copyright (C) 2010-2014 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.gui.player; + +import com.jpexs.decompiler.flash.gui.FlashUnsupportedException; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.utf8.Utf8Helper; +import com.sun.jna.Native; +import com.sun.jna.Platform; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.SHELLEXECUTEINFO; +import com.sun.jna.platform.win32.Shell32; +import com.sun.jna.platform.win32.WinDef; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.platform.win32.WinUser; +import com.sun.jna.ptr.IntByReference; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class FlashPlayerPanel extends Panel implements Closeable, MediaDisplay { + + private boolean executed = false; + private String flash; + private HANDLE pipe; + private HANDLE process; + private WinDef.HWND hwnd; + private WinDef.HWND hwndFrame; + private Component frame; + private boolean stopped = true; + private static final int CMD_PLAY = 1; + private static final int CMD_RESIZE = 2; + private static final int CMD_BGCOLOR = 3; + private static final int CMD_CURRENT_FRAME = 4; + private static final int CMD_TOTAL_FRAMES = 5; + private static final int CMD_PAUSE = 6; + private static final int CMD_RESUME = 7; + private static final int CMD_PLAYING = 8; + private static final int CMD_REWIND = 9; + private static final int CMD_GOTO = 10; + private static final int CMD_CALL = 11; + private static final int CMD_GETVARIABLE = 12; + private static final int CMD_SETVARIABLE = 13; + private static final int PIPE_TIMEOUT_MS = 1000; + private int frameRate; + public boolean specialPlayback = false; + private boolean specialPlaying = false; + + public synchronized String getVariable(String name) throws IOException { + if (pipe != null) { + writeToPipe(new byte[]{CMD_GETVARIABLE}); + int nameLen = name.getBytes().length; + writeToPipe(new byte[]{(byte) ((nameLen >> 8) & 0xff), (byte) (nameLen & 0xff)}); + writeToPipe(name.getBytes()); + byte res[] = new byte[2]; + readFromPipe(res); + int retLen = ((res[0] & 0xff) << 8) + (res[1] & 0xff); + res = new byte[retLen]; + readFromPipe(res); + String ret = new String(res, 0, retLen); + return ret; + } + return null; + } + + public synchronized void setVariable(String name, String value) throws IOException { + if (pipe != null) { + writeToPipe(new byte[]{CMD_SETVARIABLE}); + int nameLen = name.getBytes().length; + writeToPipe(new byte[]{(byte) ((nameLen >> 8) & 0xff), (byte) (nameLen & 0xff)}); + writeToPipe(name.getBytes()); + + int valLen = value.getBytes().length; + writeToPipe(new byte[]{(byte) ((valLen >> 8) & 0xff), (byte) (valLen & 0xff)}); + writeToPipe(value.getBytes()); + } + } + + public synchronized String call(String callString) throws IOException { + if (pipe != null) { + writeToPipe(new byte[]{CMD_CALL}); + int callLen = callString.getBytes().length; + writeToPipe(new byte[]{(byte) ((callLen >> 8) & 0xff), (byte) (callLen & 0xff)}); + writeToPipe(callString.getBytes()); + + byte res[] = new byte[2]; + readFromPipe(res); + int retLen = ((res[0] & 0xff) << 8) + (res[1] & 0xff); + res = new byte[retLen]; + readFromPipe(res); + String ret = new String(res, 0, retLen); + return ret; + } + return null; + } + + private synchronized void resize() throws IOException { + if (pipe != null) { + writeToPipe(new byte[]{CMD_RESIZE}); + writeToPipe(new byte[]{ + (byte) (getWidth() / 256), (byte) (getWidth() % 256), + (byte) (getHeight() / 256), (byte) (getHeight() % 256),}); + } + } + + private int __getCurrentFrame() throws IOException { + byte[] res = new byte[2]; + writeToPipe(new byte[]{CMD_CURRENT_FRAME}); + readFromPipe(res); + return ((res[0] & 0xff) << 8) + (res[1] & 0xff); + } + + @Override + public synchronized int getCurrentFrame() { + try { + if (specialPlayback) { + if (!specialPlaying) { + return specialPosition; + } + String posStr = getVariable("_root.my_sound.position"); + if (posStr != null) { + return Integer.parseInt(posStr); + } + } + return __getCurrentFrame(); + } catch (IOException ex) { + return 0; + } + } + + @Override + public synchronized int getTotalFrames() { + try { + if (specialPlayback) { + String durStr = getVariable("_root.my_sound.duration"); + if (durStr != null) { + return Integer.parseInt(durStr); + } + } + byte[] res = new byte[2]; + writeToPipe(new byte[]{CMD_TOTAL_FRAMES}); + readFromPipe(res); + return ((res[0] & 0xff) << 8) + (res[1] & 0xff); + } catch (IOException ex) { + return 0; + } + } + + @Override + public synchronized void setBackground(Color color) { + try { + writeToPipe(new byte[]{CMD_BGCOLOR}); + writeToPipe(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()}); + } catch (IOException ex) { + } + } + + public FlashPlayerPanel(Component frame) { + if (!Platform.isWindows()) { + throw new FlashUnsupportedException(); + } + this.frame = frame; + addComponentListener(new ComponentListener() { + @Override + public void componentResized(ComponentEvent e) { + try { + resize(); + } catch (IOException ex) { + } + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + componentResized(e); + } + + @Override + public void componentHidden(ComponentEvent e) { + } + }); + } + + private synchronized void execute() { + if (!executed) { + hwnd = new WinDef.HWND(); + hwnd.setPointer(Native.getComponentPointer(this)); + + hwndFrame = new WinDef.HWND(); + hwndFrame.setPointer(Native.getComponentPointer(frame)); + + startFlashPlayer(); + + executed = true; + } + } + + private void restartFlashPlayer() { + Kernel32.INSTANCE.TerminateProcess(process, 0); + Kernel32.INSTANCE.CloseHandle(pipe); + startFlashPlayer(); + } + + private void startFlashPlayer() { + String path = Utf8Helper.urlDecode(FlashPlayerPanel.class.getProtectionDomain().getCodeSource().getLocation().getPath()); + String appDir = new File(path).getParentFile().getAbsolutePath(); + if (!appDir.endsWith("\\")) { + appDir += "\\"; + } + String exePath = appDir + "lib\\FlashPlayer.exe"; + File f = new File(exePath); + if (!f.exists()) { + Logger.getLogger(FlashPlayerPanel.class.getName()).log(Level.SEVERE, "FlashPlayer.exe not found: " + f.getPath()); + return; + } + + String pipeName = "\\\\.\\pipe\\ffdec_flashplayer_" + hwnd.getPointer().hashCode(); + pipe = Kernel32.INSTANCE.CreateNamedPipe(pipeName, Kernel32.PIPE_ACCESS_DUPLEX, Kernel32.PIPE_TYPE_BYTE, 1, 4096, 4096, 0, null); + + SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO(); + sei.fMask = 0x00000040; + sei.lpFile = new WString(exePath); + sei.lpParameters = new WString(hwnd.getPointer().hashCode() + " " + hwndFrame.getPointer().hashCode()); + sei.nShow = WinUser.SW_NORMAL; + Shell32.INSTANCE.ShellExecuteEx(sei); + process = sei.hProcess; + + Kernel32.INSTANCE.ConnectNamedPipe(pipe, null); + } + + public synchronized void stopSWF() { + displaySWF("-", null, 1); + stopped = true; + } + + public synchronized boolean isStopped() { + return stopped; + } + + public synchronized void displaySWF(String flash, Color bgColor, int frameRate) { + try { + this.flash = flash; + repaint(); + this.frameRate = frameRate; + execute(); + if (bgColor != null) { + setBackground(bgColor); + } + resize(); + if (pipe != null) { + writeToPipe(new byte[]{CMD_PLAY}); + writeToPipe(new byte[]{(byte) flash.getBytes().length}); + writeToPipe(flash.getBytes()); + } + stopped = false; + specialPlaying = false; + specialPosition = 0; + if (specialPlayback) { + play(); + } + } catch (IOException ex) { + } + } + + @Override + public void close() throws IOException { + Kernel32.INSTANCE.CloseHandle(pipe); + Kernel32.INSTANCE.TerminateProcess(process, 0); + } + + @Override + public void paint(Graphics g) { + if (flash != null) { + execute(); + } + super.paint(g); + } + private int specialPosition = 0; + + private synchronized void __pause() throws IOException { + writeToPipe(new byte[]{CMD_PAUSE}); + } + + @Override + public void pause() { + try { + if (specialPlayback) { + specialPosition = getCurrentFrame(); + __gotoFrame(3); + __play(); + specialPlaying = false; + return; + } + __pause(); + } catch (IOException ex) { + } + } + + @Override + public void rewind() { + try { + if (specialPlayback) { + boolean plays = specialPlaying; + pause(); + specialPosition = 0; + if (plays) { + play(); + } + + return; + } + writeToPipe(new byte[]{CMD_REWIND}); + } catch (IOException ex) { + } + } + + private synchronized void __play() throws IOException { + writeToPipe(new byte[]{CMD_RESUME}); + } + + @Override + public void play() { + try { + if (specialPlayback) { + double p = (((double) specialPosition) / 1000.0); + setVariable("_root.execParam", "" + p); + __gotoFrame(1); + __play(); + specialPlaying = true; + return; + } + __play(); + } catch (IOException ex) { + } + } + + @Override + public boolean isPlaying() { + try { + if (specialPlayback) { + return specialPlaying; + } + writeToPipe(new byte[]{CMD_PLAYING}); + byte[] res = new byte[1]; + readFromPipe(res); + return res[0] == 1; + } catch (IOException ex) { + return false; + } + } + + private synchronized void __gotoFrame(int frame) throws IOException { + writeToPipe(new byte[]{CMD_GOTO}); + writeToPipe(new byte[]{(byte) ((frame >> 8) & 0xff), (byte) (frame & 0xff)}); + } + + @Override + public void gotoFrame(int frame) { + try { + if (specialPlayback) { + if (specialPlaying) { + pause(); + specialPosition = frame; + play(); + } else { + specialPosition = frame; + } + return; + } + __gotoFrame(frame); + } catch (IOException ex) { + } + } + + @Override + public int getFrameRate() { + if (specialPlayback) { + return 1000; + } + return frameRate; + } + + @Override + public boolean isLoaded() { + return !isStopped(); + } + + private synchronized boolean writeToPipe(final byte[] data) throws IOException { + final IntByReference ibr = new IntByReference(); + int result = -1; + try { + result = CancellableWorker.call(new Callable() { + @Override + public Integer call() throws Exception { + boolean result = Kernel32.INSTANCE.WriteFile(pipe, data, data.length, ibr, null); + if (!result) { + return Kernel32.INSTANCE.GetLastError(); + } + if (ibr.getValue() != data.length) { + return -1; + } + return 0; + } + }, PIPE_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException ex) { + // ignore + } + if (result != 0) { + if (result == Kernel32.ERROR_NO_DATA || result == -1) { + restartFlashPlayer(); + throw new IOException("Pipe write error."); + } else { + // System.out.println("pipe write failed. datalength: " + data.length + " error:" + result); + } + } + return result == 0; + } + + private synchronized boolean readFromPipe(final byte[] res) throws IOException { + final IntByReference ibr = new IntByReference(); + int result = -1; + try { + result = CancellableWorker.call(new Callable() { + @Override + public Integer call() throws Exception { + int read = 0; + while (read < res.length) { + byte[] data = new byte[res.length - read]; + boolean result = Kernel32.INSTANCE.ReadFile(pipe, data, data.length, ibr, null); + if (!result) { + return Kernel32.INSTANCE.GetLastError(); + } + int readNow = ibr.getValue(); + System.arraycopy(data, 0, res, read, readNow); + read += readNow; + } + return 0; + } + }, PIPE_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException ex) { + // ignore + } + if (result != 0) { + if (result == Kernel32.ERROR_BROKEN_PIPE || result == -1) { + restartFlashPlayer(); + throw new IOException("Pipe read error."); + } else { + // System.out.println("pipe read failed. result: " + result + " datalength: " + res.length + " received: " + ibr.getValue() + " error: " + result); + } + } + return result == 0; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java index e564411f2..d3c2ffa4d 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineButton2Tag.java @@ -1,321 +1,335 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; -import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Cache; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Extends the capabilities of DefineButton by allowing any state transition to - * trigger actions - * - * @author JPEXS - */ -public class DefineButton2Tag extends ButtonTag implements Container { - - /** - * ID for this character - */ - @SWFType(BasicType.UI16) - public int buttonId; - - @Reserved - @SWFType(value = BasicType.UB, count = 7) - public int reserved; - - /** - * Track as menu button - */ - public boolean trackAsMenu; - /** - * Characters that make up the button - */ - public List characters; - /** - * Actions to execute at particular button events - */ - public List actions = new ArrayList<>(); - public static final int ID = 34; - - private Timeline timeline; - - private boolean isSingleFrameInitialized; - private boolean isSingleFrame; - - @Override - public int getCharacterId() { - return buttonId; - } - - @Override - public List getRecords() { - return characters; - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineButton2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineButton2", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - buttonId = sis.readUI16(); - reserved = (int) sis.readUB(7); - trackAsMenu = sis.readUB(1) == 1; - int actionOffset = sis.readUI16(); - characters = sis.readBUTTONRECORDList(true); - if (actionOffset > 0) { - actions = sis.readBUTTONCONDACTIONList(swf, this); - } - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayInputStream bais = new ByteArrayInputStream(super.data); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, bais); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(buttonId); - sos.writeUB(7, reserved); - sos.writeUB(1, trackAsMenu ? 1 : 0); - - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - OutputStream os2 = baos2; - byte[] origbrdata = null; - if (Configuration.debugCopy.get()) { - SWFInputStream sis = new SWFInputStream(bais, getVersion()); - int len = sis.readUI16(); - if (len != 0) { - origbrdata = sis.readBytesEx(len - 2); - os2 = new CopyOutputStream(os2, new ByteArrayInputStream(origbrdata)); - } - } - try (SWFOutputStream sos2 = new SWFOutputStream(os2, getVersion())) { - sos2.writeBUTTONRECORDList(characters, true); - } - byte[] brdata = baos2.toByteArray(); - if (Configuration.debugCopy.get()) { - if (origbrdata != null) { - if (origbrdata.length != brdata.length) { - /*throw nso*/ - } - } - } - if (Configuration.debugCopy.get()) { - sos = new SWFOutputStream(baos, getVersion()); - } - if ((actions == null) || (actions.isEmpty())) { - sos.writeUI16(0); - } else { - sos.writeUI16(2 + brdata.length); - } - sos.write(brdata); - if (Configuration.debugCopy.get()) { - sos = new SWFOutputStream(new CopyOutputStream(baos, bais), getVersion()); - } - sos.writeBUTTONCONDACTIONList(actions); - sos.close(); - } catch (IOException e) { - Logger.getLogger(DefineButton2Tag.class.getName()).log(Level.SEVERE, null, e); - } - return baos.toByteArray(); - } - - /** - * Returns all sub-items - * - * @return List of sub-items - */ - @Override - public List getSubItems() { - List ret = new ArrayList<>(); - ret.addAll(actions); - return ret; - } - - /** - * Returns number of sub-items - * - * @return Number of sub-items - */ - @Override - public int getItemCount() { - return actions.size(); - } - - @Override - public Set getNeededCharacters() { - HashSet needed = new HashSet<>(); - for (BUTTONRECORD r : characters) { - needed.add(r.characterId); - } - return needed; - } - private static final Cache rectCache = Cache.getInstance(true); - - @Override - public RECT getRect() { - if (rectCache.contains(this)) { - return (RECT) rectCache.get(this); - } - RECT rect = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - for (BUTTONRECORD r : characters) { - CharacterTag ch = swf.characters.get(r.characterId); - if (ch instanceof BoundedTag) { - RECT r2 = ((BoundedTag) ch).getRect(); - MATRIX mat = r.placeMatrix; - if (mat != null) { - r2 = mat.apply(r2); - } - rect.Xmin = Math.min(r2.Xmin, rect.Xmin); - rect.Ymin = Math.min(r2.Ymin, rect.Ymin); - rect.Xmax = Math.max(r2.Xmax, rect.Xmax); - rect.Ymax = Math.max(r2.Ymax, rect.Ymax); - } - } - rectCache.put(this, rect); - return rect; - } - - @Override - public boolean trackAsMenu() { - return trackAsMenu; - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - if (!isSingleFrameInitialized) { - initialiteIsSingleFrame(); - } - return isSingleFrame; - } - - private synchronized void initialiteIsSingleFrame() { - if (!isSingleFrameInitialized) { - isSingleFrame = getTimeline().isSingleFrame(); - isSingleFrameInitialized = true; - } - } - - @Override - public Timeline getTimeline() { - if (timeline != null) { - return timeline; - } - timeline = new Timeline(swf, new ArrayList(), buttonId, getRect()); - - int maxDepth = 0; - Frame frameUp = new Frame(timeline); - Frame frameDown = new Frame(timeline); - Frame frameOver = new Frame(timeline); - Frame frameHit = new Frame(timeline); - for (BUTTONRECORD r : this.characters) { - - DepthState layer = new DepthState(swf, null); - layer.colorTransForm = r.colorTransform; - layer.blendMode = r.blendMode; - layer.filters = r.filterList; - layer.matrix = r.placeMatrix; - layer.characterId = r.characterId; - if (r.placeDepth > maxDepth) { - maxDepth = r.placeDepth; - } - - if (r.buttonStateUp) { - frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, false)); - } - if (r.buttonStateDown) { - frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, false)); - } - if (r.buttonStateOver) { - frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, false)); - } - if (r.buttonStateHitTest) { - frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, false)); - } - - } - timeline.frames.add(frameUp); - if (frameOver.layers.isEmpty()) { - frameOver = frameUp; - } - timeline.frames.add(frameOver); - if (frameDown.layers.isEmpty()) { - frameDown = frameOver; - } - timeline.frames.add(frameDown); - if (frameHit.layers.isEmpty()) { - frameHit = frameUp; - } - timeline.frames.add(frameHit); - return timeline; - } - - @Override - public void resetTimeline() { - timeline = null; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; +import com.jpexs.decompiler.flash.types.BUTTONRECORD; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Extends the capabilities of DefineButton by allowing any state transition to + * trigger actions + * + * @author JPEXS + */ +public class DefineButton2Tag extends ButtonTag implements Container { + + /** + * ID for this character + */ + @SWFType(BasicType.UI16) + public int buttonId; + + @Reserved + @SWFType(value = BasicType.UB, count = 7) + public int reserved; + + /** + * Track as menu button + */ + public boolean trackAsMenu; + /** + * Characters that make up the button + */ + public List characters; + /** + * Actions to execute at particular button events + */ + public List actions = new ArrayList<>(); + public static final int ID = 34; + + private Timeline timeline; + + private boolean isSingleFrameInitialized; + private boolean isSingleFrame; + + @Override + public int getCharacterId() { + return buttonId; + } + + @Override + public List getRecords() { + return characters; + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineButton2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineButton2", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + buttonId = sis.readUI16(); + reserved = (int) sis.readUB(7); + trackAsMenu = sis.readUB(1) == 1; + int actionOffset = sis.readUI16(); + characters = sis.readBUTTONRECORDList(true); + if (actionOffset > 0) { + actions = sis.readBUTTONCONDACTIONList(swf, this); + } + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayInputStream bais = new ByteArrayInputStream(super.data); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, bais); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(buttonId); + sos.writeUB(7, reserved); + sos.writeUB(1, trackAsMenu ? 1 : 0); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + OutputStream os2 = baos2; + byte[] origbrdata = null; + if (Configuration.debugCopy.get()) { + SWFInputStream sis = new SWFInputStream(bais, getVersion()); + int len = sis.readUI16(); + if (len != 0) { + origbrdata = sis.readBytesEx(len - 2); + os2 = new CopyOutputStream(os2, new ByteArrayInputStream(origbrdata)); + } + } + try (SWFOutputStream sos2 = new SWFOutputStream(os2, getVersion())) { + sos2.writeBUTTONRECORDList(characters, true); + } + byte[] brdata = baos2.toByteArray(); + if (Configuration.debugCopy.get()) { + if (origbrdata != null) { + if (origbrdata.length != brdata.length) { + /*throw nso*/ + } + } + } + if (Configuration.debugCopy.get()) { + sos = new SWFOutputStream(baos, getVersion()); + } + if ((actions == null) || (actions.isEmpty())) { + sos.writeUI16(0); + } else { + sos.writeUI16(2 + brdata.length); + } + sos.write(brdata); + if (Configuration.debugCopy.get()) { + sos = new SWFOutputStream(new CopyOutputStream(baos, bais), getVersion()); + } + sos.writeBUTTONCONDACTIONList(actions); + sos.close(); + } catch (IOException e) { + Logger.getLogger(DefineButton2Tag.class.getName()).log(Level.SEVERE, null, e); + } + return baos.toByteArray(); + } + + /** + * Returns all sub-items + * + * @return List of sub-items + */ + @Override + public List getSubItems() { + List ret = new ArrayList<>(); + ret.addAll(actions); + return ret; + } + + /** + * Returns number of sub-items + * + * @return Number of sub-items + */ + @Override + public int getItemCount() { + return actions.size(); + } + + @Override + public void getNeededCharacters(Set needed) { + for (BUTTONRECORD r : characters) { + needed.add(r.characterId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (int i = 0; i < characters.size(); i++) { + if (characters.get(i).characterId == characterId) { + characters.remove(i); + modified = true; + i--; + } + } + if (modified) { + setModified(true); + } + return modified; + } + + private static final Cache rectCache = Cache.getInstance(true); + + @Override + public RECT getRect() { + if (rectCache.contains(this)) { + return (RECT) rectCache.get(this); + } + RECT rect = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + for (BUTTONRECORD r : characters) { + CharacterTag ch = swf.characters.get(r.characterId); + if (ch instanceof BoundedTag) { + RECT r2 = ((BoundedTag) ch).getRect(); + MATRIX mat = r.placeMatrix; + if (mat != null) { + r2 = mat.apply(r2); + } + rect.Xmin = Math.min(r2.Xmin, rect.Xmin); + rect.Ymin = Math.min(r2.Ymin, rect.Ymin); + rect.Xmax = Math.max(r2.Xmax, rect.Xmax); + rect.Ymax = Math.max(r2.Ymax, rect.Ymax); + } + } + rectCache.put(this, rect); + return rect; + } + + @Override + public boolean trackAsMenu() { + return trackAsMenu; + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + if (!isSingleFrameInitialized) { + initialiteIsSingleFrame(); + } + return isSingleFrame; + } + + private synchronized void initialiteIsSingleFrame() { + if (!isSingleFrameInitialized) { + isSingleFrame = getTimeline().isSingleFrame(); + isSingleFrameInitialized = true; + } + } + + @Override + public Timeline getTimeline() { + if (timeline != null) { + return timeline; + } + timeline = new Timeline(swf, new ArrayList(), buttonId, getRect()); + + int maxDepth = 0; + Frame frameUp = new Frame(timeline); + Frame frameDown = new Frame(timeline); + Frame frameOver = new Frame(timeline); + Frame frameHit = new Frame(timeline); + for (BUTTONRECORD r : this.characters) { + + DepthState layer = new DepthState(swf, null); + layer.colorTransForm = r.colorTransform; + layer.blendMode = r.blendMode; + layer.filters = r.filterList; + layer.matrix = r.placeMatrix; + layer.characterId = r.characterId; + if (r.placeDepth > maxDepth) { + maxDepth = r.placeDepth; + } + + if (r.buttonStateUp) { + frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, false)); + } + if (r.buttonStateDown) { + frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, false)); + } + if (r.buttonStateOver) { + frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, false)); + } + if (r.buttonStateHitTest) { + frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, false)); + } + + } + timeline.frames.add(frameUp); + if (frameOver.layers.isEmpty()) { + frameOver = frameUp; + } + timeline.frames.add(frameOver); + if (frameDown.layers.isEmpty()) { + frameDown = frameOver; + } + timeline.frames.add(frameDown); + if (frameHit.layers.isEmpty()) { + frameHit = frameUp; + } + timeline.frames.add(frameHit); + return timeline; + } + + @Override + public void resetTimeline() { + timeline = null; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 62df54f56..d4767cec9 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -1,383 +1,397 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Defines a button character - * - * @author JPEXS - */ -public class DefineButtonTag extends ButtonTag implements ASMSource { - - /** - * ID for this character - */ - @SWFType(BasicType.UI16) - public int buttonId; - /** - * Characters that make up the button - */ - public List characters; - /** - * Actions to perform - */ - //public List actions; - @Internal - public byte[] actionBytes; - public static final int ID = 7; - - @Override - public int getCharacterId() { - return buttonId; - } - private final long hdrSize; - - private Timeline timeline; - - private boolean isSingleFrameInitialized; - private boolean isSingleFrame; - - @Override - public List getRecords() { - return characters; - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineButtonTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineButton", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - buttonId = sis.readUI16(); - characters = sis.readBUTTONRECORDList(false); - //actions = sis.readActionList(); - hdrSize = sis.getPos(); - actionBytes = sis.readBytesEx(sis.available()); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(buttonId); - sos.writeBUTTONRECORDList(characters, false); - sos.write(actionBytes); - //sos.write(Action.actionsToBytes(actions, true, version)); - sos.close(); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Converts actions to ASM source - * - * @param actions - * @param writer - * @return ASM source - * @throws java.lang.InterruptedException - */ - @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { - if (actions == null) { - actions = getActions(); - } - return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + hdrSize, toString()/*FIXME?*/); - } - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - @Override - public boolean containsSource() { - return true; - } - - /** - * Returns actions associated with this object - * - * @return List of actions - * @throws java.lang.InterruptedException - */ - @Override - public List getActions() throws InterruptedException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int prevLength = 0; - if (previousTag != null) { - byte[] prevData = previousTag.getData(); - baos.write(prevData); - prevLength = prevData.length; - } - baos.write(actionBytes); - MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); - rri.seek(prevLength); - - List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrSize - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); - return list; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); - return new ArrayList<>(); - } - } - - @Override - public void setActions(List actions) { - actionBytes = Action.actionsToBytes(actions, true, swf.version); - } - - @Override - public byte[] getActionBytes() { - return actionBytes; - } - - @Override - public void setActionBytes(byte[] actionBytes) { - this.actionBytes = actionBytes; - } - - @Override - public void setModified() { - setModified(true); - } - - @Override - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { - return Helper.byteArrayToHexWithHeader(writer, actionBytes); - } - - @Override - public Set getNeededCharacters() { - HashSet needed = new HashSet<>(); - for (BUTTONRECORD r : characters) { - needed.add(r.characterId); - } - return needed; - } - private static final Cache rectCache = Cache.getInstance(true); - - @Override - public RECT getRect() { - if (rectCache.contains(this)) { - return (RECT) rectCache.get(this); - } - RECT rect = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - for (BUTTONRECORD r : characters) { - CharacterTag ch = swf.characters.get(r.characterId); - if (ch instanceof BoundedTag) { - RECT r2 = ((BoundedTag) ch).getRect(); - MATRIX mat = r.placeMatrix; - if (mat != null) { - r2 = mat.apply(r2); - } - rect.Xmin = Math.min(r2.Xmin, rect.Xmin); - rect.Ymin = Math.min(r2.Ymin, rect.Ymin); - rect.Xmax = Math.max(r2.Xmax, rect.Xmax); - rect.Ymax = Math.max(r2.Ymax, rect.Ymax); - } - } - - rectCache.put(this, rect); - return rect; - } - List listeners = new ArrayList<>(); - - @Override - public void addDisassemblyListener(DisassemblyListener listener) { - listeners.add(listener); - } - - @Override - public void removeDisassemblyListener(DisassemblyListener listener) { - listeners.remove(listener); - } - - @Override - public boolean trackAsMenu() { - return false; - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - if (!isSingleFrameInitialized) { - initialiteIsSingleFrame(); - } - return isSingleFrame; - } - - private synchronized void initialiteIsSingleFrame() { - if (!isSingleFrameInitialized) { - isSingleFrame = getTimeline().isSingleFrame(); - isSingleFrameInitialized = true; - } - } - - @Override - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { - return writer; - } - - @Override - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { - return writer; - } - - @Override - public int getPrefixLineCount() { - return 0; - } - - @Override - public String removePrefixAndSuffix(String source) { - return source; - } - - @Override - public Timeline getTimeline() { - if (timeline != null) { - return timeline; - } - timeline = new Timeline(swf, new ArrayList(), buttonId, getRect()); - - ColorTransform clrTrans = null; - for (Tag t : swf.tags) { - if (t instanceof DefineButtonCxformTag) { - DefineButtonCxformTag cx = (DefineButtonCxformTag) t; - clrTrans = cx.buttonColorTransform; - } - } - int maxDepth = 0; - Frame frameUp = new Frame(timeline); - Frame frameDown = new Frame(timeline); - Frame frameOver = new Frame(timeline); - Frame frameHit = new Frame(timeline); - for (BUTTONRECORD r : this.characters) { - - DepthState layer = new DepthState(swf, null); - layer.colorTransForm = clrTrans; - layer.blendMode = r.blendMode; - layer.filters = r.filterList; - layer.matrix = r.placeMatrix; - layer.characterId = r.characterId; - if (r.placeDepth > maxDepth) { - maxDepth = r.placeDepth; - } - - if (r.buttonStateUp) { - frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, false)); - } - if (r.buttonStateDown) { - frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, false)); - } - if (r.buttonStateOver) { - frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, false)); - } - if (r.buttonStateHitTest) { - frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, false)); - } - - } - timeline.frames.add(frameUp); - if (frameOver.layers.isEmpty()) { - frameOver = frameUp; - } - timeline.frames.add(frameOver); - if (frameDown.layers.isEmpty()) { - frameDown = frameOver; - } - timeline.frames.add(frameDown); - if (frameHit.layers.isEmpty()) { - frameHit = frameUp; - } - timeline.frames.add(frameHit); - - return timeline; - } - - @Override - public void resetTimeline() { - timeline = null; - } - - @Override - public Tag getSourceTag() { - return this; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.types.BUTTONRECORD; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Defines a button character + * + * @author JPEXS + */ +public class DefineButtonTag extends ButtonTag implements ASMSource { + + /** + * ID for this character + */ + @SWFType(BasicType.UI16) + public int buttonId; + /** + * Characters that make up the button + */ + public List characters; + /** + * Actions to perform + */ + //public List actions; + @Internal + public byte[] actionBytes; + public static final int ID = 7; + + @Override + public int getCharacterId() { + return buttonId; + } + private final long hdrSize; + + private Timeline timeline; + + private boolean isSingleFrameInitialized; + private boolean isSingleFrame; + + @Override + public List getRecords() { + return characters; + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineButtonTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineButton", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + buttonId = sis.readUI16(); + characters = sis.readBUTTONRECORDList(false); + //actions = sis.readActionList(); + hdrSize = sis.getPos(); + actionBytes = sis.readBytesEx(sis.available()); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(buttonId); + sos.writeBUTTONRECORDList(characters, false); + sos.write(actionBytes); + //sos.write(Action.actionsToBytes(actions, true, version)); + sos.close(); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Converts actions to ASM source + * + * @param actions + * @param writer + * @return ASM source + * @throws java.lang.InterruptedException + */ + @Override + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + if (actions == null) { + actions = getActions(); + } + return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + hdrSize, toString()/*FIXME?*/); + } + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + @Override + public boolean containsSource() { + return true; + } + + /** + * Returns actions associated with this object + * + * @return List of actions + * @throws java.lang.InterruptedException + */ + @Override + public List getActions() throws InterruptedException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int prevLength = 0; + if (previousTag != null) { + byte[] prevData = previousTag.getData(); + baos.write(prevData); + prevLength = prevData.length; + } + baos.write(actionBytes); + MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); + rri.seek(prevLength); + + List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrSize - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); + return list; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); + return new ArrayList<>(); + } + } + + @Override + public void setActions(List actions) { + actionBytes = Action.actionsToBytes(actions, true, swf.version); + } + + @Override + public byte[] getActionBytes() { + return actionBytes; + } + + @Override + public void setActionBytes(byte[] actionBytes) { + this.actionBytes = actionBytes; + } + + @Override + public void setModified() { + setModified(true); + } + + @Override + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { + return Helper.byteArrayToHexWithHeader(writer, actionBytes); + } + + @Override + public void getNeededCharacters(Set needed) { + for (BUTTONRECORD r : characters) { + needed.add(r.characterId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (int i = 0; i < characters.size(); i++) { + if (characters.get(i).characterId == characterId) { + characters.remove(i); + modified = true; + i--; + } + } + if (modified) { + setModified(true); + } + return modified; + } + + private static final Cache rectCache = Cache.getInstance(true); + + @Override + public RECT getRect() { + if (rectCache.contains(this)) { + return (RECT) rectCache.get(this); + } + RECT rect = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + for (BUTTONRECORD r : characters) { + CharacterTag ch = swf.characters.get(r.characterId); + if (ch instanceof BoundedTag) { + RECT r2 = ((BoundedTag) ch).getRect(); + MATRIX mat = r.placeMatrix; + if (mat != null) { + r2 = mat.apply(r2); + } + rect.Xmin = Math.min(r2.Xmin, rect.Xmin); + rect.Ymin = Math.min(r2.Ymin, rect.Ymin); + rect.Xmax = Math.max(r2.Xmax, rect.Xmax); + rect.Ymax = Math.max(r2.Ymax, rect.Ymax); + } + } + + rectCache.put(this, rect); + return rect; + } + List listeners = new ArrayList<>(); + + @Override + public void addDisassemblyListener(DisassemblyListener listener) { + listeners.add(listener); + } + + @Override + public void removeDisassemblyListener(DisassemblyListener listener) { + listeners.remove(listener); + } + + @Override + public boolean trackAsMenu() { + return false; + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + if (!isSingleFrameInitialized) { + initialiteIsSingleFrame(); + } + return isSingleFrame; + } + + private synchronized void initialiteIsSingleFrame() { + if (!isSingleFrameInitialized) { + isSingleFrame = getTimeline().isSingleFrame(); + isSingleFrameInitialized = true; + } + } + + @Override + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { + return writer; + } + + @Override + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { + return writer; + } + + @Override + public int getPrefixLineCount() { + return 0; + } + + @Override + public String removePrefixAndSuffix(String source) { + return source; + } + + @Override + public Timeline getTimeline() { + if (timeline != null) { + return timeline; + } + timeline = new Timeline(swf, new ArrayList(), buttonId, getRect()); + + ColorTransform clrTrans = null; + for (Tag t : swf.tags) { + if (t instanceof DefineButtonCxformTag) { + DefineButtonCxformTag cx = (DefineButtonCxformTag) t; + clrTrans = cx.buttonColorTransform; + } + } + int maxDepth = 0; + Frame frameUp = new Frame(timeline); + Frame frameDown = new Frame(timeline); + Frame frameOver = new Frame(timeline); + Frame frameHit = new Frame(timeline); + for (BUTTONRECORD r : this.characters) { + + DepthState layer = new DepthState(swf, null); + layer.colorTransForm = clrTrans; + layer.blendMode = r.blendMode; + layer.filters = r.filterList; + layer.matrix = r.placeMatrix; + layer.characterId = r.characterId; + if (r.placeDepth > maxDepth) { + maxDepth = r.placeDepth; + } + + if (r.buttonStateUp) { + frameUp.layers.put(r.placeDepth, new DepthState(layer, frameUp, false)); + } + if (r.buttonStateDown) { + frameDown.layers.put(r.placeDepth, new DepthState(layer, frameDown, false)); + } + if (r.buttonStateOver) { + frameOver.layers.put(r.placeDepth, new DepthState(layer, frameOver, false)); + } + if (r.buttonStateHitTest) { + frameHit.layers.put(r.placeDepth, new DepthState(layer, frameHit, false)); + } + + } + timeline.frames.add(frameUp); + if (frameOver.layers.isEmpty()) { + frameOver = frameUp; + } + timeline.frames.add(frameOver); + if (frameDown.layers.isEmpty()) { + frameDown = frameOver; + } + timeline.frames.add(frameDown); + if (frameHit.layers.isEmpty()) { + frameHit = frameUp; + } + timeline.frames.add(frameHit); + + return timeline; + } + + @Override + public void resetTimeline() { + timeline = null; + } + + @Override + public Tag getSourceTag() { + return this; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index 39a7cacf0..8e582f869 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -1,980 +1,988 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle; -import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel; -import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph; -import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord; -import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle; -import com.jpexs.decompiler.flash.tags.dynamictext.Word; -import com.jpexs.decompiler.flash.tags.text.ParseException; -import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; -import com.jpexs.decompiler.flash.tags.text.TextLexer; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -/** - * - * - * @author JPEXS - */ -public class DefineEditTextTag extends TextTag { - - @SWFType(BasicType.UI16) - public int characterID; - - public RECT bounds; - public boolean hasText; - public boolean wordWrap; - public boolean multiline; - public boolean password; - public boolean readOnly; - public boolean hasTextColor; - public boolean hasMaxLength; - public boolean hasFont; - public boolean hasFontClass; - public boolean autoSize; - public boolean hasLayout; - public boolean noSelect; - public boolean border; - public boolean wasStatic; - public boolean html; - public boolean useOutlines; - - @SWFType(BasicType.UI16) - @Conditional("hasFont") - public int fontId; - - @Conditional("hasFontClass") - public String fontClass; - - @SWFType(BasicType.UI16) - @Conditional("hasFont") - public int fontHeight; - - @Conditional("hasTextColor") - public RGBA textColor; - - @SWFType(BasicType.UI16) - @Conditional("hasMaxLength") - public int maxLength; - - @SWFType(BasicType.UI8) - @Conditional("hasLayout") - public int align; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int leftMargin; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int rightMargin; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int indent; - - @SWFType(BasicType.SI16) - @Conditional("hasLayout") - public int leading; - - public String variableName; - - @Conditional("hasText") - public String initialText; - - public static final int ID = 37; - - @Override - public RECT getBounds() { - return bounds; - } - - @Override - public MATRIX getTextMatrix() { - MATRIX matrix = new MATRIX(); - matrix.translateX = bounds.Xmin; - matrix.translateY = bounds.Ymin; - return matrix; - } - - @Override - public void setBounds(RECT r) { - bounds = r; - } - - private String stripTags(String inp) { - boolean intag = false; - String outp = ""; - inp = inp.replaceAll("
", "\r\n"); - for (int i = 0; i < inp.length(); ++i) { - if (!intag && inp.charAt(i) == '<') { - intag = true; - continue; - } - if (intag && inp.charAt(i) == '>') { - intag = false; - continue; - } - if (!intag) { - outp += inp.charAt(i); - } - } - return outp; - } - - private String entitiesReplace(String s) { - s = s.replace("<", "<"); - s = s.replace(">", ">"); - s = s.replace("&", "&"); - s = s.replace(""", "\""); - return s; - } - - @Override - public String getText(String separator) { - String ret = ""; - if (hasText) { - ret = initialText; - } - if (html) { - ret = stripTags(ret); - ret = entitiesReplace(ret); - } - return ret; - } - - private List getTextWithStyle() { - String str = ""; - TextStyle style = new TextStyle(); - style.font = getFontTag(); - style.fontHeight = fontHeight; - style.fontLeading = leading; - if (hasTextColor) { - style.textColor = textColor; - } - if (hasText) { - str = initialText; - } - final List ret = new ArrayList<>(); - if (html) { - SAXParserFactory factory = SAXParserFactory.newInstance(); - SAXParser saxParser; - final Stack styles = new Stack<>(); - styles.add(style); - try { - saxParser = factory.newSAXParser(); - DefaultHandler handler = new DefaultHandler() { - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - TextStyle style = styles.peek(); - switch (qName) { - case "p": - // todo: parse the following attribute: - // align - break; - case "b": - style = style.clone(); - style.bold = true; - styles.add(style); - break; - case "i": - style = style.clone(); - style.italic = true; - styles.add(style); - break; - case "u": - style = style.clone(); - style.underlined = true; - styles.add(style); - break; - case "font": - style = style.clone(); - String color = attributes.getValue("color"); - if (color != null) { - if (color.startsWith("#")) { - style.textColor = new RGBA(Color.decode(color)); - } - } - String size = attributes.getValue("size"); - if (size != null && size.length() > 0) { - char firstChar = size.charAt(0); - if (firstChar != '+' && firstChar != '-') { - int fontSize = Integer.parseInt(size); - style.fontHeight = fontSize * style.font.getDivider(); - style.fontLeading = leading; - } else { - // todo: parse relative sizes - } - } - // todo: parse the following attributes: - // face, letterSpacing, kerning - styles.add(style); - break; - case "br": - case "sbr": // what's this? - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = '\n'; - cs.style = style; - ret.add(cs); - break; - } - //ret = entitiesReplace(ret); - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - switch (qName) { - case "b": - case "i": - case "u": - case "font": - styles.pop(); - break; - case "p": - TextStyle style = styles.peek(); - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = '\n'; - cs.style = style; - ret.add(cs); - break; - } - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - String txt = new String(ch, start, length); - TextStyle style = styles.peek(); - addCharacters(ret, txt, style); - } - }; - str = " \n" - + "]>" + str + ""; - saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler); - } catch (ParserConfigurationException | SAXException | IOException ex) { - Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); - } - } else { - addCharacters(ret, str, style); - } - return ret; - } - - private void addCharacters(List list, String str, TextStyle style) { - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = ch; - cs.style = style; - list.add(cs); - } - } - - @Override - public List getFontIds() { - List ret = new ArrayList<>(); - ret.add(fontId); - return ret; - } - - @Override - public String getFormattedText() { - String ret = ""; - ret += "["; - String[] alignValues = {"left", "right", "center", "justify"}; - ret += "\r\nxmin " + bounds.Xmin + "\r\nymin " + bounds.Ymin + "\r\nxmax " + bounds.Xmax + "\r\nymax " + bounds.Ymax + "\r\n"; - ret += (wordWrap ? "wordwrap 1\r\n" : "") + (multiline ? "multiline 1\r\n" : "") - + (password ? "password 1\r\n" : "") + (readOnly ? "readonly 1\r\n" : "") - + (autoSize ? "autosize 1\r\n" : "") + (noSelect ? "noselect 1\r\n" : "") - + (border ? "border 1\r\n" : "") + (wasStatic ? "wasstatic 1\r\n" : "") - + (html ? "html 1\r\n" : "") + (useOutlines ? "useoutlines 1\r\n" : "") - + (hasFont ? "font " + fontId + "\r\n" + "height " + fontHeight + "\r\n" : "") + (hasTextColor ? "color " + textColor.toHexARGB() + "\r\n" : "") - + (hasFontClass ? "fontclass " + fontClass + "\r\n" : "") + (hasMaxLength ? "maxlength " + maxLength + "\r\n" : "") - + "align " + alignValues[align] + "\r\n" - + (hasLayout ? "leftmargin " + leftMargin + "\r\nrightmargin " + rightMargin + "\r\nindent " + indent + "\r\nleading " + leading + "\r\n" : "") - + (!variableName.isEmpty() ? "variablename " + variableName + "\r\n" : ""); - ret += "]"; - if (hasText) { - ret += initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]"); - } - return ret; - } - - @Override - public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { - try { - TextLexer lexer = new TextLexer(new StringReader(formattedText)); - ParsedSymbol s = null; - formattedText = ""; - RECT bounds = new RECT(this.bounds); - boolean wordWrap = false; - boolean multiline = false; - boolean password = false; - boolean readOnly = false; - boolean autoSize = false; - boolean noSelect = false; - boolean border = false; - boolean wasStatic = false; - boolean html = false; - boolean useOutlines = false; - int fontId = -1; - int fontHeight = -1; - String fontClass = null; - RGBA textColor = null; - int maxLength = -1; - int align = -1; - int leftMargin = -1; - int rightMargin = -1; - int indent = -1; - int leading = -1; - String variableName = null; - - int textIdx = 0; - while ((s = lexer.yylex()) != null) { - switch (s.type) { - case PARAMETER: - String paramName = (String) s.values[0]; - String paramValue = (String) s.values[1]; - switch (paramName) { - case "xmin": - try { - bounds.Xmin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmin value. Number expected.", lexer.yyline()); - } - break; - case "ymin": - try { - bounds.Ymin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymin value. Number expected.", lexer.yyline()); - } - break; - case "xmax": - try { - bounds.Xmax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmax value. Number expected.", lexer.yyline()); - } - break; - case "ymax": - try { - bounds.Ymax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymax value. Number expected.", lexer.yyline()); - } - break; - case "wordwrap": - if (paramValue.equals("1")) { - wordWrap = true; - } - break; - case "multiline": - if (paramValue.equals("1")) { - multiline = true; - } - break; - case "password": - if (paramValue.equals("1")) { - password = true; - } - break; - case "readonly": - if (paramValue.equals("1")) { - readOnly = true; - } - break; - case "autosize": - if (paramValue.equals("1")) { - autoSize = true; - } - break; - case "noselect": - if (paramValue.equals("1")) { - noSelect = true; - } - break; - case "border": - if (paramValue.equals("1")) { - border = true; - } - break; - case "wasstatic": - if (paramValue.equals("1")) { - wasStatic = true; - } - break; - case "html": - if (paramValue.equals("1")) { - html = true; - } - break; - case "useoutlines": - if (paramValue.equals("1")) { - useOutlines = true; - } - break; - case "font": - try { - fontId = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid font value. Number expected.", lexer.yyline()); - } - break; - case "fontclass": - fontClass = paramValue; - break; - case "height": - try { - fontHeight = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid height value. Number expected.", lexer.yyline()); - } - break; - case "color": - Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); - if (m.matches()) { - textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); - } else { - throw new ParseException("Invalid color. Valid format is #aarrggbb.", lexer.yyline()); - } - break; - case "maxlength": - try { - maxLength = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid maxLength value. Number expected.", lexer.yyline()); - } - break; - case "align": - switch (paramValue) { - case "left": - align = 0; - break; - case "right": - align = 1; - break; - case "center": - align = 2; - break; - case "justify": - align = 3; - break; - default: - throw new ParseException("Invalid align value. Expected one of: left,right,center or justify.", lexer.yyline()); - } - break; - case "leftmargin": - try { - leftMargin = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid leftmargin value. Number expected.", lexer.yyline()); - } - break; - case "rightmargin": - try { - rightMargin = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid rightmargin value. Number expected.", lexer.yyline()); - } - break; - case "indent": - try { - indent = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid indent value. Number expected.", lexer.yyline()); - } - break; - case "leading": - try { - leading = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new ParseException("Invalid leading value. Number expected.", lexer.yyline()); - } - break; - case "variablename": - variableName = paramValue; - break; - default: - throw new ParseException("Unrecognized parameter name", lexer.yyline()); - } - break; - case TEXT: - formattedText += (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; - break; - } - } - - this.bounds = bounds; - if (formattedText.length() > 0) { - initialText = formattedText; - this.hasText = true; - } else { - this.hasText = false; - } - this.wordWrap = wordWrap; - this.multiline = multiline; - this.password = password; - this.readOnly = readOnly; - this.noSelect = noSelect; - this.border = border; - this.wasStatic = wasStatic; - this.html = html; - this.useOutlines = useOutlines; - if (textColor != null) { - hasTextColor = true; - this.textColor = textColor; - } - if (maxLength > -1) { - this.maxLength = maxLength; - hasMaxLength = true; - } - if (fontId > -1) { - this.fontId = fontId; - } - if (fontHeight > -1) { - this.fontHeight = fontHeight; - } - if (fontClass != null) { - this.fontClass = fontClass; - hasFontClass = true; - } - this.autoSize = autoSize; - if ((leftMargin > -1) - || (rightMargin > -1) - || (indent > -1) - || (leading > -1)) { - this.leftMargin = leftMargin; - this.rightMargin = rightMargin; - this.indent = indent; - this.leading = leading; - hasLayout = true; - } - if (variableName == null) { - variableName = ""; - } - this.variableName = variableName; - - } catch (IOException ex) { - Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); - return false; - } - return true; - - } - - @Override - public RECT getRect() { - return bounds; - } - - @Override - public int getCharacterId() { - return characterID; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeRECT(bounds); - sos.writeUB(1, hasText ? 1 : 0); - sos.writeUB(1, wordWrap ? 1 : 0); - sos.writeUB(1, multiline ? 1 : 0); - sos.writeUB(1, password ? 1 : 0); - sos.writeUB(1, readOnly ? 1 : 0); - sos.writeUB(1, hasTextColor ? 1 : 0); - sos.writeUB(1, hasMaxLength ? 1 : 0); - sos.writeUB(1, hasFont ? 1 : 0); - sos.writeUB(1, hasFontClass ? 1 : 0); - sos.writeUB(1, autoSize ? 1 : 0); - sos.writeUB(1, hasLayout ? 1 : 0); - sos.writeUB(1, noSelect ? 1 : 0); - sos.writeUB(1, border ? 1 : 0); - sos.writeUB(1, wasStatic ? 1 : 0); - sos.writeUB(1, html ? 1 : 0); - sos.writeUB(1, useOutlines ? 1 : 0); - if (hasFont) { - sos.writeUI16(fontId); - } - if (hasFontClass) { - sos.writeString(fontClass); - } - if (hasFont) { - sos.writeUI16(fontHeight); - } - if (hasTextColor) { - sos.writeRGBA(textColor); - } - if (hasMaxLength) { - sos.writeUI16(maxLength); - } - if (hasLayout) { - sos.writeUI8(align); - sos.writeUI16(leftMargin); - sos.writeUI16(rightMargin); - sos.writeUI16(indent); - sos.writeSI16(leading); - } - sos.writeString(variableName); - if (hasText) { - sos.writeString(initialText); - } - - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineEditTextTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineEditText", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterID = sis.readUI16(); - bounds = sis.readRECT(); - hasText = sis.readUB(1) == 1; - wordWrap = sis.readUB(1) == 1; - multiline = sis.readUB(1) == 1; - password = sis.readUB(1) == 1; - readOnly = sis.readUB(1) == 1; - hasTextColor = sis.readUB(1) == 1; - hasMaxLength = sis.readUB(1) == 1; - hasFont = sis.readUB(1) == 1; - hasFontClass = sis.readUB(1) == 1; - autoSize = sis.readUB(1) == 1; - hasLayout = sis.readUB(1) == 1; - noSelect = sis.readUB(1) == 1; - border = sis.readUB(1) == 1; - wasStatic = sis.readUB(1) == 1; - html = sis.readUB(1) == 1; - useOutlines = sis.readUB(1) == 1; - if (hasFont) { - fontId = sis.readUI16(); - } - if (hasFontClass) { - fontClass = sis.readString(); - } - if (hasFont) { - fontHeight = sis.readUI16(); - } - if (hasTextColor) { - textColor = sis.readRGBA(); - } - if (hasMaxLength) { - maxLength = sis.readUI16(); - } - if (hasLayout) { - align = sis.readUI8(); //0 left,1 right, 2 center, 3 justify - leftMargin = sis.readUI16(); - rightMargin = sis.readUI16(); - indent = sis.readUI16(); - leading = sis.readSI16(); - } - variableName = sis.readString(); - if (hasText) { - initialText = sis.readString(); - } - - } - - @Override - public Set getNeededCharacters() { - HashSet needed = new HashSet<>(); - if (hasFont) { - needed.add(fontId); - } - return needed; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - render(false, image, transformation, colorTransform); - } - - private String render(boolean canvas, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - if (image == null) { - return ""; //TODO: handle Html Canvas conversion - } - if (border) { - // border is always black, fill color is always white? - RGB borderColor = new RGBA(Color.black); - RGB fillColor = new RGBA(Color.white); - drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform); - } - if (hasText) { - DynamicTextModel textModel = new DynamicTextModel(); - List txt = getTextWithStyle(); - TextStyle lastStyle = null; - char prevChar = 0; - boolean lastWasWhiteSpace = false; - for (int i = 0; i < txt.size(); i++) { - CharacterWithStyle cs = txt.get(i); - char c = cs.character; - if (c != '\r' && c != '\n') { - // create new SameStyleTextRecord for all words and all diffrent style text parts - if (lastWasWhiteSpace && !Character.isWhitespace(c)) { - textModel.newWord(); - lastWasWhiteSpace = false; - } - if (cs.style != lastStyle) { - lastStyle = cs.style; - textModel.style = lastStyle; - textModel.newRecord(); - } - Character nextChar = null; - if (i + 1 < txt.size()) { - nextChar = txt.get(i + 1).character; - } - int advance; - FontTag font = lastStyle.font; - GLYPHENTRY ge = new GLYPHENTRY(); - ge.glyphIndex = font.charToGlyph(c); - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextChar != null) { - kerningAdjustment = font.getGlyphKerningAdjustment(ge.glyphIndex, font.charToGlyph(nextChar)); - kerningAdjustment /= font.getDivider(); - } - advance = (int) Math.round(font.getDivider() * Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); - } else { - String fontName = FontTag.defaultFontName; - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar)); - } - ge.glyphAdvance = advance; - textModel.addGlyph(c, ge); - if (Character.isWhitespace(c)) { - lastWasWhiteSpace = true; - } - } else { - if (c == '\r' || prevChar != '\r') { - if (multiline) { - textModel.newParagraph(); - } - } - } - prevChar = c; - } - - textModel.calculateTexWidths(); - List> lines; - if (multiline && wordWrap) { - lines = new ArrayList<>(); - int lineLength = 0; - for (Paragraph paragraph : textModel.paragraphs) { - List line = new ArrayList<>(); - for (Word word : paragraph.words) { - if (lineLength + word.width <= bounds.getWidth()) { - line.addAll(word.records); - lineLength += word.width; - } else { - lines.add(line); - line = new ArrayList<>(); - line.addAll(word.records); - lineLength = 0; - } - } - if (!line.isEmpty()) { - lines.add(line); - } - } - } else { - lines = new ArrayList<>(); - for (Paragraph paragraph : textModel.paragraphs) { - List line = new ArrayList<>(); - for (Word word : paragraph.words) { - for (SameStyleTextRecord tr : word.records) { - line.add(tr); - } - } - lines.add(line); - } - } - - // remove spaces after last word - for (List line : lines) { - boolean removed = true; - while (removed) { - removed = false; - while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) { - line.remove(line.size() - 1); - removed = true; - } - if (line.size() > 0) { - SameStyleTextRecord lastRecord = line.get(line.size() - 1); - while (lastRecord.glyphEntries.size() > 0 - && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) { - lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1); - removed = true; - } - } - } - } - - textModel.calculateTexWidths(); - - List allTextRecords = new ArrayList<>(); - int yOffset = 0; - for (List line : lines) { - int width = 0; - int currentOffset = 0; - for (SameStyleTextRecord tr : line) { - width += tr.width; - if (tr.style.fontHeight + tr.style.fontLeading > currentOffset) { - currentOffset = tr.style.fontHeight + tr.style.fontLeading; - } - } - yOffset += currentOffset; - int alignOffset = 0; - switch (align) { - case 0: // left - alignOffset = 0; - break; - case 1: // right - alignOffset = bounds.getWidth() - width; - break; - case 2: // center - alignOffset = (bounds.getWidth() - width) / 2; - break; - case 3: // justify - // todo; - break; - } - for (SameStyleTextRecord tr : line) { - tr.xOffset = alignOffset; - alignOffset += tr.width; - } - for (SameStyleTextRecord tr : line) { - TEXTRECORD tr2 = new TEXTRECORD(); - tr2.styleFlagsHasFont = true; - tr2.fontId = fontId; - tr2.textHeight = tr.style.fontHeight; - if (tr.style.textColor != null) { - tr2.styleFlagsHasColor = true; - tr2.textColorA = tr.style.textColor; - } - // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering - tr2.styleFlagsHasXOffset = true; - tr2.xOffset = tr.xOffset; - if (yOffset != 0) { - tr2.styleFlagsHasYOffset = true; - tr2.yOffset = yOffset; - } - tr2.glyphEntries = new GLYPHENTRY[tr.glyphEntries.size()]; - for (int i = 0; i < tr2.glyphEntries.length; i++) { - tr2.glyphEntries[i] = tr.glyphEntries.get(i).glyphEntry; - } - allTextRecords.add(tr2); - } - } - - staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform); - } - return ""; //TODO: Return HTML Canvas converted - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - private FontTag getFontTag() { - FontTag font = null; - for (Tag tag : swf.tags) { - if (tag instanceof FontTag) { - if (((FontTag) tag).getFontId() == fontId) { - font = (FontTag) tag; - } - } - } - return font; - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - return render(true, null, new Matrix(), new ColorTransform()); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle; +import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel; +import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph; +import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord; +import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle; +import com.jpexs.decompiler.flash.tags.dynamictext.Word; +import com.jpexs.decompiler.flash.tags.text.ParseException; +import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; +import com.jpexs.decompiler.flash.tags.text.TextLexer; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Color; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * + * + * @author JPEXS + */ +public class DefineEditTextTag extends TextTag { + + @SWFType(BasicType.UI16) + public int characterID; + + public RECT bounds; + public boolean hasText; + public boolean wordWrap; + public boolean multiline; + public boolean password; + public boolean readOnly; + public boolean hasTextColor; + public boolean hasMaxLength; + public boolean hasFont; + public boolean hasFontClass; + public boolean autoSize; + public boolean hasLayout; + public boolean noSelect; + public boolean border; + public boolean wasStatic; + public boolean html; + public boolean useOutlines; + + @SWFType(BasicType.UI16) + @Conditional("hasFont") + public int fontId; + + @Conditional("hasFontClass") + public String fontClass; + + @SWFType(BasicType.UI16) + @Conditional("hasFont") + public int fontHeight; + + @Conditional("hasTextColor") + public RGBA textColor; + + @SWFType(BasicType.UI16) + @Conditional("hasMaxLength") + public int maxLength; + + @SWFType(BasicType.UI8) + @Conditional("hasLayout") + public int align; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int leftMargin; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int rightMargin; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int indent; + + @SWFType(BasicType.SI16) + @Conditional("hasLayout") + public int leading; + + public String variableName; + + @Conditional("hasText") + public String initialText; + + public static final int ID = 37; + + @Override + public RECT getBounds() { + return bounds; + } + + @Override + public MATRIX getTextMatrix() { + MATRIX matrix = new MATRIX(); + matrix.translateX = bounds.Xmin; + matrix.translateY = bounds.Ymin; + return matrix; + } + + @Override + public void setBounds(RECT r) { + bounds = r; + } + + private String stripTags(String inp) { + boolean intag = false; + String outp = ""; + inp = inp.replaceAll("
", "\r\n"); + for (int i = 0; i < inp.length(); ++i) { + if (!intag && inp.charAt(i) == '<') { + intag = true; + continue; + } + if (intag && inp.charAt(i) == '>') { + intag = false; + continue; + } + if (!intag) { + outp += inp.charAt(i); + } + } + return outp; + } + + private String entitiesReplace(String s) { + s = s.replace("<", "<"); + s = s.replace(">", ">"); + s = s.replace("&", "&"); + s = s.replace(""", "\""); + return s; + } + + @Override + public String getText(String separator) { + String ret = ""; + if (hasText) { + ret = initialText; + } + if (html) { + ret = stripTags(ret); + ret = entitiesReplace(ret); + } + return ret; + } + + private List getTextWithStyle() { + String str = ""; + TextStyle style = new TextStyle(); + style.font = getFontTag(); + style.fontHeight = fontHeight; + style.fontLeading = leading; + if (hasTextColor) { + style.textColor = textColor; + } + if (hasText) { + str = initialText; + } + final List ret = new ArrayList<>(); + if (html) { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser; + final Stack styles = new Stack<>(); + styles.add(style); + try { + saxParser = factory.newSAXParser(); + DefaultHandler handler = new DefaultHandler() { + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + TextStyle style = styles.peek(); + switch (qName) { + case "p": + // todo: parse the following attribute: + // align + break; + case "b": + style = style.clone(); + style.bold = true; + styles.add(style); + break; + case "i": + style = style.clone(); + style.italic = true; + styles.add(style); + break; + case "u": + style = style.clone(); + style.underlined = true; + styles.add(style); + break; + case "font": + style = style.clone(); + String color = attributes.getValue("color"); + if (color != null) { + if (color.startsWith("#")) { + style.textColor = new RGBA(Color.decode(color)); + } + } + String size = attributes.getValue("size"); + if (size != null && size.length() > 0) { + char firstChar = size.charAt(0); + if (firstChar != '+' && firstChar != '-') { + int fontSize = Integer.parseInt(size); + style.fontHeight = fontSize * style.font.getDivider(); + style.fontLeading = leading; + } else { + // todo: parse relative sizes + } + } + // todo: parse the following attributes: + // face, letterSpacing, kerning + styles.add(style); + break; + case "br": + case "sbr": // what's this? + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = '\n'; + cs.style = style; + ret.add(cs); + break; + } + //ret = entitiesReplace(ret); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + switch (qName) { + case "b": + case "i": + case "u": + case "font": + styles.pop(); + break; + case "p": + TextStyle style = styles.peek(); + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = '\n'; + cs.style = style; + ret.add(cs); + break; + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + String txt = new String(ch, start, length); + TextStyle style = styles.peek(); + addCharacters(ret, txt, style); + } + }; + str = " \n" + + "]>" + str + ""; + saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler); + } catch (ParserConfigurationException | SAXException | IOException ex) { + Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); + } + } else { + addCharacters(ret, str, style); + } + return ret; + } + + private void addCharacters(List list, String str, TextStyle style) { + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = ch; + cs.style = style; + list.add(cs); + } + } + + @Override + public List getFontIds() { + List ret = new ArrayList<>(); + ret.add(fontId); + return ret; + } + + @Override + public String getFormattedText() { + String ret = ""; + ret += "["; + String[] alignValues = {"left", "right", "center", "justify"}; + ret += "\r\nxmin " + bounds.Xmin + "\r\nymin " + bounds.Ymin + "\r\nxmax " + bounds.Xmax + "\r\nymax " + bounds.Ymax + "\r\n"; + ret += (wordWrap ? "wordwrap 1\r\n" : "") + (multiline ? "multiline 1\r\n" : "") + + (password ? "password 1\r\n" : "") + (readOnly ? "readonly 1\r\n" : "") + + (autoSize ? "autosize 1\r\n" : "") + (noSelect ? "noselect 1\r\n" : "") + + (border ? "border 1\r\n" : "") + (wasStatic ? "wasstatic 1\r\n" : "") + + (html ? "html 1\r\n" : "") + (useOutlines ? "useoutlines 1\r\n" : "") + + (hasFont ? "font " + fontId + "\r\n" + "height " + fontHeight + "\r\n" : "") + (hasTextColor ? "color " + textColor.toHexARGB() + "\r\n" : "") + + (hasFontClass ? "fontclass " + fontClass + "\r\n" : "") + (hasMaxLength ? "maxlength " + maxLength + "\r\n" : "") + + "align " + alignValues[align] + "\r\n" + + (hasLayout ? "leftmargin " + leftMargin + "\r\nrightmargin " + rightMargin + "\r\nindent " + indent + "\r\nleading " + leading + "\r\n" : "") + + (!variableName.isEmpty() ? "variablename " + variableName + "\r\n" : ""); + ret += "]"; + if (hasText) { + ret += initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]"); + } + return ret; + } + + @Override + public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { + try { + TextLexer lexer = new TextLexer(new StringReader(formattedText)); + ParsedSymbol s = null; + formattedText = ""; + RECT bounds = new RECT(this.bounds); + boolean wordWrap = false; + boolean multiline = false; + boolean password = false; + boolean readOnly = false; + boolean autoSize = false; + boolean noSelect = false; + boolean border = false; + boolean wasStatic = false; + boolean html = false; + boolean useOutlines = false; + int fontId = -1; + int fontHeight = -1; + String fontClass = null; + RGBA textColor = null; + int maxLength = -1; + int align = -1; + int leftMargin = -1; + int rightMargin = -1; + int indent = -1; + int leading = -1; + String variableName = null; + + int textIdx = 0; + while ((s = lexer.yylex()) != null) { + switch (s.type) { + case PARAMETER: + String paramName = (String) s.values[0]; + String paramValue = (String) s.values[1]; + switch (paramName) { + case "xmin": + try { + bounds.Xmin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmin value. Number expected.", lexer.yyline()); + } + break; + case "ymin": + try { + bounds.Ymin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymin value. Number expected.", lexer.yyline()); + } + break; + case "xmax": + try { + bounds.Xmax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmax value. Number expected.", lexer.yyline()); + } + break; + case "ymax": + try { + bounds.Ymax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymax value. Number expected.", lexer.yyline()); + } + break; + case "wordwrap": + if (paramValue.equals("1")) { + wordWrap = true; + } + break; + case "multiline": + if (paramValue.equals("1")) { + multiline = true; + } + break; + case "password": + if (paramValue.equals("1")) { + password = true; + } + break; + case "readonly": + if (paramValue.equals("1")) { + readOnly = true; + } + break; + case "autosize": + if (paramValue.equals("1")) { + autoSize = true; + } + break; + case "noselect": + if (paramValue.equals("1")) { + noSelect = true; + } + break; + case "border": + if (paramValue.equals("1")) { + border = true; + } + break; + case "wasstatic": + if (paramValue.equals("1")) { + wasStatic = true; + } + break; + case "html": + if (paramValue.equals("1")) { + html = true; + } + break; + case "useoutlines": + if (paramValue.equals("1")) { + useOutlines = true; + } + break; + case "font": + try { + fontId = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid font value. Number expected.", lexer.yyline()); + } + break; + case "fontclass": + fontClass = paramValue; + break; + case "height": + try { + fontHeight = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid height value. Number expected.", lexer.yyline()); + } + break; + case "color": + Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); + if (m.matches()) { + textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); + } else { + throw new ParseException("Invalid color. Valid format is #aarrggbb.", lexer.yyline()); + } + break; + case "maxlength": + try { + maxLength = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid maxLength value. Number expected.", lexer.yyline()); + } + break; + case "align": + switch (paramValue) { + case "left": + align = 0; + break; + case "right": + align = 1; + break; + case "center": + align = 2; + break; + case "justify": + align = 3; + break; + default: + throw new ParseException("Invalid align value. Expected one of: left,right,center or justify.", lexer.yyline()); + } + break; + case "leftmargin": + try { + leftMargin = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid leftmargin value. Number expected.", lexer.yyline()); + } + break; + case "rightmargin": + try { + rightMargin = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid rightmargin value. Number expected.", lexer.yyline()); + } + break; + case "indent": + try { + indent = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid indent value. Number expected.", lexer.yyline()); + } + break; + case "leading": + try { + leading = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new ParseException("Invalid leading value. Number expected.", lexer.yyline()); + } + break; + case "variablename": + variableName = paramValue; + break; + default: + throw new ParseException("Unrecognized parameter name", lexer.yyline()); + } + break; + case TEXT: + formattedText += (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; + break; + } + } + + this.bounds = bounds; + if (formattedText.length() > 0) { + initialText = formattedText; + this.hasText = true; + } else { + this.hasText = false; + } + this.wordWrap = wordWrap; + this.multiline = multiline; + this.password = password; + this.readOnly = readOnly; + this.noSelect = noSelect; + this.border = border; + this.wasStatic = wasStatic; + this.html = html; + this.useOutlines = useOutlines; + if (textColor != null) { + hasTextColor = true; + this.textColor = textColor; + } + if (maxLength > -1) { + this.maxLength = maxLength; + hasMaxLength = true; + } + if (fontId > -1) { + this.fontId = fontId; + } + if (fontHeight > -1) { + this.fontHeight = fontHeight; + } + if (fontClass != null) { + this.fontClass = fontClass; + hasFontClass = true; + } + this.autoSize = autoSize; + if ((leftMargin > -1) + || (rightMargin > -1) + || (indent > -1) + || (leading > -1)) { + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + this.indent = indent; + this.leading = leading; + hasLayout = true; + } + if (variableName == null) { + variableName = ""; + } + this.variableName = variableName; + + } catch (IOException ex) { + Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); + return false; + } + return true; + + } + + @Override + public RECT getRect() { + return bounds; + } + + @Override + public int getCharacterId() { + return characterID; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeRECT(bounds); + sos.writeUB(1, hasText ? 1 : 0); + sos.writeUB(1, wordWrap ? 1 : 0); + sos.writeUB(1, multiline ? 1 : 0); + sos.writeUB(1, password ? 1 : 0); + sos.writeUB(1, readOnly ? 1 : 0); + sos.writeUB(1, hasTextColor ? 1 : 0); + sos.writeUB(1, hasMaxLength ? 1 : 0); + sos.writeUB(1, hasFont ? 1 : 0); + sos.writeUB(1, hasFontClass ? 1 : 0); + sos.writeUB(1, autoSize ? 1 : 0); + sos.writeUB(1, hasLayout ? 1 : 0); + sos.writeUB(1, noSelect ? 1 : 0); + sos.writeUB(1, border ? 1 : 0); + sos.writeUB(1, wasStatic ? 1 : 0); + sos.writeUB(1, html ? 1 : 0); + sos.writeUB(1, useOutlines ? 1 : 0); + if (hasFont) { + sos.writeUI16(fontId); + } + if (hasFontClass) { + sos.writeString(fontClass); + } + if (hasFont) { + sos.writeUI16(fontHeight); + } + if (hasTextColor) { + sos.writeRGBA(textColor); + } + if (hasMaxLength) { + sos.writeUI16(maxLength); + } + if (hasLayout) { + sos.writeUI8(align); + sos.writeUI16(leftMargin); + sos.writeUI16(rightMargin); + sos.writeUI16(indent); + sos.writeSI16(leading); + } + sos.writeString(variableName); + if (hasText) { + sos.writeString(initialText); + } + + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineEditTextTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineEditText", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterID = sis.readUI16(); + bounds = sis.readRECT(); + hasText = sis.readUB(1) == 1; + wordWrap = sis.readUB(1) == 1; + multiline = sis.readUB(1) == 1; + password = sis.readUB(1) == 1; + readOnly = sis.readUB(1) == 1; + hasTextColor = sis.readUB(1) == 1; + hasMaxLength = sis.readUB(1) == 1; + hasFont = sis.readUB(1) == 1; + hasFontClass = sis.readUB(1) == 1; + autoSize = sis.readUB(1) == 1; + hasLayout = sis.readUB(1) == 1; + noSelect = sis.readUB(1) == 1; + border = sis.readUB(1) == 1; + wasStatic = sis.readUB(1) == 1; + html = sis.readUB(1) == 1; + useOutlines = sis.readUB(1) == 1; + if (hasFont) { + fontId = sis.readUI16(); + } + if (hasFontClass) { + fontClass = sis.readString(); + } + if (hasFont) { + fontHeight = sis.readUI16(); + } + if (hasTextColor) { + textColor = sis.readRGBA(); + } + if (hasMaxLength) { + maxLength = sis.readUI16(); + } + if (hasLayout) { + align = sis.readUI8(); //0 left,1 right, 2 center, 3 justify + leftMargin = sis.readUI16(); + rightMargin = sis.readUI16(); + indent = sis.readUI16(); + leading = sis.readSI16(); + } + variableName = sis.readString(); + if (hasText) { + initialText = sis.readString(); + } + + } + + @Override + public void getNeededCharacters(Set needed) { + if (hasFont) { + needed.add(fontId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + if (fontId == characterId) { + hasFont = false; + fontId = 0; + setModified(true); + return true; + } + return false; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + render(false, image, transformation, colorTransform); + } + + private String render(boolean canvas, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + if (image == null) { + return ""; //TODO: handle Html Canvas conversion + } + if (border) { + // border is always black, fill color is always white? + RGB borderColor = new RGBA(Color.black); + RGB fillColor = new RGBA(Color.white); + drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform); + } + if (hasText) { + DynamicTextModel textModel = new DynamicTextModel(); + List txt = getTextWithStyle(); + TextStyle lastStyle = null; + char prevChar = 0; + boolean lastWasWhiteSpace = false; + for (int i = 0; i < txt.size(); i++) { + CharacterWithStyle cs = txt.get(i); + char c = cs.character; + if (c != '\r' && c != '\n') { + // create new SameStyleTextRecord for all words and all diffrent style text parts + if (lastWasWhiteSpace && !Character.isWhitespace(c)) { + textModel.newWord(); + lastWasWhiteSpace = false; + } + if (cs.style != lastStyle) { + lastStyle = cs.style; + textModel.style = lastStyle; + textModel.newRecord(); + } + Character nextChar = null; + if (i + 1 < txt.size()) { + nextChar = txt.get(i + 1).character; + } + int advance; + FontTag font = lastStyle.font; + GLYPHENTRY ge = new GLYPHENTRY(); + ge.glyphIndex = font.charToGlyph(c); + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextChar != null) { + kerningAdjustment = font.getGlyphKerningAdjustment(ge.glyphIndex, font.charToGlyph(nextChar)); + kerningAdjustment /= font.getDivider(); + } + advance = (int) Math.round(font.getDivider() * Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); + } else { + String fontName = FontTag.defaultFontName; + advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar)); + } + ge.glyphAdvance = advance; + textModel.addGlyph(c, ge); + if (Character.isWhitespace(c)) { + lastWasWhiteSpace = true; + } + } else { + if (c == '\r' || prevChar != '\r') { + if (multiline) { + textModel.newParagraph(); + } + } + } + prevChar = c; + } + + textModel.calculateTexWidths(); + List> lines; + if (multiline && wordWrap) { + lines = new ArrayList<>(); + int lineLength = 0; + for (Paragraph paragraph : textModel.paragraphs) { + List line = new ArrayList<>(); + for (Word word : paragraph.words) { + if (lineLength + word.width <= bounds.getWidth()) { + line.addAll(word.records); + lineLength += word.width; + } else { + lines.add(line); + line = new ArrayList<>(); + line.addAll(word.records); + lineLength = 0; + } + } + if (!line.isEmpty()) { + lines.add(line); + } + } + } else { + lines = new ArrayList<>(); + for (Paragraph paragraph : textModel.paragraphs) { + List line = new ArrayList<>(); + for (Word word : paragraph.words) { + for (SameStyleTextRecord tr : word.records) { + line.add(tr); + } + } + lines.add(line); + } + } + + // remove spaces after last word + for (List line : lines) { + boolean removed = true; + while (removed) { + removed = false; + while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) { + line.remove(line.size() - 1); + removed = true; + } + if (line.size() > 0) { + SameStyleTextRecord lastRecord = line.get(line.size() - 1); + while (lastRecord.glyphEntries.size() > 0 + && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) { + lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1); + removed = true; + } + } + } + } + + textModel.calculateTexWidths(); + + List allTextRecords = new ArrayList<>(); + int yOffset = 0; + for (List line : lines) { + int width = 0; + int currentOffset = 0; + for (SameStyleTextRecord tr : line) { + width += tr.width; + if (tr.style.fontHeight + tr.style.fontLeading > currentOffset) { + currentOffset = tr.style.fontHeight + tr.style.fontLeading; + } + } + yOffset += currentOffset; + int alignOffset = 0; + switch (align) { + case 0: // left + alignOffset = 0; + break; + case 1: // right + alignOffset = bounds.getWidth() - width; + break; + case 2: // center + alignOffset = (bounds.getWidth() - width) / 2; + break; + case 3: // justify + // todo; + break; + } + for (SameStyleTextRecord tr : line) { + tr.xOffset = alignOffset; + alignOffset += tr.width; + } + for (SameStyleTextRecord tr : line) { + TEXTRECORD tr2 = new TEXTRECORD(); + tr2.styleFlagsHasFont = true; + tr2.fontId = fontId; + tr2.textHeight = tr.style.fontHeight; + if (tr.style.textColor != null) { + tr2.styleFlagsHasColor = true; + tr2.textColorA = tr.style.textColor; + } + // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering + tr2.styleFlagsHasXOffset = true; + tr2.xOffset = tr.xOffset; + if (yOffset != 0) { + tr2.styleFlagsHasYOffset = true; + tr2.yOffset = yOffset; + } + tr2.glyphEntries = new GLYPHENTRY[tr.glyphEntries.size()]; + for (int i = 0; i < tr2.glyphEntries.length; i++) { + tr2.glyphEntries[i] = tr.glyphEntries.get(i).glyphEntry; + } + allTextRecords.add(tr2); + } + } + + staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform); + } + return ""; //TODO: Return HTML Canvas converted + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private FontTag getFontTag() { + FontTag font = null; + for (Tag tag : swf.tags) { + if (tag instanceof FontTag) { + if (((FontTag) tag).getFontId() == fontId) { + font = (FontTag) tag; + } + } + } + return font; + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + return render(true, null, new Matrix(), new ColorTransform()); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java index 4076371dc..666935759 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineMorphShape2Tag.java @@ -1,368 +1,377 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.SerializableImage; -import java.awt.Shape; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * - * - * @author JPEXS - */ -public class DefineMorphShape2Tag extends CharacterTag implements MorphShapeTag { - - @SWFType(BasicType.UI16) - public int characterId; - public RECT startBounds; - public RECT endBounds; - public RECT startEdgeBounds; - public RECT endEdgeBounds; - @Reserved - @SWFType(value = BasicType.UB, count = 6) - public int reserved; - public boolean usesNonScalingStrokes; - public boolean usesScalingStrokes; - public MORPHFILLSTYLEARRAY morphFillStyles; - public MORPHLINESTYLEARRAY morphLineStyles; - public SHAPE startEdges; - public SHAPE endEdges; - public static final int ID = 84; - public static final int MAX_RATIO = 65535; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - ret.addAll(morphFillStyles.getNeededCharacters()); - ret.addAll(startEdges.getNeededCharacters()); - ret.addAll(endEdges.getNeededCharacters()); - return ret; - } - - @Override - public RECT getRect() { - RECT rect = new RECT(); - rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin); - rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin); - rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax); - rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax); - return rect; - } - - @Override - public int getCharacterId() { - return characterId; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterId); - sos.writeRECT(startBounds); - sos.writeRECT(endBounds); - sos.writeRECT(startEdgeBounds); - sos.writeRECT(endEdgeBounds); - sos.writeUB(6, reserved); - sos.writeUB(1, usesNonScalingStrokes ? 1 : 0); - sos.writeUB(1, usesScalingStrokes ? 1 : 0); - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion()); - sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 2); - sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 2); - sos2.writeSHAPE(startEdges, 2); - byte[] ba2 = baos2.toByteArray(); - sos.writeUI32(ba2.length); - sos.write(ba2); - sos.writeSHAPE(endEdges, 2); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineMorphShape2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineMorphShape2", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterId = sis.readUI16(); - startBounds = sis.readRECT(); - endBounds = sis.readRECT(); - startEdgeBounds = sis.readRECT(); - endEdgeBounds = sis.readRECT(); - reserved = (int) sis.readUB(6); - usesNonScalingStrokes = sis.readUB(1) == 1; - usesScalingStrokes = sis.readUB(1) == 1; - long offset = sis.readUI32(); - morphFillStyles = sis.readMORPHFILLSTYLEARRAY(); - morphLineStyles = sis.readMORPHLINESTYLEARRAY(2); - startEdges = sis.readSHAPE(2, true); - endEdges = sis.readSHAPE(2, true); - } - - @Override - public RECT getStartBounds() { - return startBounds; - } - - @Override - public RECT getEndBounds() { - return endBounds; - } - - @Override - public MORPHFILLSTYLEARRAY getFillStyles() { - return morphFillStyles; - } - - @Override - public MORPHLINESTYLEARRAY getLineStyles() { - return morphLineStyles; - } - - @Override - public SHAPE getStartEdges() { - return startEdges; - } - - @Override - public SHAPE getEndEdges() { - return endEdges; - } - - @Override - public int getShapeNum() { - return 2; - } - - @Override - public SHAPEWITHSTYLE getShapeAtRatio(int ratio) { - List finalRecords = new ArrayList<>(); - FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio); - LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio); - - int startPosX = 0, startPosY = 0; - int endPosX = 0, endPosY = 0; - int posX = 0, posY = 0; - - for (int startIndex = 0, endIndex = 0; - startIndex < startEdges.shapeRecords.size() - && endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) { - - SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex); - SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex); - if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) { - StyleChangeRecord scr1; - if (edge1 instanceof StyleChangeRecord) { - scr1 = (StyleChangeRecord) edge1; - if (scr1.stateMoveTo) { - startPosX = scr1.moveDeltaX; - startPosY = scr1.moveDeltaY; - } - } else { - scr1 = new StyleChangeRecord(); - startIndex--; - } - StyleChangeRecord scr2; - if (edge2 instanceof StyleChangeRecord) { - scr2 = (StyleChangeRecord) edge2; - if (scr2.stateMoveTo) { - endPosX = scr2.moveDeltaX; - endPosY = scr2.moveDeltaY; - } - } else { - scr2 = new StyleChangeRecord(); - endIndex--; - } - StyleChangeRecord scr = (StyleChangeRecord) scr1.clone(); - if (scr1.stateMoveTo || scr2.stateMoveTo) { - scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / 65535; - scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / 65535; - scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY; - } - finalRecords.add(scr); - continue; - } - - if (edge1 instanceof EndShapeRecord) { - finalRecords.add(edge1); - break; - } - if (edge2 instanceof EndShapeRecord) { - finalRecords.add(edge2); - break; - } - - if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer1 = null; - if (edge1 instanceof CurvedEdgeRecord) { - cer1 = (CurvedEdgeRecord) edge1; - } else if (edge1 instanceof StraightEdgeRecord) { - cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1); - } - CurvedEdgeRecord cer2 = null; - if (edge2 instanceof CurvedEdgeRecord) { - cer2 = (CurvedEdgeRecord) edge2; - } else if (edge2 instanceof StraightEdgeRecord) { - cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2); - } - if ((cer2 == null) || (cer1 == null)) { - continue; - } - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / 65535; - cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / 65535; - cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / 65535; - cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / 65535; - startPosX += cer1.controlDeltaX + cer1.anchorDeltaX; - startPosY += cer1.controlDeltaY + cer1.anchorDeltaY; - endPosX += cer2.controlDeltaX + cer2.anchorDeltaX; - endPosY += cer2.controlDeltaY + cer2.anchorDeltaY; - posX += cer.controlDeltaX + cer.anchorDeltaX; - posY += cer.controlDeltaY + cer.anchorDeltaY; - finalRecords.add(cer); - } else { - StraightEdgeRecord ser1 = null; - if (edge1 instanceof StraightEdgeRecord) { - ser1 = (StraightEdgeRecord) edge1; - } - StraightEdgeRecord ser2 = null; - if (edge2 instanceof StraightEdgeRecord) { - ser2 = (StraightEdgeRecord) edge2; - } - if ((ser2 == null) || (ser1 == null)) { - continue; - } - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.generalLineFlag = true; - ser.vertLineFlag = false; - ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / 65535; - ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / 65535; - startPosX += ser1.deltaX; - startPosY += ser1.deltaY; - endPosX += ser2.deltaX; - endPosY += ser2.deltaY; - posX += ser.deltaX; - posY += ser.deltaX; - finalRecords.add(ser); - } - } - SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); - shape.fillStyles = fillStyles; - shape.lineStyles = lineStyles; - shape.shapeRecords = finalRecords; - return shape; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SHAPEWITHSTYLE shape = getShapeAtRatio(ratio); - // shapeNum: 4 - // todo: Currently the generated image is not cached, because the cache - // key contains the hashCode of the finalRecord object, and it is always - // recreated - BitmapExporter.export(swf, shape, null, image, transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - if (ratio == -2) { - SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0); - SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535); - SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform); - shapeExporter.export(); - } else { - SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform); - shapeExporter.export(); - } - } - - @Override - public int getNumFrames() { - return 65536; - } - - @Override - public boolean isSingleFrame() { - // Morpshape is a single frame specified with the ratio - return true; - } - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline()); - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0); - cmse.export(); - - return cmse.getShapeData(); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.SerializableImage; +import java.awt.Shape; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * + * + * @author JPEXS + */ +public class DefineMorphShape2Tag extends CharacterTag implements MorphShapeTag { + + @SWFType(BasicType.UI16) + public int characterId; + public RECT startBounds; + public RECT endBounds; + public RECT startEdgeBounds; + public RECT endEdgeBounds; + @Reserved + @SWFType(value = BasicType.UB, count = 6) + public int reserved; + public boolean usesNonScalingStrokes; + public boolean usesScalingStrokes; + public MORPHFILLSTYLEARRAY morphFillStyles; + public MORPHLINESTYLEARRAY morphLineStyles; + public SHAPE startEdges; + public SHAPE endEdges; + public static final int ID = 84; + public static final int MAX_RATIO = 65535; + + @Override + public void getNeededCharacters(Set needed) { + morphFillStyles.getNeededCharacters(needed); + startEdges.getNeededCharacters(needed); + endEdges.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + modified |= morphFillStyles.removeCharacter(characterId); + modified |= startEdges.removeCharacter(characterId); + modified |= endEdges.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public RECT getRect() { + RECT rect = new RECT(); + rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin); + rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin); + rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax); + rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax); + return rect; + } + + @Override + public int getCharacterId() { + return characterId; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterId); + sos.writeRECT(startBounds); + sos.writeRECT(endBounds); + sos.writeRECT(startEdgeBounds); + sos.writeRECT(endEdgeBounds); + sos.writeUB(6, reserved); + sos.writeUB(1, usesNonScalingStrokes ? 1 : 0); + sos.writeUB(1, usesScalingStrokes ? 1 : 0); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion()); + sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 2); + sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 2); + sos2.writeSHAPE(startEdges, 2); + byte[] ba2 = baos2.toByteArray(); + sos.writeUI32(ba2.length); + sos.write(ba2); + sos.writeSHAPE(endEdges, 2); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineMorphShape2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineMorphShape2", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterId = sis.readUI16(); + startBounds = sis.readRECT(); + endBounds = sis.readRECT(); + startEdgeBounds = sis.readRECT(); + endEdgeBounds = sis.readRECT(); + reserved = (int) sis.readUB(6); + usesNonScalingStrokes = sis.readUB(1) == 1; + usesScalingStrokes = sis.readUB(1) == 1; + long offset = sis.readUI32(); + morphFillStyles = sis.readMORPHFILLSTYLEARRAY(); + morphLineStyles = sis.readMORPHLINESTYLEARRAY(2); + startEdges = sis.readSHAPE(2, true); + endEdges = sis.readSHAPE(2, true); + } + + @Override + public RECT getStartBounds() { + return startBounds; + } + + @Override + public RECT getEndBounds() { + return endBounds; + } + + @Override + public MORPHFILLSTYLEARRAY getFillStyles() { + return morphFillStyles; + } + + @Override + public MORPHLINESTYLEARRAY getLineStyles() { + return morphLineStyles; + } + + @Override + public SHAPE getStartEdges() { + return startEdges; + } + + @Override + public SHAPE getEndEdges() { + return endEdges; + } + + @Override + public int getShapeNum() { + return 2; + } + + @Override + public SHAPEWITHSTYLE getShapeAtRatio(int ratio) { + List finalRecords = new ArrayList<>(); + FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio); + LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio); + + int startPosX = 0, startPosY = 0; + int endPosX = 0, endPosY = 0; + int posX = 0, posY = 0; + + for (int startIndex = 0, endIndex = 0; + startIndex < startEdges.shapeRecords.size() + && endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) { + + SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex); + SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex); + if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) { + StyleChangeRecord scr1; + if (edge1 instanceof StyleChangeRecord) { + scr1 = (StyleChangeRecord) edge1; + if (scr1.stateMoveTo) { + startPosX = scr1.moveDeltaX; + startPosY = scr1.moveDeltaY; + } + } else { + scr1 = new StyleChangeRecord(); + startIndex--; + } + StyleChangeRecord scr2; + if (edge2 instanceof StyleChangeRecord) { + scr2 = (StyleChangeRecord) edge2; + if (scr2.stateMoveTo) { + endPosX = scr2.moveDeltaX; + endPosY = scr2.moveDeltaY; + } + } else { + scr2 = new StyleChangeRecord(); + endIndex--; + } + StyleChangeRecord scr = (StyleChangeRecord) scr1.clone(); + if (scr1.stateMoveTo || scr2.stateMoveTo) { + scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / 65535; + scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / 65535; + scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY; + } + finalRecords.add(scr); + continue; + } + + if (edge1 instanceof EndShapeRecord) { + finalRecords.add(edge1); + break; + } + if (edge2 instanceof EndShapeRecord) { + finalRecords.add(edge2); + break; + } + + if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer1 = null; + if (edge1 instanceof CurvedEdgeRecord) { + cer1 = (CurvedEdgeRecord) edge1; + } else if (edge1 instanceof StraightEdgeRecord) { + cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1); + } + CurvedEdgeRecord cer2 = null; + if (edge2 instanceof CurvedEdgeRecord) { + cer2 = (CurvedEdgeRecord) edge2; + } else if (edge2 instanceof StraightEdgeRecord) { + cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2); + } + if ((cer2 == null) || (cer1 == null)) { + continue; + } + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / 65535; + cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / 65535; + cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / 65535; + cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / 65535; + startPosX += cer1.controlDeltaX + cer1.anchorDeltaX; + startPosY += cer1.controlDeltaY + cer1.anchorDeltaY; + endPosX += cer2.controlDeltaX + cer2.anchorDeltaX; + endPosY += cer2.controlDeltaY + cer2.anchorDeltaY; + posX += cer.controlDeltaX + cer.anchorDeltaX; + posY += cer.controlDeltaY + cer.anchorDeltaY; + finalRecords.add(cer); + } else { + StraightEdgeRecord ser1 = null; + if (edge1 instanceof StraightEdgeRecord) { + ser1 = (StraightEdgeRecord) edge1; + } + StraightEdgeRecord ser2 = null; + if (edge2 instanceof StraightEdgeRecord) { + ser2 = (StraightEdgeRecord) edge2; + } + if ((ser2 == null) || (ser1 == null)) { + continue; + } + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.generalLineFlag = true; + ser.vertLineFlag = false; + ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / 65535; + ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / 65535; + startPosX += ser1.deltaX; + startPosY += ser1.deltaY; + endPosX += ser2.deltaX; + endPosY += ser2.deltaY; + posX += ser.deltaX; + posY += ser.deltaX; + finalRecords.add(ser); + } + } + SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); + shape.fillStyles = fillStyles; + shape.lineStyles = lineStyles; + shape.shapeRecords = finalRecords; + return shape; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + SHAPEWITHSTYLE shape = getShapeAtRatio(ratio); + // shapeNum: 4 + // todo: Currently the generated image is not cached, because the cache + // key contains the hashCode of the finalRecord object, and it is always + // recreated + BitmapExporter.export(swf, shape, null, image, transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + if (ratio == -2) { + SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0); + SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535); + SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform); + shapeExporter.export(); + } else { + SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform); + shapeExporter.export(); + } + } + + @Override + public int getNumFrames() { + return 65536; + } + + @Override + public boolean isSingleFrame() { + // Morpshape is a single frame specified with the ratio + return true; + } + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline()); + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0); + cmse.export(); + + return cmse.getShapeData(); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java b/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java index 8f670e110..edec881b8 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineMorphShapeTag.java @@ -1,351 +1,360 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.SerializableImage; -import java.awt.Shape; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * - * - * @author JPEXS - */ -public class DefineMorphShapeTag extends CharacterTag implements MorphShapeTag { - - @SWFType(BasicType.UI16) - public int characterId; - public RECT startBounds; - public RECT endBounds; - public MORPHFILLSTYLEARRAY morphFillStyles; - public MORPHLINESTYLEARRAY morphLineStyles; - public SHAPE startEdges; - public SHAPE endEdges; - public static final int ID = 46; - public static final int MAX_RATIO = 65535; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - ret.addAll(morphFillStyles.getNeededCharacters()); - ret.addAll(startEdges.getNeededCharacters()); - ret.addAll(endEdges.getNeededCharacters()); - return ret; - } - - @Override - public int getCharacterId() { - return characterId; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterId); - sos.writeRECT(startBounds); - sos.writeRECT(endBounds); - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion()); - sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 1); - sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 1); - sos2.writeSHAPE(startEdges, 1); - byte[] d = baos2.toByteArray(); - sos.writeUI32(d.length); - sos.write(d); - sos.writeSHAPE(endEdges, 1); - - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineMorphShapeTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineMorphShape", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterId = sis.readUI16(); - startBounds = sis.readRECT(); - endBounds = sis.readRECT(); - long offset = sis.readUI32(); //ignore - morphFillStyles = sis.readMORPHFILLSTYLEARRAY(); - morphLineStyles = sis.readMORPHLINESTYLEARRAY(1); - startEdges = sis.readSHAPE(1, true); - endEdges = sis.readSHAPE(1, true); - } - - @Override - public RECT getRect() { - RECT rect = new RECT(); - rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin); - rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin); - rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax); - rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax); - return rect; - } - - @Override - public RECT getStartBounds() { - return startBounds; - } - - @Override - public RECT getEndBounds() { - return endBounds; - } - - @Override - public MORPHFILLSTYLEARRAY getFillStyles() { - return morphFillStyles; - } - - @Override - public MORPHLINESTYLEARRAY getLineStyles() { - return morphLineStyles; - } - - @Override - public SHAPE getStartEdges() { - return startEdges; - } - - @Override - public SHAPE getEndEdges() { - return endEdges; - } - - @Override - public int getShapeNum() { - return 1; - } - - @Override - public SHAPEWITHSTYLE getShapeAtRatio(int ratio) { - List finalRecords = new ArrayList<>(); - FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio); - LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio); - - int startPosX = 0, startPosY = 0; - int endPosX = 0, endPosY = 0; - int posX = 0, posY = 0; - - for (int startIndex = 0, endIndex = 0; - startIndex < startEdges.shapeRecords.size() - && endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) { - - SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex); - SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex); - if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) { - StyleChangeRecord scr1; - if (edge1 instanceof StyleChangeRecord) { - scr1 = (StyleChangeRecord) edge1; - if (scr1.stateMoveTo) { - startPosX = scr1.moveDeltaX; - startPosY = scr1.moveDeltaY; - } - } else { - scr1 = new StyleChangeRecord(); - startIndex--; - } - StyleChangeRecord scr2; - if (edge2 instanceof StyleChangeRecord) { - scr2 = (StyleChangeRecord) edge2; - if (scr2.stateMoveTo) { - endPosX = scr2.moveDeltaX; - endPosY = scr2.moveDeltaY; - } - } else { - scr2 = new StyleChangeRecord(); - endIndex--; - } - StyleChangeRecord scr = (StyleChangeRecord) scr1.clone(); - if (scr1.stateMoveTo || scr2.stateMoveTo) { - scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO; - scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO; - scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY; - } - finalRecords.add(scr); - continue; - } - - if (edge1 instanceof EndShapeRecord) { - finalRecords.add(edge1); - break; - } - if (edge2 instanceof EndShapeRecord) { - finalRecords.add(edge2); - break; - } - - if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer1 = null; - if (edge1 instanceof CurvedEdgeRecord) { - cer1 = (CurvedEdgeRecord) edge1; - } else if (edge1 instanceof StraightEdgeRecord) { - cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1); - } - CurvedEdgeRecord cer2 = null; - if (edge2 instanceof CurvedEdgeRecord) { - cer2 = (CurvedEdgeRecord) edge2; - } else if (edge2 instanceof StraightEdgeRecord) { - cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2); - } - if ((cer2 == null) || (cer1 == null)) { - continue; - } - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO; - cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO; - cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO; - cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO; - startPosX += cer1.controlDeltaX + cer1.anchorDeltaX; - startPosY += cer1.controlDeltaY + cer1.anchorDeltaY; - endPosX += cer2.controlDeltaX + cer2.anchorDeltaX; - endPosY += cer2.controlDeltaY + cer2.anchorDeltaY; - posX += cer.controlDeltaX + cer.anchorDeltaX; - posY += cer.controlDeltaY + cer.anchorDeltaY; - finalRecords.add(cer); - } else { - StraightEdgeRecord ser1 = null; - if (edge1 instanceof StraightEdgeRecord) { - ser1 = (StraightEdgeRecord) edge1; - } - StraightEdgeRecord ser2 = null; - if (edge2 instanceof StraightEdgeRecord) { - ser2 = (StraightEdgeRecord) edge2; - } - if ((ser2 == null) || (ser1 == null)) { - continue; - } - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.generalLineFlag = true; - ser.vertLineFlag = false; - ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO; - ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO; - startPosX += ser1.deltaX; - startPosY += ser1.deltaY; - endPosX += ser2.deltaX; - endPosY += ser2.deltaY; - posX += ser.deltaX; - posY += ser.deltaX; - finalRecords.add(ser); - } - } - SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); - shape.fillStyles = fillStyles; - shape.lineStyles = lineStyles; - shape.shapeRecords = finalRecords; - return shape; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SHAPEWITHSTYLE shape = getShapeAtRatio(ratio); - // shapeNum: 3 - // todo: Currently the generated image is not cached, because the cache - // key contains the hashCode of the finalRecord object, and it is always - // recreated - BitmapExporter.export(swf, shape, null, image, transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - if (ratio == -2) { - SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0); - SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535); - SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform); - shapeExporter.export(); - } else { - SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform); - shapeExporter.export(); - } - } - - @Override - public int getNumFrames() { - return 65536; - } - - @Override - public boolean isSingleFrame() { - // Morpshape is a single frame specified with the ratio - return true; - } - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline()); - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0); - cmse.export(); - - return cmse.getShapeData(); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.SerializableImage; +import java.awt.Shape; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * + * + * @author JPEXS + */ +public class DefineMorphShapeTag extends CharacterTag implements MorphShapeTag { + + @SWFType(BasicType.UI16) + public int characterId; + public RECT startBounds; + public RECT endBounds; + public MORPHFILLSTYLEARRAY morphFillStyles; + public MORPHLINESTYLEARRAY morphLineStyles; + public SHAPE startEdges; + public SHAPE endEdges; + public static final int ID = 46; + public static final int MAX_RATIO = 65535; + + @Override + public void getNeededCharacters(Set needed) { + morphFillStyles.getNeededCharacters(needed); + startEdges.getNeededCharacters(needed); + endEdges.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + modified |= morphFillStyles.removeCharacter(characterId); + modified |= startEdges.removeCharacter(characterId); + modified |= endEdges.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public int getCharacterId() { + return characterId; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterId); + sos.writeRECT(startBounds); + sos.writeRECT(endBounds); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(baos2, getVersion()); + sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 1); + sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 1); + sos2.writeSHAPE(startEdges, 1); + byte[] d = baos2.toByteArray(); + sos.writeUI32(d.length); + sos.write(d); + sos.writeSHAPE(endEdges, 1); + + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineMorphShapeTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineMorphShape", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterId = sis.readUI16(); + startBounds = sis.readRECT(); + endBounds = sis.readRECT(); + long offset = sis.readUI32(); //ignore + morphFillStyles = sis.readMORPHFILLSTYLEARRAY(); + morphLineStyles = sis.readMORPHLINESTYLEARRAY(1); + startEdges = sis.readSHAPE(1, true); + endEdges = sis.readSHAPE(1, true); + } + + @Override + public RECT getRect() { + RECT rect = new RECT(); + rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin); + rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin); + rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax); + rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax); + return rect; + } + + @Override + public RECT getStartBounds() { + return startBounds; + } + + @Override + public RECT getEndBounds() { + return endBounds; + } + + @Override + public MORPHFILLSTYLEARRAY getFillStyles() { + return morphFillStyles; + } + + @Override + public MORPHLINESTYLEARRAY getLineStyles() { + return morphLineStyles; + } + + @Override + public SHAPE getStartEdges() { + return startEdges; + } + + @Override + public SHAPE getEndEdges() { + return endEdges; + } + + @Override + public int getShapeNum() { + return 1; + } + + @Override + public SHAPEWITHSTYLE getShapeAtRatio(int ratio) { + List finalRecords = new ArrayList<>(); + FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio); + LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio); + + int startPosX = 0, startPosY = 0; + int endPosX = 0, endPosY = 0; + int posX = 0, posY = 0; + + for (int startIndex = 0, endIndex = 0; + startIndex < startEdges.shapeRecords.size() + && endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) { + + SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex); + SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex); + if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) { + StyleChangeRecord scr1; + if (edge1 instanceof StyleChangeRecord) { + scr1 = (StyleChangeRecord) edge1; + if (scr1.stateMoveTo) { + startPosX = scr1.moveDeltaX; + startPosY = scr1.moveDeltaY; + } + } else { + scr1 = new StyleChangeRecord(); + startIndex--; + } + StyleChangeRecord scr2; + if (edge2 instanceof StyleChangeRecord) { + scr2 = (StyleChangeRecord) edge2; + if (scr2.stateMoveTo) { + endPosX = scr2.moveDeltaX; + endPosY = scr2.moveDeltaY; + } + } else { + scr2 = new StyleChangeRecord(); + endIndex--; + } + StyleChangeRecord scr = (StyleChangeRecord) scr1.clone(); + if (scr1.stateMoveTo || scr2.stateMoveTo) { + scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO; + scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO; + scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY; + } + finalRecords.add(scr); + continue; + } + + if (edge1 instanceof EndShapeRecord) { + finalRecords.add(edge1); + break; + } + if (edge2 instanceof EndShapeRecord) { + finalRecords.add(edge2); + break; + } + + if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer1 = null; + if (edge1 instanceof CurvedEdgeRecord) { + cer1 = (CurvedEdgeRecord) edge1; + } else if (edge1 instanceof StraightEdgeRecord) { + cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1); + } + CurvedEdgeRecord cer2 = null; + if (edge2 instanceof CurvedEdgeRecord) { + cer2 = (CurvedEdgeRecord) edge2; + } else if (edge2 instanceof StraightEdgeRecord) { + cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2); + } + if ((cer2 == null) || (cer1 == null)) { + continue; + } + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO; + cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO; + cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO; + cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO; + startPosX += cer1.controlDeltaX + cer1.anchorDeltaX; + startPosY += cer1.controlDeltaY + cer1.anchorDeltaY; + endPosX += cer2.controlDeltaX + cer2.anchorDeltaX; + endPosY += cer2.controlDeltaY + cer2.anchorDeltaY; + posX += cer.controlDeltaX + cer.anchorDeltaX; + posY += cer.controlDeltaY + cer.anchorDeltaY; + finalRecords.add(cer); + } else { + StraightEdgeRecord ser1 = null; + if (edge1 instanceof StraightEdgeRecord) { + ser1 = (StraightEdgeRecord) edge1; + } + StraightEdgeRecord ser2 = null; + if (edge2 instanceof StraightEdgeRecord) { + ser2 = (StraightEdgeRecord) edge2; + } + if ((ser2 == null) || (ser1 == null)) { + continue; + } + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.generalLineFlag = true; + ser.vertLineFlag = false; + ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO; + ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO; + startPosX += ser1.deltaX; + startPosY += ser1.deltaY; + endPosX += ser2.deltaX; + endPosY += ser2.deltaY; + posX += ser.deltaX; + posY += ser.deltaX; + finalRecords.add(ser); + } + } + SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); + shape.fillStyles = fillStyles; + shape.lineStyles = lineStyles; + shape.shapeRecords = finalRecords; + return shape; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + SHAPEWITHSTYLE shape = getShapeAtRatio(ratio); + // shapeNum: 3 + // todo: Currently the generated image is not cached, because the cache + // key contains the hashCode of the finalRecord object, and it is always + // recreated + BitmapExporter.export(swf, shape, null, image, transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + if (ratio == -2) { + SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0); + SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535); + SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform); + shapeExporter.export(); + } else { + SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform); + shapeExporter.export(); + } + } + + @Override + public int getNumFrames() { + return 65536; + } + + @Override + public boolean isSingleFrame() { + // Morpshape is a single frame specified with the ratio + return true; + } + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline()); + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0); + cmse.export(); + + return cmse.getShapeData(); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java index 9298a4545..7f3774b81 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineShape2Tag.java @@ -1,80 +1,89 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.Set; - -public class DefineShape2Tag extends ShapeTag { - - @SWFType(BasicType.UI16) - public int shapeId; - private final RECT shapeBounds; - public SHAPEWITHSTYLE shapes; - public static final int ID = 22; - - @Override - public int getShapeNum() { - return 2; - } - - @Override - public SHAPEWITHSTYLE getShapes() { - return shapes; - } - - @Override - public Set getNeededCharacters() { - return shapes.getNeededCharacters(); - } - - @Override - public int getCharacterId() { - return shapeId; - } - - @Override - public RECT getRect() { - return shapeBounds; - } - - public DefineShape2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineShape2", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - shapeId = sis.readUI16(); - shapeBounds = sis.readRECT(); - shapes = sis.readSHAPEWITHSTYLE(2, false); - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Set; + +public class DefineShape2Tag extends ShapeTag { + + @SWFType(BasicType.UI16) + public int shapeId; + private final RECT shapeBounds; + public SHAPEWITHSTYLE shapes; + public static final int ID = 22; + + @Override + public int getShapeNum() { + return 2; + } + + @Override + public SHAPEWITHSTYLE getShapes() { + return shapes; + } + + @Override + public void getNeededCharacters(Set needed) { + shapes.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = shapes.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public int getCharacterId() { + return shapeId; + } + + @Override + public RECT getRect() { + return shapeBounds; + } + + public DefineShape2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineShape2", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + shapeId = sis.readUI16(); + shapeBounds = sis.readRECT(); + shapes = sis.readSHAPEWITHSTYLE(2, false); + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java index d5d6673a6..4db3187a0 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineShape3Tag.java @@ -1,80 +1,89 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.Set; - -public class DefineShape3Tag extends ShapeTag { - - @SWFType(BasicType.UI16) - public int shapeId; - public RECT shapeBounds; - public SHAPEWITHSTYLE shapes; - public static final int ID = 32; - - @Override - public int getShapeNum() { - return 3; - } - - @Override - public SHAPEWITHSTYLE getShapes() { - return shapes; - } - - @Override - public Set getNeededCharacters() { - return shapes.getNeededCharacters(); - } - - @Override - public RECT getRect() { - return shapeBounds; - } - - @Override - public int getCharacterId() { - return shapeId; - } - - public DefineShape3Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineShape3", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - shapeId = sis.readUI16(); - shapeBounds = sis.readRECT(); - shapes = sis.readSHAPEWITHSTYLE(3, false); - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Set; + +public class DefineShape3Tag extends ShapeTag { + + @SWFType(BasicType.UI16) + public int shapeId; + public RECT shapeBounds; + public SHAPEWITHSTYLE shapes; + public static final int ID = 32; + + @Override + public int getShapeNum() { + return 3; + } + + @Override + public SHAPEWITHSTYLE getShapes() { + return shapes; + } + + @Override + public void getNeededCharacters(Set needed) { + shapes.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = shapes.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public RECT getRect() { + return shapeBounds; + } + + @Override + public int getCharacterId() { + return shapeId; + } + + public DefineShape3Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineShape3", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + shapeId = sis.readUI16(); + shapeBounds = sis.readRECT(); + shapes = sis.readSHAPEWITHSTYLE(3, false); + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java index 4ac8797cf..f70845209 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineShape4Tag.java @@ -1,93 +1,102 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.Set; - -public class DefineShape4Tag extends ShapeTag { - - @SWFType(BasicType.UI16) - public int shapeId; - public RECT shapeBounds; - public RECT edgeBounds; - @Reserved - @SWFType(value = BasicType.UB, count = 5) - public int reserved; - public boolean usesFillWindingRule; - public boolean usesNonScalingStrokes; - public boolean usesScalingStrokes; - public SHAPEWITHSTYLE shapes; - public static final int ID = 83; - - @Override - public int getShapeNum() { - return 4; - } - - @Override - public SHAPEWITHSTYLE getShapes() { - return shapes; - } - - @Override - public Set getNeededCharacters() { - return shapes.getNeededCharacters(); - } - - @Override - public int getCharacterId() { - return shapeId; - } - - @Override - public RECT getRect() { - return shapeBounds; - } - - public DefineShape4Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineShape4", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - shapeId = sis.readUI16(); - shapeBounds = sis.readRECT(); - edgeBounds = sis.readRECT(); - reserved = (int) sis.readUB(5); - usesFillWindingRule = sis.readUB(1) == 1; - usesNonScalingStrokes = sis.readUB(1) == 1; - usesScalingStrokes = sis.readUB(1) == 1; - shapes = sis.readSHAPEWITHSTYLE(4, false); - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Set; + +public class DefineShape4Tag extends ShapeTag { + + @SWFType(BasicType.UI16) + public int shapeId; + public RECT shapeBounds; + public RECT edgeBounds; + @Reserved + @SWFType(value = BasicType.UB, count = 5) + public int reserved; + public boolean usesFillWindingRule; + public boolean usesNonScalingStrokes; + public boolean usesScalingStrokes; + public SHAPEWITHSTYLE shapes; + public static final int ID = 83; + + @Override + public int getShapeNum() { + return 4; + } + + @Override + public SHAPEWITHSTYLE getShapes() { + return shapes; + } + + @Override + public void getNeededCharacters(Set needed) { + shapes.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = shapes.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public int getCharacterId() { + return shapeId; + } + + @Override + public RECT getRect() { + return shapeBounds; + } + + public DefineShape4Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineShape4", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + shapeId = sis.readUI16(); + shapeBounds = sis.readRECT(); + edgeBounds = sis.readRECT(); + reserved = (int) sis.readUB(5); + usesFillWindingRule = sis.readUB(1) == 1; + usesNonScalingStrokes = sis.readUB(1) == 1; + usesScalingStrokes = sis.readUB(1) == 1; + shapes = sis.readSHAPEWITHSTYLE(4, false); + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java b/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java index 1d8b7ad98..efcb8317c 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineShapeTag.java @@ -1,100 +1,109 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Set; - -public class DefineShapeTag extends ShapeTag { - - @SWFType(BasicType.UI16) - public int shapeId; - public RECT shapeBounds; - public SHAPEWITHSTYLE shapes; - public static final int ID = 2; - - @Override - public int getShapeNum() { - return 1; - } - - @Override - public SHAPEWITHSTYLE getShapes() { - return shapes; - } - - @Override - public Set getNeededCharacters() { - return shapes.getNeededCharacters(); - } - - @Override - public RECT getRect() { - return shapeBounds; - } - - public DefineShapeTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineShape", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - shapeId = sis.readUI16(); - shapeBounds = sis.readRECT(); - shapes = sis.readSHAPEWITHSTYLE(1, false); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, getVersion()); - try { - sos.writeUI16(shapeId); - sos.writeRECT(shapeBounds); - sos.writeSHAPEWITHSTYLE(shapes, 1); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - @Override - public int getCharacterId() { - return shapeId; - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Set; + +public class DefineShapeTag extends ShapeTag { + + @SWFType(BasicType.UI16) + public int shapeId; + public RECT shapeBounds; + public SHAPEWITHSTYLE shapes; + public static final int ID = 2; + + @Override + public int getShapeNum() { + return 1; + } + + @Override + public SHAPEWITHSTYLE getShapes() { + return shapes; + } + + @Override + public void getNeededCharacters(Set needed) { + shapes.getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = shapes.removeCharacter(characterId); + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public RECT getRect() { + return shapeBounds; + } + + public DefineShapeTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineShape", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + shapeId = sis.readUI16(); + shapeBounds = sis.readRECT(); + shapes = sis.readSHAPEWITHSTYLE(1, false); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, getVersion()); + try { + sos.writeUI16(shapeId); + sos.writeRECT(shapeBounds); + sos.writeSHAPEWITHSTYLE(shapes, 1); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + @Override + public int getCharacterId() { + return shapeId; + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java b/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java index 7087b1267..2432b7db9 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineSpriteTag.java @@ -1,341 +1,359 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.SerializableImage; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Defines a sprite character - */ -public class DefineSpriteTag extends CharacterTag implements Container, DrawableTag, Timelined { - - /** - * Character ID of sprite - */ - @SWFType(BasicType.UI16) - public int spriteId; - /** - * Number of frames in sprite - */ - @SWFType(BasicType.UI16) - public int frameCount; - /** - * A series of tags - */ - public List subTags; - public boolean hasEndTag; - - public static final int ID = 39; - - private Timeline timeline; - - private boolean isSingleFrameInitialized; - private boolean isSingleFrame; - - @Override - public Timeline getTimeline() { - if (timeline == null) { - timeline = new Timeline(swf, subTags, spriteId, getRect()); - } - return timeline; - } - - @Override - public void resetTimeline() { - timeline = null; - } - - @Override - public int getCharacterId() { - return spriteId; - } - - private RECT getCharacterBounds(Set characters) { - RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - boolean foundSomething = false; - for (int c : characters) { - Tag t = swf.characters.get(c); - RECT r = null; - if (t instanceof BoundedTag) { - r = ((BoundedTag) t).getRect(); - } - if (r != null) { - foundSomething = true; - ret.Xmin = Math.min(r.Xmin, ret.Xmin); - ret.Ymin = Math.min(r.Ymin, ret.Ymin); - ret.Xmax = Math.max(r.Xmax, ret.Xmax); - ret.Ymax = Math.max(r.Ymax, ret.Ymax); - } - } - if (!foundSomething) { - return new RECT(); - } - return ret; - } - private static final Cache rectCache = Cache.getInstance(true); - - @Override - public RECT getRect() { - if (rectCache.contains(this)) { - return (RECT) rectCache.get(this); - } - RECT emptyRet = new RECT(); - RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - HashMap depthMap = new HashMap<>(); - boolean foundSomething = false; - int pos = 0; - for (Tag t : subTags) { - pos++; - MATRIX m = null; - int characterId = -1; - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; - m = pot.getMatrix(); - int charId = pot.getCharacterId(); - if (charId > -1) { - depthMap.put(pot.getDepth(), charId); - characterId = (charId); - } else { - Integer chi = (depthMap.get(pot.getDepth())); - if (chi != null) { - characterId = chi; - } - } - } - if (characterId == -1) { - continue; - } - Set need = new HashSet<>(); - need.add(characterId); - RECT r = getCharacterBounds(need); - - if (m != null) { - AffineTransform trans = SWF.matrixToTransform(m); - - java.awt.Point topleft = new java.awt.Point(); - trans.transform(new java.awt.Point(r.Xmin, r.Ymin), topleft); - java.awt.Point topright = new java.awt.Point(); - trans.transform(new java.awt.Point(r.Xmax, r.Ymin), topright); - java.awt.Point bottomright = new java.awt.Point(); - trans.transform(new java.awt.Point(r.Xmax, r.Ymax), bottomright); - java.awt.Point bottomleft = new java.awt.Point(); - trans.transform(new java.awt.Point(r.Xmin, r.Ymax), bottomleft); - - r.Xmin = (int) Math.min(Math.min(Math.min(topleft.x, topright.x), bottomleft.x), bottomright.x); - r.Ymin = (int) Math.min(Math.min(Math.min(topleft.y, topright.y), bottomleft.y), bottomright.y); - r.Xmax = (int) Math.max(Math.max(Math.max(topleft.x, topright.x), bottomleft.x), bottomright.x); - r.Ymax = (int) Math.max(Math.max(Math.max(topleft.y, topright.y), bottomleft.y), bottomright.y); - - } - ret.Xmin = Math.min(r.Xmin, ret.Xmin); - ret.Ymin = Math.min(r.Ymin, ret.Ymin); - ret.Xmax = Math.max(r.Xmax, ret.Xmax); - ret.Ymax = Math.max(r.Ymax, ret.Ymax); - foundSomething = true; - } - if (!foundSomething) { - ret = new RECT(); - } - rectCache.put(this, ret); - return ret; - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param level - * @param pos - * @param parallel - * @param skipUnusualTags - * @throws IOException - * @throws java.lang.InterruptedException - */ - public DefineSpriteTag(SWF swf, byte[] headerData, byte[] data, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException { - super(swf, ID, "DefineSprite", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version, pos); - spriteId = sis.readUI16(); - frameCount = sis.readUI16(); - List subTags = sis.readTagList(swf, this, level + 1, parallel, skipUnusualTags, true, swf.gfx); - if (subTags.get(subTags.size() - 1).getId() == EndTag.ID) { - hasEndTag = true; - subTags.remove(subTags.size() - 1); - } - this.subTags = subTags; - } - static int c = 0; - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(spriteId); - sos.writeUI16(frameCount); - sos.writeTags(subTags); - if (hasEndTag) { - sos.writeUI16(0); - } - sos.close(); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Returns all sub-items - * - * @return List of sub-items - */ - @Override - public List getSubItems() { - List ret = new ArrayList<>(); - ret.addAll(subTags); - return ret; - } - - /** - * Returns number of sub-items - * - * @return Number of sub-items - */ - @Override - public int getItemCount() { - return subTags.size(); - } - - @Override - public boolean hasSubTags() { - return true; - } - - @Override - public List getSubTags() { - return subTags; - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - for (Tag t : subTags) { - ret.addAll(t.getNeededCharacters()); - } - return ret; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SWF.frameToImage(getTimeline(), frame, time, stateUnderCursor, mouseButton, image, transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { - SWF.frameToSvg(getTimeline(), 0, 0, null, 0, exporter, colorTransform, level + 1); - } - - @Override - public int getNumFrames() { - return frameCount; - } - - @Override - public boolean isSingleFrame() { - if (!isSingleFrameInitialized) { - initialiteIsSingleFrame(); - } - return isSingleFrame; - } - - private synchronized void initialiteIsSingleFrame() { - if (!isSingleFrameInitialized) { - if (frameCount > 1) { - isSingleFrameInitialized = true; - return; - } - - isSingleFrame = getTimeline().isSingleFrame(); - isSingleFrameInitialized = true; - } - } - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - return getTimeline().getOutline(frame, time, ratio, stateUnderCursor, mouseButton, transformation); - } - - @Override - public boolean isModified() { - if (super.isModified()) { - return true; - } - for (Tag t : subTags) { - if (t.isModified()) { - return true; - } - } - return false; - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - return getTimeline().toHtmlCanvas(unitDivisor, null); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.SerializableImage; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Defines a sprite character + */ +public class DefineSpriteTag extends CharacterTag implements Container, DrawableTag, Timelined { + + /** + * Character ID of sprite + */ + @SWFType(BasicType.UI16) + public int spriteId; + /** + * Number of frames in sprite + */ + @SWFType(BasicType.UI16) + public int frameCount; + /** + * A series of tags + */ + public List subTags; + public boolean hasEndTag; + + public static final int ID = 39; + + private Timeline timeline; + + private boolean isSingleFrameInitialized; + private boolean isSingleFrame; + + @Override + public Timeline getTimeline() { + if (timeline == null) { + timeline = new Timeline(swf, subTags, spriteId, getRect()); + } + return timeline; + } + + @Override + public void resetTimeline() { + timeline = null; + } + + @Override + public int getCharacterId() { + return spriteId; + } + + private RECT getCharacterBounds(Set characters) { + RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + boolean foundSomething = false; + for (int c : characters) { + Tag t = swf.characters.get(c); + RECT r = null; + if (t instanceof BoundedTag) { + r = ((BoundedTag) t).getRect(); + } + if (r != null) { + foundSomething = true; + ret.Xmin = Math.min(r.Xmin, ret.Xmin); + ret.Ymin = Math.min(r.Ymin, ret.Ymin); + ret.Xmax = Math.max(r.Xmax, ret.Xmax); + ret.Ymax = Math.max(r.Ymax, ret.Ymax); + } + } + if (!foundSomething) { + return new RECT(); + } + return ret; + } + private static final Cache rectCache = Cache.getInstance(true); + + @Override + public RECT getRect() { + if (rectCache.contains(this)) { + return (RECT) rectCache.get(this); + } + RECT emptyRet = new RECT(); + RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + HashMap depthMap = new HashMap<>(); + boolean foundSomething = false; + int pos = 0; + for (Tag t : subTags) { + pos++; + MATRIX m = null; + int characterId = -1; + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t; + m = pot.getMatrix(); + int charId = pot.getCharacterId(); + if (charId > -1) { + depthMap.put(pot.getDepth(), charId); + characterId = (charId); + } else { + Integer chi = (depthMap.get(pot.getDepth())); + if (chi != null) { + characterId = chi; + } + } + } + if (characterId == -1) { + continue; + } + Set need = new HashSet<>(); + need.add(characterId); + RECT r = getCharacterBounds(need); + + if (m != null) { + AffineTransform trans = SWF.matrixToTransform(m); + + java.awt.Point topleft = new java.awt.Point(); + trans.transform(new java.awt.Point(r.Xmin, r.Ymin), topleft); + java.awt.Point topright = new java.awt.Point(); + trans.transform(new java.awt.Point(r.Xmax, r.Ymin), topright); + java.awt.Point bottomright = new java.awt.Point(); + trans.transform(new java.awt.Point(r.Xmax, r.Ymax), bottomright); + java.awt.Point bottomleft = new java.awt.Point(); + trans.transform(new java.awt.Point(r.Xmin, r.Ymax), bottomleft); + + r.Xmin = (int) Math.min(Math.min(Math.min(topleft.x, topright.x), bottomleft.x), bottomright.x); + r.Ymin = (int) Math.min(Math.min(Math.min(topleft.y, topright.y), bottomleft.y), bottomright.y); + r.Xmax = (int) Math.max(Math.max(Math.max(topleft.x, topright.x), bottomleft.x), bottomright.x); + r.Ymax = (int) Math.max(Math.max(Math.max(topleft.y, topright.y), bottomleft.y), bottomright.y); + + } + ret.Xmin = Math.min(r.Xmin, ret.Xmin); + ret.Ymin = Math.min(r.Ymin, ret.Ymin); + ret.Xmax = Math.max(r.Xmax, ret.Xmax); + ret.Ymax = Math.max(r.Ymax, ret.Ymax); + foundSomething = true; + } + if (!foundSomething) { + ret = new RECT(); + } + rectCache.put(this, ret); + return ret; + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param level + * @param pos + * @param parallel + * @param skipUnusualTags + * @throws IOException + * @throws java.lang.InterruptedException + */ + public DefineSpriteTag(SWF swf, byte[] headerData, byte[] data, int level, long pos, boolean parallel, boolean skipUnusualTags) throws IOException, InterruptedException { + super(swf, ID, "DefineSprite", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version, pos); + spriteId = sis.readUI16(); + frameCount = sis.readUI16(); + List subTags = sis.readTagList(swf, this, level + 1, parallel, skipUnusualTags, true, swf.gfx); + if (subTags.get(subTags.size() - 1).getId() == EndTag.ID) { + hasEndTag = true; + subTags.remove(subTags.size() - 1); + } + this.subTags = subTags; + } + static int c = 0; + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(spriteId); + sos.writeUI16(frameCount); + sos.writeTags(subTags); + if (hasEndTag) { + sos.writeUI16(0); + } + sos.close(); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Returns all sub-items + * + * @return List of sub-items + */ + @Override + public List getSubItems() { + List ret = new ArrayList<>(); + ret.addAll(subTags); + return ret; + } + + /** + * Returns number of sub-items + * + * @return Number of sub-items + */ + @Override + public int getItemCount() { + return subTags.size(); + } + + @Override + public boolean hasSubTags() { + return true; + } + + @Override + public List getSubTags() { + return subTags; + } + + @Override + public void getNeededCharacters(Set needed) { + for (Tag t : subTags) { + if (t instanceof CharacterIdTag) { + needed.add(((CharacterIdTag) t).getCharacterId()); + } + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (int i = 0; i < subTags.size(); i++) { + Tag t = subTags.get(i); + if (t instanceof CharacterIdTag && ((CharacterIdTag) t).getCharacterId() == characterId) { + subTags.remove(i); + i--; + modified = true; + } + } + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + SWF.frameToImage(getTimeline(), frame, time, stateUnderCursor, mouseButton, image, transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + SWF.frameToSvg(getTimeline(), 0, 0, null, 0, exporter, colorTransform, level + 1); + } + + @Override + public int getNumFrames() { + return frameCount; + } + + @Override + public boolean isSingleFrame() { + if (!isSingleFrameInitialized) { + initialiteIsSingleFrame(); + } + return isSingleFrame; + } + + private synchronized void initialiteIsSingleFrame() { + if (!isSingleFrameInitialized) { + if (frameCount > 1) { + isSingleFrameInitialized = true; + return; + } + + isSingleFrame = getTimeline().isSingleFrame(); + isSingleFrameInitialized = true; + } + } + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + return getTimeline().getOutline(frame, time, ratio, stateUnderCursor, mouseButton, transformation); + } + + @Override + public boolean isModified() { + if (super.isModified()) { + return true; + } + for (Tag t : subTags) { + if (t.isModified()) { + return true; + } + } + return false; + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + return getTimeline().toHtmlCanvas(unitDivisor, null); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java b/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java index c9f68c196..d2c25d060 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineText2Tag.java @@ -1,520 +1,533 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.text.ParseException; -import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; -import com.jpexs.decompiler.flash.tags.text.TextLexer; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * - * @author JPEXS - */ -public class DefineText2Tag extends TextTag { - - @SWFType(BasicType.UI16) - public int characterID; - public RECT textBounds; - public MATRIX textMatrix; - public List textRecords; - public static final int ID = 33; - - @Override - public RECT getBounds() { - return textBounds; - } - - @Override - public MATRIX getTextMatrix() { - return textMatrix; - } - - @Override - public void setBounds(RECT r) { - textBounds = r; - } - - @Override - public String getText(String separator) { - FontTag fnt = null; - String ret = ""; - boolean first = true; - for (TEXTRECORD rec : textRecords) { - if (!first) { - ret += Helper.newLine + separator + Helper.newLine; - } - first = false; - if (rec.styleFlagsHasFont) { - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == rec.fontId) { - fnt = ((FontTag) t); - break; - } - } - } - } - if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) { - /*if (!ret.isEmpty()) { - ret += "\r\n"; - }*/ - } - ret += rec.getText(fnt); - } - return ret; - } - - @Override - public List getFontIds() { - List ret = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasFont) { - ret.add(rec.fontId); - } - } - return ret; - } - - @Override - public String getFormattedText() { - FontTag fnt = null; - String ret = ""; - ret += "[\r\nxmin " + textBounds.Xmin + "\r\nymin " + textBounds.Ymin + "\r\nxmax " + textBounds.Xmax + "\r\nymax " + textBounds.Ymax; - if (textMatrix.translateX != 0) { - ret += "\r\ntranslatex " + textMatrix.translateX; - } - if (textMatrix.translateY != 0) { - ret += "\r\ntranslatey " + textMatrix.translateY; - } - if (textMatrix.hasScale) { - ret += "\r\nscalex " + textMatrix.scaleX; - ret += "\r\nscaley " + textMatrix.scaleY; - } - if (textMatrix.hasRotate) { - ret += "\r\nrotateskew0 " + textMatrix.rotateSkew0; - ret += "\r\nrotateskew1 " + textMatrix.rotateSkew1; - } - ret += "\r\n]"; - for (TEXTRECORD rec : textRecords) { - String params = ""; - if (rec.styleFlagsHasFont) { - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == rec.fontId) { - fnt = ((FontTag) t); - break; - } - } - } - params += "\r\nfont " + rec.fontId + "\r\nheight " + rec.textHeight; - } - if (rec.styleFlagsHasColor) { - params += "\r\ncolor " + rec.textColorA.toHexARGB(); - } - if (rec.styleFlagsHasXOffset) { - params += "\r\nx " + rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - params += "\r\ny " + rec.yOffset; - } - if (params.length() > 0) { - ret += "[" + params + "\r\n]"; - } - ret += Helper.escapeString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"); - } - return ret; - } - - @Override - public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { - List oldTextRecords = textRecords; - try { - TextLexer lexer = new TextLexer(new StringReader(formattedText)); - ParsedSymbol s = null; - textRecords = new ArrayList<>(); - RGBA colorA = null; - int fontId = -1; - int textHeight = -1; - FontTag font = null; - String fontName = null; - Integer x = null; - Integer y = null; - int currentX = 0; - int currentY = 0; - int maxX = Integer.MIN_VALUE; - int minX = Integer.MAX_VALUE; - MATRIX textMatrix = new MATRIX(); - textMatrix.hasRotate = false; - textMatrix.hasScale = false; - RECT textBounds = new RECT(); - int textIdx = 0; - while ((s = lexer.yylex()) != null) { - switch (s.type) { - case PARAMETER: - String paramName = (String) s.values[0]; - String paramValue = (String) s.values[1]; - if (paramName.equals("color")) { - Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); - if (m.matches()) { - colorA = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); - } else { - throw new ParseException("Invalid color. Valid format is #aarrggbb.", lexer.yyline()); - } - } - switch (paramName) { - case "font": - try { - fontId = Integer.parseInt(paramValue); - - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == fontId) { - font = (FontTag) t; - fontName = font.getSystemFontName(); - break; - } - } - } - if (font == null) { - throw new ParseException("Font not found", lexer.yyline()); - } - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid font id - number expected.", lexer.yyline()); - } - break; - case "height": - try { - textHeight = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid font height - number expected.", lexer.yyline()); - } - break; - case "x": - try { - x = Integer.parseInt(paramValue); - currentX = x; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid x position - number expected.", lexer.yyline()); - } - break; - case "y": - try { - y = Integer.parseInt(paramValue); - currentY = y; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid y position - number expected.", lexer.yyline()); - } - break; - case "xmin": - try { - textBounds.Xmin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmin position - number expected.", lexer.yyline()); - } - break; - case "xmax": - try { - textBounds.Xmax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmax position - number expected.", lexer.yyline()); - } - break; - case "ymin": - try { - textBounds.Ymin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymin position - number expected.", lexer.yyline()); - } - break; - case "ymax": - try { - textBounds.Ymax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymax position - number expected.", lexer.yyline()); - } - break; - case "scalex": - try { - textMatrix.scaleX = Integer.parseInt(paramValue); - textMatrix.hasScale = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); - } - break; - case "scaley": - try { - textMatrix.scaleY = Integer.parseInt(paramValue); - textMatrix.hasScale = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); - } - break; - case "rotateskew0": - try { - textMatrix.rotateSkew0 = Integer.parseInt(paramValue); - textMatrix.hasRotate = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid rotateskew0 value - number expected.", lexer.yyline()); - } - break; - case "rotateskew1": - try { - textMatrix.rotateSkew1 = Integer.parseInt(paramValue); - textMatrix.hasRotate = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid rotateskew1 value - number expected.", lexer.yyline()); - } - break; - case "translatex": - try { - textMatrix.translateX = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid translatex value - number expected.", lexer.yyline()); - } - break; - case "translatey": - try { - textMatrix.translateY = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid translatey value - number expected.", lexer.yyline()); - } - break; - } - break; - case TEXT: - if (font == null) { - throw new ParseException("Font not defined", lexer.yyline()); - } - TEXTRECORD tr = new TEXTRECORD(); - textRecords.add(tr); - if (fontId > -1) { - tr.fontId = fontId; - tr.textHeight = textHeight; - fontId = -1; - tr.styleFlagsHasFont = true; - } - if (colorA != null) { - tr.textColorA = colorA; - tr.styleFlagsHasColor = true; - colorA = null; - } - if (x != null) { - tr.xOffset = x; - tr.styleFlagsHasXOffset = true; - x = null; - } - if (y != null) { - tr.yOffset = y; - tr.styleFlagsHasYOffset = true; - y = null; - } - String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; - tr.glyphEntries = new GLYPHENTRY[txt.length()]; - for (int i = 0; i < txt.length(); i++) { - char c = txt.charAt(i); - Character nextChar = null; - if (i + 1 < txt.length()) { - nextChar = txt.charAt(i + 1); - } - - if (!font.containsChar(c)) { - if (!missingCharHandler.handle(font, c)) { - return false; - } - } - if (nextChar != null && !font.containsChar(nextChar)) { - if (!missingCharHandler.handle(font, nextChar)) { - return false; - } - } - tr.glyphEntries[i] = new GLYPHENTRY(); - tr.glyphEntries[i].glyphIndex = font.charToGlyph(c); - - int advance; - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextChar != null) { - kerningAdjustment = font.getGlyphKerningAdjustment(tr.glyphEntries[i].glyphIndex, font.charToGlyph(nextChar)); - } - advance = (int) Math.round(font.getDivider() * Math.round(((double) textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); - } else { - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); - } - tr.glyphEntries[i].glyphAdvance = advance; - - currentX += advance; - - } - if (currentX > maxX) { - maxX = currentX; - } - if (currentX < minX) { - minX = currentX; - } - break; - } - - } - this.textRecords = textRecords; - this.textBounds = textBounds; - //this.textBounds.Xmin = minX; - //this.textBounds.Xmax = maxX; - } catch (IOException ex) { - textRecords = oldTextRecords; - return false; - } catch (ParseException ex) { - textRecords = oldTextRecords; - throw ex; - } - return true; - } - - @Override - public RECT getRect() { - return textBounds; - } - - @Override - public int getCharacterId() { - return characterID; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeRECT(textBounds); - sos.writeMatrix(textMatrix); - - int glyphBits = 0; - int advanceBits = 0; - for (TEXTRECORD tr : textRecords) { - for (GLYPHENTRY ge : tr.glyphEntries) { - glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex); - advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance); - } - } - sos.writeUI8(glyphBits); - sos.writeUI8(advanceBits); - for (TEXTRECORD tr : textRecords) { - sos.writeTEXTRECORD(tr, true, glyphBits, advanceBits); - } - sos.writeUI8(0); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineText2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineText2", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterID = sis.readUI16(); - textBounds = sis.readRECT(); - textMatrix = sis.readMatrix(); - int glyphBits = sis.readUI8(); - int advanceBits = sis.readUI8(); - textRecords = new ArrayList<>(); - TEXTRECORD tr; - while ((tr = sis.readTEXTRECORD(true, glyphBits, advanceBits)) != null) { - textRecords.add(tr); - } - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - for (TEXTRECORD tr : textRecords) { - if (tr.styleFlagsHasFont) { - ret.add(tr.fontId); - } - } - return ret; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - staticTextToImage(swf, textRecords, 2, image, getTextMatrix(), transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - staticTextToSVG(swf, textRecords, 2, exporter, getRect(), getTextMatrix(), colorTransform); - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 2, textBounds, textMatrix, new ColorTransform()); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.ParseException; +import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; +import com.jpexs.decompiler.flash.tags.text.TextLexer; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * + * @author JPEXS + */ +public class DefineText2Tag extends TextTag { + + @SWFType(BasicType.UI16) + public int characterID; + public RECT textBounds; + public MATRIX textMatrix; + public List textRecords; + public static final int ID = 33; + + @Override + public RECT getBounds() { + return textBounds; + } + + @Override + public MATRIX getTextMatrix() { + return textMatrix; + } + + @Override + public void setBounds(RECT r) { + textBounds = r; + } + + @Override + public String getText(String separator) { + FontTag fnt = null; + String ret = ""; + boolean first = true; + for (TEXTRECORD rec : textRecords) { + if (!first) { + ret += Helper.newLine + separator + Helper.newLine; + } + first = false; + if (rec.styleFlagsHasFont) { + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == rec.fontId) { + fnt = ((FontTag) t); + break; + } + } + } + } + if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) { + /*if (!ret.isEmpty()) { + ret += "\r\n"; + }*/ + } + ret += rec.getText(fnt); + } + return ret; + } + + @Override + public List getFontIds() { + List ret = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasFont) { + ret.add(rec.fontId); + } + } + return ret; + } + + @Override + public String getFormattedText() { + FontTag fnt = null; + String ret = ""; + ret += "[\r\nxmin " + textBounds.Xmin + "\r\nymin " + textBounds.Ymin + "\r\nxmax " + textBounds.Xmax + "\r\nymax " + textBounds.Ymax; + if (textMatrix.translateX != 0) { + ret += "\r\ntranslatex " + textMatrix.translateX; + } + if (textMatrix.translateY != 0) { + ret += "\r\ntranslatey " + textMatrix.translateY; + } + if (textMatrix.hasScale) { + ret += "\r\nscalex " + textMatrix.scaleX; + ret += "\r\nscaley " + textMatrix.scaleY; + } + if (textMatrix.hasRotate) { + ret += "\r\nrotateskew0 " + textMatrix.rotateSkew0; + ret += "\r\nrotateskew1 " + textMatrix.rotateSkew1; + } + ret += "\r\n]"; + for (TEXTRECORD rec : textRecords) { + String params = ""; + if (rec.styleFlagsHasFont) { + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == rec.fontId) { + fnt = ((FontTag) t); + break; + } + } + } + params += "\r\nfont " + rec.fontId + "\r\nheight " + rec.textHeight; + } + if (rec.styleFlagsHasColor) { + params += "\r\ncolor " + rec.textColorA.toHexARGB(); + } + if (rec.styleFlagsHasXOffset) { + params += "\r\nx " + rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + params += "\r\ny " + rec.yOffset; + } + if (params.length() > 0) { + ret += "[" + params + "\r\n]"; + } + ret += Helper.escapeString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"); + } + return ret; + } + + @Override + public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { + List oldTextRecords = textRecords; + try { + TextLexer lexer = new TextLexer(new StringReader(formattedText)); + ParsedSymbol s = null; + textRecords = new ArrayList<>(); + RGBA colorA = null; + int fontId = -1; + int textHeight = -1; + FontTag font = null; + String fontName = null; + Integer x = null; + Integer y = null; + int currentX = 0; + int currentY = 0; + int maxX = Integer.MIN_VALUE; + int minX = Integer.MAX_VALUE; + MATRIX textMatrix = new MATRIX(); + textMatrix.hasRotate = false; + textMatrix.hasScale = false; + RECT textBounds = new RECT(); + int textIdx = 0; + while ((s = lexer.yylex()) != null) { + switch (s.type) { + case PARAMETER: + String paramName = (String) s.values[0]; + String paramValue = (String) s.values[1]; + if (paramName.equals("color")) { + Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); + if (m.matches()) { + colorA = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); + } else { + throw new ParseException("Invalid color. Valid format is #aarrggbb.", lexer.yyline()); + } + } + switch (paramName) { + case "font": + try { + fontId = Integer.parseInt(paramValue); + + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == fontId) { + font = (FontTag) t; + fontName = font.getSystemFontName(); + break; + } + } + } + if (font == null) { + throw new ParseException("Font not found", lexer.yyline()); + } + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid font id - number expected.", lexer.yyline()); + } + break; + case "height": + try { + textHeight = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid font height - number expected.", lexer.yyline()); + } + break; + case "x": + try { + x = Integer.parseInt(paramValue); + currentX = x; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid x position - number expected.", lexer.yyline()); + } + break; + case "y": + try { + y = Integer.parseInt(paramValue); + currentY = y; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid y position - number expected.", lexer.yyline()); + } + break; + case "xmin": + try { + textBounds.Xmin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmin position - number expected.", lexer.yyline()); + } + break; + case "xmax": + try { + textBounds.Xmax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmax position - number expected.", lexer.yyline()); + } + break; + case "ymin": + try { + textBounds.Ymin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymin position - number expected.", lexer.yyline()); + } + break; + case "ymax": + try { + textBounds.Ymax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymax position - number expected.", lexer.yyline()); + } + break; + case "scalex": + try { + textMatrix.scaleX = Integer.parseInt(paramValue); + textMatrix.hasScale = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); + } + break; + case "scaley": + try { + textMatrix.scaleY = Integer.parseInt(paramValue); + textMatrix.hasScale = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); + } + break; + case "rotateskew0": + try { + textMatrix.rotateSkew0 = Integer.parseInt(paramValue); + textMatrix.hasRotate = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid rotateskew0 value - number expected.", lexer.yyline()); + } + break; + case "rotateskew1": + try { + textMatrix.rotateSkew1 = Integer.parseInt(paramValue); + textMatrix.hasRotate = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid rotateskew1 value - number expected.", lexer.yyline()); + } + break; + case "translatex": + try { + textMatrix.translateX = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid translatex value - number expected.", lexer.yyline()); + } + break; + case "translatey": + try { + textMatrix.translateY = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid translatey value - number expected.", lexer.yyline()); + } + break; + } + break; + case TEXT: + if (font == null) { + throw new ParseException("Font not defined", lexer.yyline()); + } + TEXTRECORD tr = new TEXTRECORD(); + textRecords.add(tr); + if (fontId > -1) { + tr.fontId = fontId; + tr.textHeight = textHeight; + fontId = -1; + tr.styleFlagsHasFont = true; + } + if (colorA != null) { + tr.textColorA = colorA; + tr.styleFlagsHasColor = true; + colorA = null; + } + if (x != null) { + tr.xOffset = x; + tr.styleFlagsHasXOffset = true; + x = null; + } + if (y != null) { + tr.yOffset = y; + tr.styleFlagsHasYOffset = true; + y = null; + } + String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; + tr.glyphEntries = new GLYPHENTRY[txt.length()]; + for (int i = 0; i < txt.length(); i++) { + char c = txt.charAt(i); + Character nextChar = null; + if (i + 1 < txt.length()) { + nextChar = txt.charAt(i + 1); + } + + if (!font.containsChar(c)) { + if (!missingCharHandler.handle(font, c)) { + return false; + } + } + if (nextChar != null && !font.containsChar(nextChar)) { + if (!missingCharHandler.handle(font, nextChar)) { + return false; + } + } + tr.glyphEntries[i] = new GLYPHENTRY(); + tr.glyphEntries[i].glyphIndex = font.charToGlyph(c); + + int advance; + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextChar != null) { + kerningAdjustment = font.getGlyphKerningAdjustment(tr.glyphEntries[i].glyphIndex, font.charToGlyph(nextChar)); + } + advance = (int) Math.round(font.getDivider() * Math.round(((double) textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); + } else { + advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); + } + tr.glyphEntries[i].glyphAdvance = advance; + + currentX += advance; + + } + if (currentX > maxX) { + maxX = currentX; + } + if (currentX < minX) { + minX = currentX; + } + break; + } + + } + this.textRecords = textRecords; + this.textBounds = textBounds; + //this.textBounds.Xmin = minX; + //this.textBounds.Xmax = maxX; + } catch (IOException ex) { + textRecords = oldTextRecords; + return false; + } catch (ParseException ex) { + textRecords = oldTextRecords; + throw ex; + } + return true; + } + + @Override + public RECT getRect() { + return textBounds; + } + + @Override + public int getCharacterId() { + return characterID; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeRECT(textBounds); + sos.writeMatrix(textMatrix); + + int glyphBits = 0; + int advanceBits = 0; + for (TEXTRECORD tr : textRecords) { + for (GLYPHENTRY ge : tr.glyphEntries) { + glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex); + advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance); + } + } + sos.writeUI8(glyphBits); + sos.writeUI8(advanceBits); + for (TEXTRECORD tr : textRecords) { + sos.writeTEXTRECORD(tr, true, glyphBits, advanceBits); + } + sos.writeUI8(0); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineText2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineText2", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterID = sis.readUI16(); + textBounds = sis.readRECT(); + textMatrix = sis.readMatrix(); + int glyphBits = sis.readUI8(); + int advanceBits = sis.readUI8(); + textRecords = new ArrayList<>(); + TEXTRECORD tr; + while ((tr = sis.readTEXTRECORD(true, glyphBits, advanceBits)) != null) { + textRecords.add(tr); + } + } + + @Override + public void getNeededCharacters(Set needed) { + for (TEXTRECORD tr : textRecords) { + if (tr.styleFlagsHasFont) { + needed.add(tr.fontId); + } + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (TEXTRECORD tr : textRecords) { + if (tr.fontId == characterId) { + tr.styleFlagsHasFont = false; + tr.fontId = 0; + modified = true; + } + } + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + staticTextToImage(swf, textRecords, 2, image, getTextMatrix(), transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + staticTextToSVG(swf, textRecords, 2, exporter, getRect(), getTextMatrix(), colorTransform); + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 2, textBounds, textMatrix, new ColorTransform()); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java b/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java index e95825156..f53b47994 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineTextTag.java @@ -1,536 +1,549 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.text.ParseException; -import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; -import com.jpexs.decompiler.flash.tags.text.TextLexer; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * - * @author JPEXS - */ -public class DefineTextTag extends TextTag { - - @SWFType(BasicType.UI16) - public int characterID; - public RECT textBounds; - public MATRIX textMatrix; - public List textRecords; - public static final int ID = 11; - - @Override - public MATRIX getTextMatrix() { - return textMatrix; - } - - @Override - public RECT getBounds() { - return textBounds; - } - - @Override - public void setBounds(RECT r) { - textBounds = r; - } - - @Override - public String getText(String separator) { - FontTag fnt = null; - String ret = ""; - boolean first = true; - for (TEXTRECORD rec : textRecords) { - if (!first) { - ret += Helper.newLine + separator + Helper.newLine; - } - first = false; - if (rec.styleFlagsHasFont) { - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == rec.fontId) { - fnt = ((FontTag) t); - break; - } - } - } - } - if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) { - /*if (!ret.isEmpty()) { - ret += "\r\n"; - }*/ - } - ret += rec.getText(fnt); - } - return ret; - } - - @Override - public List getFontIds() { - List ret = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasFont) { - ret.add(rec.fontId); - } - } - return ret; - } - - @Override - public String getFormattedText() { - FontTag fnt = null; - String ret = ""; - ret += "[\r\nxmin " + textBounds.Xmin + "\r\nymin " + textBounds.Ymin + "\r\nxmax " + textBounds.Xmax + "\r\nymax " + textBounds.Ymax; - if (textMatrix.translateX != 0) { - ret += "\r\ntranslatex " + textMatrix.translateX; - } - if (textMatrix.translateY != 0) { - ret += "\r\ntranslatey " + textMatrix.translateY; - } - if (textMatrix.hasScale) { - ret += "\r\nscalex " + textMatrix.scaleX; - ret += "\r\nscaley " + textMatrix.scaleY; - } - if (textMatrix.hasRotate) { - ret += "\r\nrotateskew0 " + textMatrix.rotateSkew0; - ret += "\r\nrotateskew1 " + textMatrix.rotateSkew1; - } - ret += "\r\n]"; - for (TEXTRECORD rec : textRecords) { - String params = ""; - if (rec.styleFlagsHasFont) { - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == rec.fontId) { - fnt = ((FontTag) t); - break; - } - } - } - params += "\r\nfont " + rec.fontId + "\r\nheight " + rec.textHeight; - } - if (rec.styleFlagsHasColor) { - params += "\r\ncolor " + rec.textColor.toHexRGB(); - } - if (rec.styleFlagsHasXOffset) { - params += "\r\nx " + rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - params += "\r\ny " + rec.yOffset; - } - if (params.length() > 0) { - ret += "[" + params + "\r\n]"; - } - - if (fnt == null) { - ret += AppStrings.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)); - } else { - ret += Helper.escapeString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"); - } - } - return ret; - } - - @Override - public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { - List oldTextRecords = textRecords; - try { - TextLexer lexer = new TextLexer(new StringReader(formattedText)); - ParsedSymbol s = null; - textRecords = new ArrayList<>(); - RGB color = null; - int fontId = -1; - int textHeight = -1; - FontTag font = null; - String fontName = null; - Integer x = null; - Integer y = null; - int currentX = 0; - int currentY = 0; - int maxX = Integer.MIN_VALUE; - int minX = Integer.MAX_VALUE; - MATRIX textMatrix = new MATRIX(); - textMatrix.hasRotate = false; - textMatrix.hasScale = false; - RECT textBounds = new RECT(); - int textIdx = 0; - while ((s = lexer.yylex()) != null) { - switch (s.type) { - case PARAMETER: - String paramName = (String) s.values[0]; - String paramValue = (String) s.values[1]; - switch (paramName) { - case "color": - Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); - if (m.matches()) { - color = new RGB(Integer.parseInt(m.group(1), 16), Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16)); - } else { - throw new ParseException("Invalid color. Valid format is #rrggbb.", lexer.yyline()); - } - break; - case "font": - try { - fontId = Integer.parseInt(paramValue); - - for (Tag t : swf.tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == fontId) { - font = (FontTag) t; - fontName = font.getSystemFontName(); - break; - } - } - } - if (font == null) { - throw new ParseException("Font not found", lexer.yyline()); - } - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid font id - number expected.", lexer.yyline()); - } - break; - case "height": - try { - textHeight = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid font height - number expected.", lexer.yyline()); - } - break; - case "x": - try { - x = Integer.parseInt(paramValue); - currentX = x; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid x position - number expected.", lexer.yyline()); - } - break; - case "y": - try { - y = Integer.parseInt(paramValue); - currentY = y; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid y position - number expected.", lexer.yyline()); - } - break; - case "xmin": - try { - textBounds.Xmin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmin position - number expected.", lexer.yyline()); - } - break; - case "xmax": - try { - textBounds.Xmax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid xmax position - number expected.", lexer.yyline()); - } - break; - case "ymin": - try { - textBounds.Ymin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymin position - number expected.", lexer.yyline()); - } - break; - case "ymax": - try { - textBounds.Ymax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid ymax position - number expected.", lexer.yyline()); - } - break; - case "scalex": - try { - textMatrix.scaleX = Integer.parseInt(paramValue); - textMatrix.hasScale = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); - } - break; - case "scaley": - try { - textMatrix.scaleY = Integer.parseInt(paramValue); - textMatrix.hasScale = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); - } - break; - case "rotateskew0": - try { - textMatrix.rotateSkew0 = Integer.parseInt(paramValue); - textMatrix.hasRotate = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid rotateskew0 value - number expected.", lexer.yyline()); - } - break; - case "rotateskew1": - try { - textMatrix.rotateSkew1 = Integer.parseInt(paramValue); - textMatrix.hasRotate = true; - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid rotateskew1 value - number expected.", lexer.yyline()); - } - break; - case "translatex": - try { - textMatrix.translateX = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid translatex value - number expected.", lexer.yyline()); - } - break; - case "translatey": - try { - textMatrix.translateY = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new ParseException("Invalid translatey value - number expected.", lexer.yyline()); - } - break; - } - break; - case TEXT: - if (font == null) { - throw new ParseException("Font not defined", lexer.yyline()); - } - TEXTRECORD tr = new TEXTRECORD(); - textRecords.add(tr); - if (fontId > -1) { - tr.fontId = fontId; - tr.textHeight = textHeight; - fontId = -1; - tr.styleFlagsHasFont = true; - } - if (color != null) { - tr.textColor = color; - tr.styleFlagsHasColor = true; - color = null; - } - if (x != null) { - tr.xOffset = x; - tr.styleFlagsHasXOffset = true; - x = null; - } - if (y != null) { - tr.yOffset = y; - tr.styleFlagsHasYOffset = true; - y = null; - } - String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; - tr.glyphEntries = new GLYPHENTRY[txt.length()]; - for (int i = 0; i < txt.length(); i++) { - char c = txt.charAt(i); - Character nextChar = null; - if (i + 1 < txt.length()) { - nextChar = txt.charAt(i + 1); - } - - if (!font.containsChar(c)) { - if (!missingCharHandler.handle(font, c)) { - return false; - } - } - if (nextChar != null && !font.containsChar(nextChar)) { - if (!missingCharHandler.handle(font, nextChar)) { - return false; - } - } - tr.glyphEntries[i] = new GLYPHENTRY(); - tr.glyphEntries[i].glyphIndex = font.charToGlyph(c); - - int advance; - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextChar != null) { - kerningAdjustment = font.getGlyphKerningAdjustment(tr.glyphEntries[i].glyphIndex, font.charToGlyph(nextChar)); - } - advance = (int) Math.round(font.getDivider() * Math.round(((double) textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); - } else { - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); - } - tr.glyphEntries[i].glyphAdvance = advance; - - currentX += advance; - - } - - if (currentX > maxX) { - maxX = currentX; - } - if (currentX < minX) { - minX = currentX; - } - break; - } - - } - this.textMatrix = textMatrix; - this.textBounds = textBounds; - //this.textBounds.Xmin = minX; - //this.textBounds.Xmax = maxX; - } catch (IOException ex) { - textRecords = oldTextRecords; - return false; - } catch (ParseException ex) { - textRecords = oldTextRecords; - throw ex; - } - return true; - } - - @Override - public int getCharacterId() { - return characterID; - } - - public DefineTextTag(SWF swf, int characterID, RECT textBounds, MATRIX textMatrix, List textRecords) { - super(swf, ID, "DefineText", null, null, 0); - this.characterID = characterID; - this.textBounds = textBounds; - this.textMatrix = textMatrix; - this.textRecords = textRecords; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterID); - sos.writeRECT(textBounds); - sos.writeMatrix(textMatrix); - - int glyphBits = 0; - int advanceBits = 0; - for (TEXTRECORD tr : textRecords) { - for (GLYPHENTRY ge : tr.glyphEntries) { - glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex); - advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance); - } - } - - sos.writeUI8(glyphBits); - sos.writeUI8(advanceBits); - for (TEXTRECORD tr : textRecords) { - sos.writeTEXTRECORD(tr, false, glyphBits, advanceBits); - } - sos.writeUI8(0); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DefineTextTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DefineText", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterID = sis.readUI16(); - textBounds = sis.readRECT(); - textMatrix = sis.readMatrix(); - int glyphBits = sis.readUI8(); - int advanceBits = sis.readUI8(); - textRecords = new ArrayList<>(); - TEXTRECORD tr; - while ((tr = sis.readTEXTRECORD(false, glyphBits, advanceBits)) != null) { - textRecords.add(tr); - } - } - - @Override - public RECT getRect() { - return textBounds; - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - for (TEXTRECORD tr : textRecords) { - if (tr.styleFlagsHasFont) { - ret.add(tr.fontId); - } - } - return ret; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - staticTextToImage(swf, textRecords, 1, image, getTextMatrix(), transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - staticTextToSVG(swf, textRecords, 1, exporter, getRect(), getTextMatrix(), colorTransform); - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 1, textBounds, textMatrix, new ColorTransform()); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.ParseException; +import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; +import com.jpexs.decompiler.flash.tags.text.TextLexer; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * + * @author JPEXS + */ +public class DefineTextTag extends TextTag { + + @SWFType(BasicType.UI16) + public int characterID; + public RECT textBounds; + public MATRIX textMatrix; + public List textRecords; + public static final int ID = 11; + + @Override + public MATRIX getTextMatrix() { + return textMatrix; + } + + @Override + public RECT getBounds() { + return textBounds; + } + + @Override + public void setBounds(RECT r) { + textBounds = r; + } + + @Override + public String getText(String separator) { + FontTag fnt = null; + String ret = ""; + boolean first = true; + for (TEXTRECORD rec : textRecords) { + if (!first) { + ret += Helper.newLine + separator + Helper.newLine; + } + first = false; + if (rec.styleFlagsHasFont) { + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == rec.fontId) { + fnt = ((FontTag) t); + break; + } + } + } + } + if (rec.styleFlagsHasXOffset || rec.styleFlagsHasYOffset) { + /*if (!ret.isEmpty()) { + ret += "\r\n"; + }*/ + } + ret += rec.getText(fnt); + } + return ret; + } + + @Override + public List getFontIds() { + List ret = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasFont) { + ret.add(rec.fontId); + } + } + return ret; + } + + @Override + public String getFormattedText() { + FontTag fnt = null; + String ret = ""; + ret += "[\r\nxmin " + textBounds.Xmin + "\r\nymin " + textBounds.Ymin + "\r\nxmax " + textBounds.Xmax + "\r\nymax " + textBounds.Ymax; + if (textMatrix.translateX != 0) { + ret += "\r\ntranslatex " + textMatrix.translateX; + } + if (textMatrix.translateY != 0) { + ret += "\r\ntranslatey " + textMatrix.translateY; + } + if (textMatrix.hasScale) { + ret += "\r\nscalex " + textMatrix.scaleX; + ret += "\r\nscaley " + textMatrix.scaleY; + } + if (textMatrix.hasRotate) { + ret += "\r\nrotateskew0 " + textMatrix.rotateSkew0; + ret += "\r\nrotateskew1 " + textMatrix.rotateSkew1; + } + ret += "\r\n]"; + for (TEXTRECORD rec : textRecords) { + String params = ""; + if (rec.styleFlagsHasFont) { + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == rec.fontId) { + fnt = ((FontTag) t); + break; + } + } + } + params += "\r\nfont " + rec.fontId + "\r\nheight " + rec.textHeight; + } + if (rec.styleFlagsHasColor) { + params += "\r\ncolor " + rec.textColor.toHexRGB(); + } + if (rec.styleFlagsHasXOffset) { + params += "\r\nx " + rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + params += "\r\ny " + rec.yOffset; + } + if (params.length() > 0) { + ret += "[" + params + "\r\n]"; + } + + if (fnt == null) { + ret += AppStrings.translate("fontNotFound").replace("%fontId%", Integer.toString(rec.fontId)); + } else { + ret += Helper.escapeString(rec.getText(fnt)).replace("[", "\\[").replace("]", "\\]"); + } + } + return ret; + } + + @Override + public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException { + List oldTextRecords = textRecords; + try { + TextLexer lexer = new TextLexer(new StringReader(formattedText)); + ParsedSymbol s = null; + textRecords = new ArrayList<>(); + RGB color = null; + int fontId = -1; + int textHeight = -1; + FontTag font = null; + String fontName = null; + Integer x = null; + Integer y = null; + int currentX = 0; + int currentY = 0; + int maxX = Integer.MIN_VALUE; + int minX = Integer.MAX_VALUE; + MATRIX textMatrix = new MATRIX(); + textMatrix.hasRotate = false; + textMatrix.hasScale = false; + RECT textBounds = new RECT(); + int textIdx = 0; + while ((s = lexer.yylex()) != null) { + switch (s.type) { + case PARAMETER: + String paramName = (String) s.values[0]; + String paramValue = (String) s.values[1]; + switch (paramName) { + case "color": + Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); + if (m.matches()) { + color = new RGB(Integer.parseInt(m.group(1), 16), Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16)); + } else { + throw new ParseException("Invalid color. Valid format is #rrggbb.", lexer.yyline()); + } + break; + case "font": + try { + fontId = Integer.parseInt(paramValue); + + for (Tag t : swf.tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == fontId) { + font = (FontTag) t; + fontName = font.getSystemFontName(); + break; + } + } + } + if (font == null) { + throw new ParseException("Font not found", lexer.yyline()); + } + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid font id - number expected.", lexer.yyline()); + } + break; + case "height": + try { + textHeight = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid font height - number expected.", lexer.yyline()); + } + break; + case "x": + try { + x = Integer.parseInt(paramValue); + currentX = x; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid x position - number expected.", lexer.yyline()); + } + break; + case "y": + try { + y = Integer.parseInt(paramValue); + currentY = y; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid y position - number expected.", lexer.yyline()); + } + break; + case "xmin": + try { + textBounds.Xmin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmin position - number expected.", lexer.yyline()); + } + break; + case "xmax": + try { + textBounds.Xmax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid xmax position - number expected.", lexer.yyline()); + } + break; + case "ymin": + try { + textBounds.Ymin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymin position - number expected.", lexer.yyline()); + } + break; + case "ymax": + try { + textBounds.Ymax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid ymax position - number expected.", lexer.yyline()); + } + break; + case "scalex": + try { + textMatrix.scaleX = Integer.parseInt(paramValue); + textMatrix.hasScale = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); + } + break; + case "scaley": + try { + textMatrix.scaleY = Integer.parseInt(paramValue); + textMatrix.hasScale = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid scalex value - number expected.", lexer.yyline()); + } + break; + case "rotateskew0": + try { + textMatrix.rotateSkew0 = Integer.parseInt(paramValue); + textMatrix.hasRotate = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid rotateskew0 value - number expected.", lexer.yyline()); + } + break; + case "rotateskew1": + try { + textMatrix.rotateSkew1 = Integer.parseInt(paramValue); + textMatrix.hasRotate = true; + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid rotateskew1 value - number expected.", lexer.yyline()); + } + break; + case "translatex": + try { + textMatrix.translateX = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid translatex value - number expected.", lexer.yyline()); + } + break; + case "translatey": + try { + textMatrix.translateY = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new ParseException("Invalid translatey value - number expected.", lexer.yyline()); + } + break; + } + break; + case TEXT: + if (font == null) { + throw new ParseException("Font not defined", lexer.yyline()); + } + TEXTRECORD tr = new TEXTRECORD(); + textRecords.add(tr); + if (fontId > -1) { + tr.fontId = fontId; + tr.textHeight = textHeight; + fontId = -1; + tr.styleFlagsHasFont = true; + } + if (color != null) { + tr.textColor = color; + tr.styleFlagsHasColor = true; + color = null; + } + if (x != null) { + tr.xOffset = x; + tr.styleFlagsHasXOffset = true; + x = null; + } + if (y != null) { + tr.yOffset = y; + tr.styleFlagsHasYOffset = true; + y = null; + } + String txt = (texts == null || textIdx >= texts.length) ? (String) s.values[0] : texts[textIdx++]; + tr.glyphEntries = new GLYPHENTRY[txt.length()]; + for (int i = 0; i < txt.length(); i++) { + char c = txt.charAt(i); + Character nextChar = null; + if (i + 1 < txt.length()) { + nextChar = txt.charAt(i + 1); + } + + if (!font.containsChar(c)) { + if (!missingCharHandler.handle(font, c)) { + return false; + } + } + if (nextChar != null && !font.containsChar(nextChar)) { + if (!missingCharHandler.handle(font, nextChar)) { + return false; + } + } + tr.glyphEntries[i] = new GLYPHENTRY(); + tr.glyphEntries[i].glyphIndex = font.charToGlyph(c); + + int advance; + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextChar != null) { + kerningAdjustment = font.getGlyphKerningAdjustment(tr.glyphEntries[i].glyphIndex, font.charToGlyph(nextChar)); + } + advance = (int) Math.round(font.getDivider() * Math.round(((double) textHeight * font.getGlyphAdvance(tr.glyphEntries[i].glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); + } else { + advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor), c, nextChar)); + } + tr.glyphEntries[i].glyphAdvance = advance; + + currentX += advance; + + } + + if (currentX > maxX) { + maxX = currentX; + } + if (currentX < minX) { + minX = currentX; + } + break; + } + + } + this.textMatrix = textMatrix; + this.textBounds = textBounds; + //this.textBounds.Xmin = minX; + //this.textBounds.Xmax = maxX; + } catch (IOException ex) { + textRecords = oldTextRecords; + return false; + } catch (ParseException ex) { + textRecords = oldTextRecords; + throw ex; + } + return true; + } + + @Override + public int getCharacterId() { + return characterID; + } + + public DefineTextTag(SWF swf, int characterID, RECT textBounds, MATRIX textMatrix, List textRecords) { + super(swf, ID, "DefineText", null, null, 0); + this.characterID = characterID; + this.textBounds = textBounds; + this.textMatrix = textMatrix; + this.textRecords = textRecords; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterID); + sos.writeRECT(textBounds); + sos.writeMatrix(textMatrix); + + int glyphBits = 0; + int advanceBits = 0; + for (TEXTRECORD tr : textRecords) { + for (GLYPHENTRY ge : tr.glyphEntries) { + glyphBits = SWFOutputStream.enlargeBitCountU(glyphBits, ge.glyphIndex); + advanceBits = SWFOutputStream.enlargeBitCountS(advanceBits, ge.glyphAdvance); + } + } + + sos.writeUI8(glyphBits); + sos.writeUI8(advanceBits); + for (TEXTRECORD tr : textRecords) { + sos.writeTEXTRECORD(tr, false, glyphBits, advanceBits); + } + sos.writeUI8(0); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DefineTextTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DefineText", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterID = sis.readUI16(); + textBounds = sis.readRECT(); + textMatrix = sis.readMatrix(); + int glyphBits = sis.readUI8(); + int advanceBits = sis.readUI8(); + textRecords = new ArrayList<>(); + TEXTRECORD tr; + while ((tr = sis.readTEXTRECORD(false, glyphBits, advanceBits)) != null) { + textRecords.add(tr); + } + } + + @Override + public RECT getRect() { + return textBounds; + } + + @Override + public void getNeededCharacters(Set needed) { + for (TEXTRECORD tr : textRecords) { + if (tr.styleFlagsHasFont) { + needed.add(tr.fontId); + } + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (TEXTRECORD tr : textRecords) { + if (tr.fontId == characterId) { + tr.styleFlagsHasFont = false; + tr.fontId = 0; + modified = true; + } + } + if (modified) { + setModified(true); + } + return modified; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + staticTextToImage(swf, textRecords, 1, image, getTextMatrix(), transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + staticTextToSVG(swf, textRecords, 1, exporter, getRect(), getTextMatrix(), colorTransform); + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + return staticTextToHtmlCanvas(unitDivisor, swf, textRecords, 1, textBounds, textMatrix, new ColorTransform()); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index 450180af0..5b9b19799 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -1,197 +1,196 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Instructs Flash Player to perform a list of actions when the current frame is - * complete. - */ -public class DoActionTag extends Tag implements ASMSource { - - /** - * List of actions to perform - */ - //public List actions = new ArrayList(); - @Internal - public byte[] actionBytes; - public static final int ID = 12; - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - */ - public DoActionTag(SWF swf, byte[] headerData, byte[] data, long pos) { - super(swf, ID, "DoAction", headerData, data, pos); - actionBytes = data; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - return actionBytes;//Action.actionsToBytes(actions, true, version); - } - - /** - * Converts actions to ASM source - * - * @param actions - * @param writer - * @return ASM source - * @throws java.lang.InterruptedException - */ - @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { - if (actions == null) { - actions = getActions(); - } - return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos(), toString()/*FIXME?*/); - } - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - @Override - public boolean containsSource() { - return true; - } - - /** - * Returns string representation of the object - * - * @return String representation of the object - */ - @Override - public String toString() { - return "DoAction"; - } - - @Override - public List getActions() throws InterruptedException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int prevLength = 0; - if (previousTag != null) { - byte[] prevData = previousTag.getData(); - baos.write(prevData); - prevLength = prevData.length; - byte[] header = getHeader(data); - baos.write(header); - prevLength += header.length; - } - baos.write(actionBytes); - MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); - rri.seek(prevLength); - List list = ActionListReader.readActionListTimeout(listeners, getPos() - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); - return list; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); - return new ArrayList<>(); - } - } - - @Override - public void setActions(List actions) { - actionBytes = Action.actionsToBytes(actions, true, swf.version); - } - - @Override - public byte[] getActionBytes() { - return actionBytes; - } - - @Override - public void setActionBytes(byte[] actionBytes) { - this.actionBytes = actionBytes; - } - - @Override - public void setModified() { - setModified(true); - } - - @Override - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { - return Helper.byteArrayToHexWithHeader(writer, actionBytes); - } - - List listeners = new ArrayList<>(); - - @Override - public void addDisassemblyListener(DisassemblyListener listener) { - listeners.add(listener); - } - - @Override - public void removeDisassemblyListener(DisassemblyListener listener) { - listeners.remove(listener); - } - - @Override - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { - return writer; - } - - @Override - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { - return writer; - } - - @Override - public int getPrefixLineCount() { - return 0; - } - - @Override - public String removePrefixAndSuffix(String source) { - return source; - } - - @Override - public Tag getSourceTag() { - return this; - } - - -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Instructs Flash Player to perform a list of actions when the current frame is + * complete. + */ +public class DoActionTag extends Tag implements ASMSource { + + /** + * List of actions to perform + */ + //public List actions = new ArrayList(); + @Internal + public byte[] actionBytes; + public static final int ID = 12; + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + */ + public DoActionTag(SWF swf, byte[] headerData, byte[] data, long pos) { + super(swf, ID, "DoAction", headerData, data, pos); + actionBytes = data; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + return actionBytes;//Action.actionsToBytes(actions, true, version); + } + + /** + * Converts actions to ASM source + * + * @param actions + * @param writer + * @return ASM source + * @throws java.lang.InterruptedException + */ + @Override + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + if (actions == null) { + actions = getActions(); + } + return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos(), toString()/*FIXME?*/); + } + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + @Override + public boolean containsSource() { + return true; + } + + /** + * Returns string representation of the object + * + * @return String representation of the object + */ + @Override + public String toString() { + return "DoAction"; + } + + @Override + public List getActions() throws InterruptedException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int prevLength = 0; + if (previousTag != null) { + byte[] prevData = previousTag.getData(); + baos.write(prevData); + prevLength = prevData.length; + byte[] header = getHeader(data); + baos.write(header); + prevLength += header.length; + } + baos.write(actionBytes); + MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); + rri.seek(prevLength); + List list = ActionListReader.readActionListTimeout(listeners, getPos() - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); + return list; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); + return new ArrayList<>(); + } + } + + @Override + public void setActions(List actions) { + actionBytes = Action.actionsToBytes(actions, true, swf.version); + } + + @Override + public byte[] getActionBytes() { + return actionBytes; + } + + @Override + public void setActionBytes(byte[] actionBytes) { + this.actionBytes = actionBytes; + } + + @Override + public void setModified() { + setModified(true); + } + + @Override + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { + return Helper.byteArrayToHexWithHeader(writer, actionBytes); + } + + List listeners = new ArrayList<>(); + + @Override + public void addDisassemblyListener(DisassemblyListener listener) { + listeners.add(listener); + } + + @Override + public void removeDisassemblyListener(DisassemblyListener listener) { + listeners.remove(listener); + } + + @Override + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { + return writer; + } + + @Override + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { + return writer; + } + + @Override + public int getPrefixLineCount() { + return 0; + } + + @Override + public String removePrefixAndSuffix(String source) { + return source; + } + + @Override + public Tag getSourceTag() { + return this; + } + +} diff --git a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index 99e42e77c..57d143365 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -1,245 +1,244 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class DoInitActionTag extends CharacterIdTag implements ASMSource { - - /** - * Identifier of Sprite - */ - @SWFType(BasicType.UI16) - public int spriteId = 0; - /** - * List of actions to perform - */ - //public List actions = new ArrayList(); - @Internal - public byte[] actionBytes; - public static final int ID = 59; - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public DoInitActionTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "DoInitAction", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - spriteId = sis.readUI16(); - //actions = sis.readActionList(); - actionBytes = sis.readBytesEx(sis.available()); - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, getVersion()); - try { - sos.writeUI16(spriteId); - sos.write(actionBytes); - //sos.write(Action.actionsToBytes(actions, true, version)); - sos.close(); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - @Override - public boolean containsSource() { - return true; - } - - /** - * Converts actions to ASM source - * - * @param actions - * @param writer - * @return ASM source - * @throws java.lang.InterruptedException - */ - @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { - if (actions == null) { - actions = getActions(); - } - return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + 2, toString()/*FIXME?*/); - } - - @Override - public List getActions() throws InterruptedException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int prevLength = 0; - if (previousTag != null) { - byte[] prevData = previousTag.getData(); - baos.write(prevData); - prevLength = prevData.length; - baos.write(0); - baos.write(0); - prevLength += 2; - byte[] header = getHeader(data); - baos.write(header); - prevLength += header.length; - } - baos.write(actionBytes); - MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); - rri.seek(prevLength); - List list = ActionListReader.readActionListTimeout(listeners, getPos() + 2 - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); - return list; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); - return new ArrayList<>(); - } - } - - @Override - public void setActions(List actions) { - actionBytes = Action.actionsToBytes(actions, true, swf.version); - } - - @Override - public byte[] getActionBytes() { - return actionBytes; - } - - @Override - public void setActionBytes(byte[] actionBytes) { - this.actionBytes = actionBytes; - } - - @Override - public void setModified() { - setModified(true); - } - - @Override - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { - return Helper.byteArrayToHexWithHeader(writer, actionBytes); - } - - @Override - public int getCharacterId() { - return spriteId; - } - List listeners = new ArrayList<>(); - - @Override - public void addDisassemblyListener(DisassemblyListener listener) { - listeners.add(listener); - } - - @Override - public void removeDisassemblyListener(DisassemblyListener listener) { - listeners.remove(listener); - } - - @Override - public String getExportFileName() { - String expName = getExportName(); - if ((expName == null) || expName.isEmpty()) { - return super.toString(); - } - String[] pathParts; - if (expName.contains(".")) { - pathParts = expName.split("\\."); - } else { - pathParts = new String[]{expName}; - } - return pathParts[pathParts.length - 1]; - } - - @Override - public String toString() { - String expName = getExportName(); - if ((expName == null) || expName.isEmpty()) { - return super.toString(); - } - String[] pathParts; - if (expName.contains(".")) { - pathParts = expName.split("\\."); - } else { - pathParts = new String[]{expName}; - } - return pathParts[pathParts.length - 1]; - } - - @Override - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { - return writer; - } - - @Override - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { - return writer; - } - - @Override - public int getPrefixLineCount() { - return 0; - } - - @Override - public String removePrefixAndSuffix(String source) { - return source; - } - - @Override - public Tag getSourceTag() { - return this; - } - - -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DoInitActionTag extends CharacterIdTag implements ASMSource { + + /** + * Identifier of Sprite + */ + @SWFType(BasicType.UI16) + public int spriteId = 0; + /** + * List of actions to perform + */ + //public List actions = new ArrayList(); + @Internal + public byte[] actionBytes; + public static final int ID = 59; + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public DoInitActionTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "DoInitAction", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + spriteId = sis.readUI16(); + //actions = sis.readActionList(); + actionBytes = sis.readBytesEx(sis.available()); + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, getVersion()); + try { + sos.writeUI16(spriteId); + sos.write(actionBytes); + //sos.write(Action.actionsToBytes(actions, true, version)); + sos.close(); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + @Override + public boolean containsSource() { + return true; + } + + /** + * Converts actions to ASM source + * + * @param actions + * @param writer + * @return ASM source + * @throws java.lang.InterruptedException + */ + @Override + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + if (actions == null) { + actions = getActions(); + } + return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + 2, toString()/*FIXME?*/); + } + + @Override + public List getActions() throws InterruptedException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int prevLength = 0; + if (previousTag != null) { + byte[] prevData = previousTag.getData(); + baos.write(prevData); + prevLength = prevData.length; + baos.write(0); + baos.write(0); + prevLength += 2; + byte[] header = getHeader(data); + baos.write(header); + prevLength += header.length; + } + baos.write(actionBytes); + MemoryInputStream rri = new MemoryInputStream(baos.toByteArray()); + rri.seek(prevLength); + List list = ActionListReader.readActionListTimeout(listeners, getPos() + 2 - prevLength, rri, getVersion(), prevLength, -1, toString()/*FIXME?*/); + return list; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + Logger.getLogger(DoActionTag.class.getName()).log(Level.SEVERE, null, ex); + return new ArrayList<>(); + } + } + + @Override + public void setActions(List actions) { + actionBytes = Action.actionsToBytes(actions, true, swf.version); + } + + @Override + public byte[] getActionBytes() { + return actionBytes; + } + + @Override + public void setActionBytes(byte[] actionBytes) { + this.actionBytes = actionBytes; + } + + @Override + public void setModified() { + setModified(true); + } + + @Override + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { + return Helper.byteArrayToHexWithHeader(writer, actionBytes); + } + + @Override + public int getCharacterId() { + return spriteId; + } + List listeners = new ArrayList<>(); + + @Override + public void addDisassemblyListener(DisassemblyListener listener) { + listeners.add(listener); + } + + @Override + public void removeDisassemblyListener(DisassemblyListener listener) { + listeners.remove(listener); + } + + @Override + public String getExportFileName() { + String expName = getExportName(); + if ((expName == null) || expName.isEmpty()) { + return super.toString(); + } + String[] pathParts; + if (expName.contains(".")) { + pathParts = expName.split("\\."); + } else { + pathParts = new String[]{expName}; + } + return pathParts[pathParts.length - 1]; + } + + @Override + public String toString() { + String expName = getExportName(); + if ((expName == null) || expName.isEmpty()) { + return super.toString(); + } + String[] pathParts; + if (expName.contains(".")) { + pathParts = expName.split("\\."); + } else { + pathParts = new String[]{expName}; + } + return pathParts[pathParts.length - 1]; + } + + @Override + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { + return writer; + } + + @Override + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { + return writer; + } + + @Override + public int getPrefixLineCount() { + return 0; + } + + @Override + public String removePrefixAndSuffix(String source) { + return source; + } + + @Override + public Tag getSourceTag() { + return this; + } + +} diff --git a/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java b/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java index 0efb1428d..114afd880 100644 --- a/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/PlaceObject2Tag.java @@ -1,400 +1,403 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Extends the functionality of the PlaceObject2Tag - * - * @author JPEXS - */ -public class PlaceObject2Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { - - /** - * @since SWF 5 Has clip actions (sprite characters only) - */ - public boolean placeFlagHasClipActions; - /** - * Has clip depth - */ - public boolean placeFlagHasClipDepth; - /** - * Has name - */ - public boolean placeFlagHasName; - /** - * Has ratio - */ - public boolean placeFlagHasRatio; - /** - * Has color transform - */ - public boolean placeFlagHasColorTransform; - /** - * Has matrix - */ - public boolean placeFlagHasMatrix; - /** - * Places a character - */ - public boolean placeFlagHasCharacter; - /** - * Defines a character to be moved - */ - public boolean placeFlagMove; - /** - * Depth of character - */ - @SWFType(BasicType.UI16) - public int depth; - /** - * If PlaceFlagHasCharacter, ID of character to place - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasCharacter") - public int characterId; - /** - * If PlaceFlagHasMatrix, Transform matrix data - */ - @Conditional("placeFlagHasMatrix") - public MATRIX matrix; - /** - * If PlaceFlagHasColorTransform, Color transform data - */ - @Conditional("placeFlagHasColorTransform") - public CXFORMWITHALPHA colorTransform; - /** - * If PlaceFlagHasRatio, ratio - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasRatio") - public int ratio; - /** - * If PlaceFlagHasName, Name of character - */ - @Conditional("placeFlagHasName") - public String name; - /** - * If PlaceFlagHasClipDepth, Clip depth - */ - @Conditional("placeFlagHasClipDepth") - public int clipDepth; - /** - * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data - */ - @Conditional("placeFlagHasClipActions") - @Internal //TODO: make editable - public CLIPACTIONS clipActions; - public static final int ID = 26; - - @Override - public int getClipDepth() { - if (placeFlagHasClipDepth) { - return clipDepth; - } - return -1; - } - - @Override - public List getFilters() { - return null; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); - sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); - sos.writeUB(1, placeFlagHasName ? 1 : 0); - sos.writeUB(1, placeFlagHasRatio ? 1 : 0); - sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); - sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); - sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); - sos.writeUB(1, placeFlagMove ? 1 : 0); - sos.writeUI16(depth); - if (placeFlagHasCharacter) { - sos.writeUI16(characterId); - } - if (placeFlagHasMatrix) { - sos.writeMatrix(matrix); - } - if (placeFlagHasColorTransform) { - sos.writeCXFORMWITHALPHA(colorTransform); - } - if (placeFlagHasRatio) { - sos.writeUI16(ratio); - } - if (placeFlagHasName) { - sos.writeString(name); - } - if (placeFlagHasClipDepth) { - sos.writeUI16(clipDepth); - } - if (placeFlagHasClipActions) { - sos.writeCLIPACTIONS(clipActions); - } - sos.close(); - } catch (IOException e) { - } - return baos.toByteArray(); - } - - public PlaceObject2Tag(SWF swf, boolean placeFlagHasClipActions, boolean placeFlagHasClipDepth, boolean placeFlagHasName, boolean placeFlagHasRatio, boolean placeFlagHasColorTransform, boolean placeFlagHasMatrix, boolean placeFlagHasCharacter, boolean placeFlagMove, int depth, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, CLIPACTIONS clipActions) { - super(swf, ID, "PlaceObject2", null, null, 0); - this.placeFlagHasClipActions = placeFlagHasClipActions; - this.placeFlagHasClipDepth = placeFlagHasClipDepth; - this.placeFlagHasName = placeFlagHasName; - this.placeFlagHasRatio = placeFlagHasRatio; - this.placeFlagHasColorTransform = placeFlagHasColorTransform; - this.placeFlagHasMatrix = placeFlagHasMatrix; - this.placeFlagHasCharacter = placeFlagHasCharacter; - this.placeFlagMove = placeFlagMove; - this.depth = depth; - this.characterId = characterId; - this.matrix = matrix; - this.colorTransform = colorTransform; - this.ratio = ratio; - this.name = name; - this.clipDepth = clipDepth; - this.clipActions = clipActions; - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public PlaceObject2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "PlaceObject2", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - placeFlagHasClipActions = sis.readUB(1) == 1; - placeFlagHasClipDepth = sis.readUB(1) == 1; - placeFlagHasName = sis.readUB(1) == 1; - placeFlagHasRatio = sis.readUB(1) == 1; - placeFlagHasColorTransform = sis.readUB(1) == 1; - placeFlagHasMatrix = sis.readUB(1) == 1; - placeFlagHasCharacter = sis.readUB(1) == 1; - placeFlagMove = sis.readUB(1) == 1; - depth = sis.readUI16(); - if (placeFlagHasCharacter) { - characterId = sis.readUI16(); - } - if (placeFlagHasMatrix) { - matrix = sis.readMatrix(); - } - if (placeFlagHasColorTransform) { - colorTransform = sis.readCXFORMWITHALPHA(); - } - if (placeFlagHasRatio) { - ratio = sis.readUI16(); - } - if (placeFlagHasName) { - name = sis.readString(); - } - if (placeFlagHasClipDepth) { - clipDepth = sis.readUI16(); - } - if (placeFlagHasClipActions) { - clipActions = sis.readCLIPACTIONS(swf, this); - } - } - - /** - * Returns all sub-items - * - * @return List of sub-items - */ - @Override - public List getSubItems() { - List ret = new ArrayList<>(); - if (placeFlagHasClipActions) { - ret.addAll(clipActions.clipActionRecords); - } - return ret; - } - - /** - * Returns number of sub-items - * - * @return Number of sub-items - */ - @Override - public int getItemCount() { - if (!placeFlagHasClipActions) { - return 0; - } - return clipActions.clipActionRecords.size(); - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - if (placeFlagHasCharacter) { - ret.add(characterId); - } - return ret; - } - - @Override - public int getCharacterId() { - if (placeFlagHasCharacter) { - return characterId; - } else { - return -1; - } - } - - @Override - public int getDepth() { - return depth; - } - - @Override - public MATRIX getMatrix() { - if (placeFlagHasMatrix) { - return matrix; - } else { - return null; - } - } - - @Override - public String getInstanceName() { - if (placeFlagHasName) { - return name; - } - return null; - } - - @Override - public ColorTransform getColorTransform() { - if (placeFlagHasColorTransform) { - return colorTransform; - } else { - return null; - } - } - - @Override - public int getBlendMode() { - return 0; - } - - @Override - public boolean cacheAsBitmap() { - return false; - } - - @Override - public String getClassName() { - return null; - } - - @Override - public boolean isVisible() { - return true; - } - - @Override - public RGBA getBackgroundColor() { - return null; - } - - @Override - public boolean flagMove() { - return placeFlagMove; - } - - @Override - public int getRatio() { - if (!placeFlagHasRatio) { - return -1; - } - return ratio; - } - - @Override - public void setInstanceName(String name) { - placeFlagHasName = true; - this.name = name; - } - - @Override - public void setClassName(String className) { - //not supported - } - - @Override - public String toString() { - if (placeFlagHasName) { - return super.toString() + " (" + name + ")"; - } else { - return super.toString(); - } - } - - @Override - public CLIPACTIONS getClipActions() { - if (placeFlagHasClipActions) { - return clipActions; - } else { - return null; - } - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Extends the functionality of the PlaceObject2Tag + * + * @author JPEXS + */ +public class PlaceObject2Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { + + /** + * @since SWF 5 Has clip actions (sprite characters only) + */ + public boolean placeFlagHasClipActions; + /** + * Has clip depth + */ + public boolean placeFlagHasClipDepth; + /** + * Has name + */ + public boolean placeFlagHasName; + /** + * Has ratio + */ + public boolean placeFlagHasRatio; + /** + * Has color transform + */ + public boolean placeFlagHasColorTransform; + /** + * Has matrix + */ + public boolean placeFlagHasMatrix; + /** + * Places a character + */ + public boolean placeFlagHasCharacter; + /** + * Defines a character to be moved + */ + public boolean placeFlagMove; + /** + * Depth of character + */ + @SWFType(BasicType.UI16) + public int depth; + /** + * If PlaceFlagHasCharacter, ID of character to place + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasCharacter") + public int characterId; + /** + * If PlaceFlagHasMatrix, Transform matrix data + */ + @Conditional("placeFlagHasMatrix") + public MATRIX matrix; + /** + * If PlaceFlagHasColorTransform, Color transform data + */ + @Conditional("placeFlagHasColorTransform") + public CXFORMWITHALPHA colorTransform; + /** + * If PlaceFlagHasRatio, ratio + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasRatio") + public int ratio; + /** + * If PlaceFlagHasName, Name of character + */ + @Conditional("placeFlagHasName") + public String name; + /** + * If PlaceFlagHasClipDepth, Clip depth + */ + @Conditional("placeFlagHasClipDepth") + public int clipDepth; + /** + * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data + */ + @Conditional("placeFlagHasClipActions") + @Internal //TODO: make editable + public CLIPACTIONS clipActions; + public static final int ID = 26; + + @Override + public int getClipDepth() { + if (placeFlagHasClipDepth) { + return clipDepth; + } + return -1; + } + + @Override + public List getFilters() { + return null; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUI16(depth); + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + if (placeFlagHasClipActions) { + sos.writeCLIPACTIONS(clipActions); + } + sos.close(); + } catch (IOException e) { + } + return baos.toByteArray(); + } + + public PlaceObject2Tag(SWF swf, boolean placeFlagHasClipActions, boolean placeFlagHasClipDepth, boolean placeFlagHasName, boolean placeFlagHasRatio, boolean placeFlagHasColorTransform, boolean placeFlagHasMatrix, boolean placeFlagHasCharacter, boolean placeFlagMove, int depth, int characterId, MATRIX matrix, CXFORMWITHALPHA colorTransform, int ratio, String name, int clipDepth, CLIPACTIONS clipActions) { + super(swf, ID, "PlaceObject2", null, null, 0); + this.placeFlagHasClipActions = placeFlagHasClipActions; + this.placeFlagHasClipDepth = placeFlagHasClipDepth; + this.placeFlagHasName = placeFlagHasName; + this.placeFlagHasRatio = placeFlagHasRatio; + this.placeFlagHasColorTransform = placeFlagHasColorTransform; + this.placeFlagHasMatrix = placeFlagHasMatrix; + this.placeFlagHasCharacter = placeFlagHasCharacter; + this.placeFlagMove = placeFlagMove; + this.depth = depth; + this.characterId = characterId; + this.matrix = matrix; + this.colorTransform = colorTransform; + this.ratio = ratio; + this.name = name; + this.clipDepth = clipDepth; + this.clipActions = clipActions; + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public PlaceObject2Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "PlaceObject2", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + placeFlagHasClipActions = sis.readUB(1) == 1; + placeFlagHasClipDepth = sis.readUB(1) == 1; + placeFlagHasName = sis.readUB(1) == 1; + placeFlagHasRatio = sis.readUB(1) == 1; + placeFlagHasColorTransform = sis.readUB(1) == 1; + placeFlagHasMatrix = sis.readUB(1) == 1; + placeFlagHasCharacter = sis.readUB(1) == 1; + placeFlagMove = sis.readUB(1) == 1; + depth = sis.readUI16(); + if (placeFlagHasCharacter) { + characterId = sis.readUI16(); + } + if (placeFlagHasMatrix) { + matrix = sis.readMatrix(); + } + if (placeFlagHasColorTransform) { + colorTransform = sis.readCXFORMWITHALPHA(); + } + if (placeFlagHasRatio) { + ratio = sis.readUI16(); + } + if (placeFlagHasName) { + name = sis.readString(); + } + if (placeFlagHasClipDepth) { + clipDepth = sis.readUI16(); + } + if (placeFlagHasClipActions) { + clipActions = sis.readCLIPACTIONS(swf, this); + } + } + + /** + * Returns all sub-items + * + * @return List of sub-items + */ + @Override + public List getSubItems() { + List ret = new ArrayList<>(); + if (placeFlagHasClipActions) { + ret.addAll(clipActions.clipActionRecords); + } + return ret; + } + + /** + * Returns number of sub-items + * + * @return Number of sub-items + */ + @Override + public int getItemCount() { + if (!placeFlagHasClipActions) { + return 0; + } + return clipActions.clipActionRecords.size(); + } + + @Override + public void getNeededCharacters(Set needed) { + if (placeFlagHasCharacter) { + needed.add(characterId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + // the place object tag will be removed + return false; + } + + @Override + public int getCharacterId() { + if (placeFlagHasCharacter) { + return characterId; + } else { + return -1; + } + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public MATRIX getMatrix() { + if (placeFlagHasMatrix) { + return matrix; + } else { + return null; + } + } + + @Override + public String getInstanceName() { + if (placeFlagHasName) { + return name; + } + return null; + } + + @Override + public ColorTransform getColorTransform() { + if (placeFlagHasColorTransform) { + return colorTransform; + } else { + return null; + } + } + + @Override + public int getBlendMode() { + return 0; + } + + @Override + public boolean cacheAsBitmap() { + return false; + } + + @Override + public String getClassName() { + return null; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public RGBA getBackgroundColor() { + return null; + } + + @Override + public boolean flagMove() { + return placeFlagMove; + } + + @Override + public int getRatio() { + if (!placeFlagHasRatio) { + return -1; + } + return ratio; + } + + @Override + public void setInstanceName(String name) { + placeFlagHasName = true; + this.name = name; + } + + @Override + public void setClassName(String className) { + //not supported + } + + @Override + public String toString() { + if (placeFlagHasName) { + return super.toString() + " (" + name + ")"; + } else { + return super.toString(); + } + } + + @Override + public CLIPACTIONS getClipActions() { + if (placeFlagHasClipActions) { + return clipActions; + } else { + return null; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java b/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java index 99f2c65dc..d587d62f8 100644 --- a/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/PlaceObject3Tag.java @@ -1,530 +1,533 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.EndOfStreamException; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Extends the functionality of the PlaceObject2Tag - * - * @author JPEXS - */ -public class PlaceObject3Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { - - /** - * @since SWF 5 has clip actions (sprite characters only) - */ - public boolean placeFlagHasClipActions; - /** - * Has clip depth - */ - public boolean placeFlagHasClipDepth; - /** - * Has name - */ - public boolean placeFlagHasName; - /** - * Has ratio - */ - public boolean placeFlagHasRatio; - /** - * Has color transform - */ - public boolean placeFlagHasColorTransform; - /** - * Has matrix - */ - public boolean placeFlagHasMatrix; - /** - * Places a character - */ - public boolean placeFlagHasCharacter; - /** - * Defines a character to be moved - */ - public boolean placeFlagMove; - /** - * Has class name or character ID of bitmap to place. If - * PlaceFlagHasClassName, use ClassName. If PlaceFlagHasCharacter, use - * CharacterId - */ - public boolean placeFlagHasImage; - /** - * Has class name of object to place - */ - public boolean placeFlagHasClassName; - /** - * Enables bitmap caching - */ - public boolean placeFlagHasCacheAsBitmap; - /** - * Has blend mode - */ - public boolean placeFlagHasBlendMode; - /** - * Has filter list - */ - public boolean placeFlagHasFilterList; - /** - * Has opaque background. SWF 11 and higher. - */ - public boolean placeFlagOpaqueBackground; - /** - * Has visibility flag. SWF 11 and higher. - */ - public boolean placeFlagHasVisible; - /** - * Depth of character - */ - @SWFType(BasicType.UI16) - public int depth; - /** - * If PlaceFlagHasClassName or (PlaceFlagHasImage and - * PlaceFlagHasCharacter), Name of the class to place - */ - @Conditional("placeFlagHasClassName|(placeFlagHasImage,placeFlagHasCharacter)") - public String className; - /** - * If PlaceFlagHasCharacter, ID of character to place - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasCharacter") - public int characterId; - /** - * If PlaceFlagHasMatrix, Transform matrix data - */ - @Conditional("placeFlagHasMatrix") - public MATRIX matrix; - /** - * If PlaceFlagHasColorTransform, Color transform data - */ - @Conditional("placeFlagHasColorTransform") - public CXFORMWITHALPHA colorTransform; - /** - * If PlaceFlagHasRatio, Ratio - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasRatio") - public int ratio; - /** - * If PlaceFlagHasName, Name of character - */ - @Conditional("placeFlagHasName") - public String name; - /** - * If PlaceFlagHasClipDepth, Clip depth - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasClipDepth") - public int clipDepth; - /** - * If PlaceFlagHasFilterList, List of filters on this object - */ - @Conditional("placeFlagHasFilterList") - public List surfaceFilterList; - /** - * If PlaceFlagHasBlendMode, Blend mode - */ - @SWFType(BasicType.UI8) - @Conditional("placeFlagHasBlendMode") - public int blendMode; - /** - * If PlaceFlagHasCacheAsBitmap, 0 = Bitmap cache disabled, 1-255 = Bitmap - * cache enabled - */ - @SWFType(BasicType.UI8) - @Conditional("placeFlagHasCacheAsBitmap") - public int bitmapCache; - /** - * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data - */ - @Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5) - @Internal //TODO: make editable - public CLIPACTIONS clipActions; - /** - * If PlaceFlagHasVisible, 0 = Place invisible, 1 = Place visible - */ - @Conditional("placeFlagHasVisible") - public int visible; - /** - * If PlaceFlagHasVisible, Background color - */ - @Conditional("placeFlagOpaqueBackground") - public RGBA backgroundColor; - // FIXME bug found in ecoDrive.swf, - @Internal - private boolean bitmapCacheBug; - @Reserved - public boolean reserved; - public static final int ID = 70; - - @Override - public List getFilters() { - if (placeFlagHasFilterList) { - return surfaceFilterList; - } else { - return null; - } - } - - @Override - public int getClipDepth() { - if (placeFlagHasClipDepth) { - return clipDepth; - } - return -1; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); - sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); - sos.writeUB(1, placeFlagHasName ? 1 : 0); - sos.writeUB(1, placeFlagHasRatio ? 1 : 0); - sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); - sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); - sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); - sos.writeUB(1, placeFlagMove ? 1 : 0); - sos.writeUB(1, reserved ? 1 : 0); - sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 - sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 - sos.writeUB(1, placeFlagHasImage ? 1 : 0); - sos.writeUB(1, placeFlagHasClassName ? 1 : 0); - sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); - sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); - sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); - sos.writeUI16(depth); - - if (placeFlagHasClassName || (placeFlagHasImage&&placeFlagHasCharacter)) { - sos.writeString(className); - } - if (placeFlagHasCharacter) { - sos.writeUI16(characterId); - } - if (placeFlagHasMatrix) { - sos.writeMatrix(matrix); - } - if (placeFlagHasColorTransform) { - sos.writeCXFORMWITHALPHA(colorTransform); - } - if (placeFlagHasRatio) { - sos.writeUI16(ratio); - } - if (placeFlagHasName) { - sos.writeString(name); - } - if (placeFlagHasClipDepth) { - sos.writeUI16(clipDepth); - } - if (placeFlagHasFilterList) { - sos.writeFILTERLIST(surfaceFilterList); - } - if (placeFlagHasBlendMode) { - sos.writeUI8(blendMode); - } - if (placeFlagHasCacheAsBitmap) { - if (!bitmapCacheBug) { - sos.writeUI8(bitmapCache); - } - } - if (placeFlagHasVisible) { - sos.writeUI8(visible); - } - if (placeFlagOpaqueBackground) { - sos.writeRGBA(backgroundColor); - } - if (placeFlagHasClipActions) { - sos.writeCLIPACTIONS(clipActions); - } - sos.close(); - } catch (IOException ex) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public PlaceObject3Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "PlaceObject3", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - placeFlagHasClipActions = sis.readUB(1) == 1; - placeFlagHasClipDepth = sis.readUB(1) == 1; - placeFlagHasName = sis.readUB(1) == 1; - placeFlagHasRatio = sis.readUB(1) == 1; - placeFlagHasColorTransform = sis.readUB(1) == 1; - placeFlagHasMatrix = sis.readUB(1) == 1; - placeFlagHasCharacter = sis.readUB(1) == 1; - placeFlagMove = sis.readUB(1) == 1; - reserved = sis.readUB(1) == 1; - placeFlagOpaqueBackground = sis.readUB(1) == 1; //SWF11 - placeFlagHasVisible = sis.readUB(1) == 1; //SWF11 - placeFlagHasImage = sis.readUB(1) == 1; - placeFlagHasClassName = sis.readUB(1) == 1; - placeFlagHasCacheAsBitmap = sis.readUB(1) == 1; - placeFlagHasBlendMode = sis.readUB(1) == 1; - placeFlagHasFilterList = sis.readUB(1) == 1; - - depth = sis.readUI16(); - if (placeFlagHasClassName || (placeFlagHasImage&&placeFlagHasCharacter)) { - className = sis.readString(); - } - if (placeFlagHasCharacter) { - characterId = sis.readUI16(); - } - if (placeFlagHasMatrix) { - matrix = sis.readMatrix(); - } - if (placeFlagHasColorTransform) { - colorTransform = sis.readCXFORMWITHALPHA(); - } - if (placeFlagHasRatio) { - ratio = sis.readUI16(); - } - if (placeFlagHasName) { - name = sis.readString(); - } - if (placeFlagHasClipDepth) { - clipDepth = sis.readUI16(); - } - if (placeFlagHasFilterList) { - surfaceFilterList = sis.readFILTERLIST(); - } - if (placeFlagHasBlendMode) { - blendMode = sis.readUI8(); - } - bitmapCacheBug = false; - if (placeFlagHasCacheAsBitmap) { - try { - bitmapCache = sis.readUI8(); - } catch (EndOfStreamException eex) { - bitmapCacheBug = true; - bitmapCache = 1; - } - } - - if (placeFlagHasVisible) { - visible = sis.readUI8(); - } - if (placeFlagOpaqueBackground) { - backgroundColor = sis.readRGBA(); - } - - if (placeFlagHasClipActions) { - clipActions = sis.readCLIPACTIONS(swf, this); - } - } - - /** - * Returns all sub-items - * - * @return List of sub-items - */ - @Override - public List getSubItems() { - List ret = new ArrayList<>(); - if (placeFlagHasClipActions) { - ret.addAll(clipActions.clipActionRecords); - } - return ret; - } - - /** - * Returns number of sub-items - * - * @return Number of sub-items - */ - @Override - public int getItemCount() { - if (!placeFlagHasClipActions) { - return 0; - } - return clipActions.clipActionRecords.size(); - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - if (placeFlagHasCharacter) { - ret.add(characterId); - } - return ret; - } - - @Override - public int getCharacterId() { - if (placeFlagHasCharacter) { - return characterId; - } else { - return -1; - } - } - - @Override - public int getDepth() { - return depth; - } - - @Override - public MATRIX getMatrix() { - if (placeFlagHasMatrix) { - return matrix; - } else { - return null; - } - } - - @Override - public String getInstanceName() { - if (placeFlagHasName) { - return name; - } - return null; - } - - @Override - public ColorTransform getColorTransform() { - if (placeFlagHasColorTransform) { - return colorTransform; - } else { - return null; - } - } - - @Override - public int getBlendMode() { - return blendMode; - } - - @Override - public String getClassName() { - if (placeFlagHasClassName) { - return className; - } - return null; - } - - @Override - public boolean cacheAsBitmap() { - return placeFlagHasCacheAsBitmap; - } - - @Override - public boolean isVisible() { - if (placeFlagHasVisible) { - return visible == 1; - } - return true; - } - - @Override - public RGBA getBackgroundColor() { - if (placeFlagOpaqueBackground) { - return backgroundColor; - } - return null; - } - - @Override - public boolean flagMove() { - return placeFlagMove; - } - - @Override - public int getRatio() { - if (!placeFlagHasRatio) { - return -1; - } - return ratio; - } - - @Override - public void setInstanceName(String name) { - placeFlagHasName = true; - this.name = name; - } - - @Override - public void setClassName(String className) { - placeFlagHasClassName = true; - this.className = className; - } - - @Override - public String toString() { - if (placeFlagHasName) { - return super.toString() + " (" + name + ")"; - } else { - return super.toString(); - } - } - - @Override - public CLIPACTIONS getClipActions() { - if (placeFlagHasClipActions) { - return clipActions; - } else { - return null; - } - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.EndOfStreamException; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Extends the functionality of the PlaceObject2Tag + * + * @author JPEXS + */ +public class PlaceObject3Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { + + /** + * @since SWF 5 has clip actions (sprite characters only) + */ + public boolean placeFlagHasClipActions; + /** + * Has clip depth + */ + public boolean placeFlagHasClipDepth; + /** + * Has name + */ + public boolean placeFlagHasName; + /** + * Has ratio + */ + public boolean placeFlagHasRatio; + /** + * Has color transform + */ + public boolean placeFlagHasColorTransform; + /** + * Has matrix + */ + public boolean placeFlagHasMatrix; + /** + * Places a character + */ + public boolean placeFlagHasCharacter; + /** + * Defines a character to be moved + */ + public boolean placeFlagMove; + /** + * Has class name or character ID of bitmap to place. If + * PlaceFlagHasClassName, use ClassName. If PlaceFlagHasCharacter, use + * CharacterId + */ + public boolean placeFlagHasImage; + /** + * Has class name of object to place + */ + public boolean placeFlagHasClassName; + /** + * Enables bitmap caching + */ + public boolean placeFlagHasCacheAsBitmap; + /** + * Has blend mode + */ + public boolean placeFlagHasBlendMode; + /** + * Has filter list + */ + public boolean placeFlagHasFilterList; + /** + * Has opaque background. SWF 11 and higher. + */ + public boolean placeFlagOpaqueBackground; + /** + * Has visibility flag. SWF 11 and higher. + */ + public boolean placeFlagHasVisible; + /** + * Depth of character + */ + @SWFType(BasicType.UI16) + public int depth; + /** + * If PlaceFlagHasClassName or (PlaceFlagHasImage and + * PlaceFlagHasCharacter), Name of the class to place + */ + @Conditional("placeFlagHasClassName|(placeFlagHasImage,placeFlagHasCharacter)") + public String className; + /** + * If PlaceFlagHasCharacter, ID of character to place + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasCharacter") + public int characterId; + /** + * If PlaceFlagHasMatrix, Transform matrix data + */ + @Conditional("placeFlagHasMatrix") + public MATRIX matrix; + /** + * If PlaceFlagHasColorTransform, Color transform data + */ + @Conditional("placeFlagHasColorTransform") + public CXFORMWITHALPHA colorTransform; + /** + * If PlaceFlagHasRatio, Ratio + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasRatio") + public int ratio; + /** + * If PlaceFlagHasName, Name of character + */ + @Conditional("placeFlagHasName") + public String name; + /** + * If PlaceFlagHasClipDepth, Clip depth + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasClipDepth") + public int clipDepth; + /** + * If PlaceFlagHasFilterList, List of filters on this object + */ + @Conditional("placeFlagHasFilterList") + public List surfaceFilterList; + /** + * If PlaceFlagHasBlendMode, Blend mode + */ + @SWFType(BasicType.UI8) + @Conditional("placeFlagHasBlendMode") + public int blendMode; + /** + * If PlaceFlagHasCacheAsBitmap, 0 = Bitmap cache disabled, 1-255 = Bitmap + * cache enabled + */ + @SWFType(BasicType.UI8) + @Conditional("placeFlagHasCacheAsBitmap") + public int bitmapCache; + /** + * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data + */ + @Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5) + @Internal //TODO: make editable + public CLIPACTIONS clipActions; + /** + * If PlaceFlagHasVisible, 0 = Place invisible, 1 = Place visible + */ + @Conditional("placeFlagHasVisible") + public int visible; + /** + * If PlaceFlagHasVisible, Background color + */ + @Conditional("placeFlagOpaqueBackground") + public RGBA backgroundColor; + // FIXME bug found in ecoDrive.swf, + @Internal + private boolean bitmapCacheBug; + @Reserved + public boolean reserved; + public static final int ID = 70; + + @Override + public List getFilters() { + if (placeFlagHasFilterList) { + return surfaceFilterList; + } else { + return null; + } + } + + @Override + public int getClipDepth() { + if (placeFlagHasClipDepth) { + return clipDepth; + } + return -1; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUB(1, reserved ? 1 : 0); + sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasImage ? 1 : 0); + sos.writeUB(1, placeFlagHasClassName ? 1 : 0); + sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); + sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); + sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); + sos.writeUI16(depth); + + if (placeFlagHasClassName || (placeFlagHasImage && placeFlagHasCharacter)) { + sos.writeString(className); + } + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + if (placeFlagHasFilterList) { + sos.writeFILTERLIST(surfaceFilterList); + } + if (placeFlagHasBlendMode) { + sos.writeUI8(blendMode); + } + if (placeFlagHasCacheAsBitmap) { + if (!bitmapCacheBug) { + sos.writeUI8(bitmapCache); + } + } + if (placeFlagHasVisible) { + sos.writeUI8(visible); + } + if (placeFlagOpaqueBackground) { + sos.writeRGBA(backgroundColor); + } + if (placeFlagHasClipActions) { + sos.writeCLIPACTIONS(clipActions); + } + sos.close(); + } catch (IOException ex) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public PlaceObject3Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "PlaceObject3", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + placeFlagHasClipActions = sis.readUB(1) == 1; + placeFlagHasClipDepth = sis.readUB(1) == 1; + placeFlagHasName = sis.readUB(1) == 1; + placeFlagHasRatio = sis.readUB(1) == 1; + placeFlagHasColorTransform = sis.readUB(1) == 1; + placeFlagHasMatrix = sis.readUB(1) == 1; + placeFlagHasCharacter = sis.readUB(1) == 1; + placeFlagMove = sis.readUB(1) == 1; + reserved = sis.readUB(1) == 1; + placeFlagOpaqueBackground = sis.readUB(1) == 1; //SWF11 + placeFlagHasVisible = sis.readUB(1) == 1; //SWF11 + placeFlagHasImage = sis.readUB(1) == 1; + placeFlagHasClassName = sis.readUB(1) == 1; + placeFlagHasCacheAsBitmap = sis.readUB(1) == 1; + placeFlagHasBlendMode = sis.readUB(1) == 1; + placeFlagHasFilterList = sis.readUB(1) == 1; + + depth = sis.readUI16(); + if (placeFlagHasClassName || (placeFlagHasImage && placeFlagHasCharacter)) { + className = sis.readString(); + } + if (placeFlagHasCharacter) { + characterId = sis.readUI16(); + } + if (placeFlagHasMatrix) { + matrix = sis.readMatrix(); + } + if (placeFlagHasColorTransform) { + colorTransform = sis.readCXFORMWITHALPHA(); + } + if (placeFlagHasRatio) { + ratio = sis.readUI16(); + } + if (placeFlagHasName) { + name = sis.readString(); + } + if (placeFlagHasClipDepth) { + clipDepth = sis.readUI16(); + } + if (placeFlagHasFilterList) { + surfaceFilterList = sis.readFILTERLIST(); + } + if (placeFlagHasBlendMode) { + blendMode = sis.readUI8(); + } + bitmapCacheBug = false; + if (placeFlagHasCacheAsBitmap) { + try { + bitmapCache = sis.readUI8(); + } catch (EndOfStreamException eex) { + bitmapCacheBug = true; + bitmapCache = 1; + } + } + + if (placeFlagHasVisible) { + visible = sis.readUI8(); + } + if (placeFlagOpaqueBackground) { + backgroundColor = sis.readRGBA(); + } + + if (placeFlagHasClipActions) { + clipActions = sis.readCLIPACTIONS(swf, this); + } + } + + /** + * Returns all sub-items + * + * @return List of sub-items + */ + @Override + public List getSubItems() { + List ret = new ArrayList<>(); + if (placeFlagHasClipActions) { + ret.addAll(clipActions.clipActionRecords); + } + return ret; + } + + /** + * Returns number of sub-items + * + * @return Number of sub-items + */ + @Override + public int getItemCount() { + if (!placeFlagHasClipActions) { + return 0; + } + return clipActions.clipActionRecords.size(); + } + + @Override + public void getNeededCharacters(Set needed) { + if (placeFlagHasCharacter) { + needed.add(characterId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + // the place object tag will be removed + return false; + } + + @Override + public int getCharacterId() { + if (placeFlagHasCharacter) { + return characterId; + } else { + return -1; + } + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public MATRIX getMatrix() { + if (placeFlagHasMatrix) { + return matrix; + } else { + return null; + } + } + + @Override + public String getInstanceName() { + if (placeFlagHasName) { + return name; + } + return null; + } + + @Override + public ColorTransform getColorTransform() { + if (placeFlagHasColorTransform) { + return colorTransform; + } else { + return null; + } + } + + @Override + public int getBlendMode() { + return blendMode; + } + + @Override + public String getClassName() { + if (placeFlagHasClassName) { + return className; + } + return null; + } + + @Override + public boolean cacheAsBitmap() { + return placeFlagHasCacheAsBitmap; + } + + @Override + public boolean isVisible() { + if (placeFlagHasVisible) { + return visible == 1; + } + return true; + } + + @Override + public RGBA getBackgroundColor() { + if (placeFlagOpaqueBackground) { + return backgroundColor; + } + return null; + } + + @Override + public boolean flagMove() { + return placeFlagMove; + } + + @Override + public int getRatio() { + if (!placeFlagHasRatio) { + return -1; + } + return ratio; + } + + @Override + public void setInstanceName(String name) { + placeFlagHasName = true; + this.name = name; + } + + @Override + public void setClassName(String className) { + placeFlagHasClassName = true; + this.className = className; + } + + @Override + public String toString() { + if (placeFlagHasName) { + return super.toString() + " (" + name + ")"; + } else { + return super.toString(); + } + } + + @Override + public CLIPACTIONS getClipActions() { + if (placeFlagHasClipActions) { + return clipActions; + } else { + return null; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java b/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java index 173006eda..e1b48bad3 100644 --- a/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/PlaceObject4Tag.java @@ -1,532 +1,535 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.EndOfStreamException; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.abc.CopyOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.Container; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Same as PlaceObject3Tag except additional AMF data - * - * @author JPEXS - */ -public class PlaceObject4Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { - - /** - * @since SWF 5 has clip actions (sprite characters only) - */ - public boolean placeFlagHasClipActions; - /** - * Has clip depth - */ - public boolean placeFlagHasClipDepth; - /** - * Has name - */ - public boolean placeFlagHasName; - /** - * Has ratio - */ - public boolean placeFlagHasRatio; - /** - * Has color transform - */ - public boolean placeFlagHasColorTransform; - /** - * Has matrix - */ - public boolean placeFlagHasMatrix; - /** - * Places a character - */ - public boolean placeFlagHasCharacter; - /** - * Defines a character to be moved - */ - public boolean placeFlagMove; - /** - * Has class name or character ID of bitmap to place. If - * PlaceFlagHasClassName, use ClassName. If PlaceFlagHasCharacter, use - * CharacterId - */ - public boolean placeFlagHasImage; - /** - * Has class name of object to place - */ - public boolean placeFlagHasClassName; - /** - * Enables bitmap caching - */ - public boolean placeFlagHasCacheAsBitmap; - /** - * Has blend mode - */ - public boolean placeFlagHasBlendMode; - /** - * Has filter list - */ - public boolean placeFlagHasFilterList; - /** - * Has opaque background. SWF 11 and higher. - */ - public boolean placeFlagOpaqueBackground; - /** - * Has visibility flag. SWF 11 and higher. - */ - public boolean placeFlagHasVisible; - /** - * Depth of character - */ - @SWFType(BasicType.UI16) - public int depth; - /** - * If PlaceFlagHasClassName or (PlaceFlagHasImage and - * PlaceFlagHasCharacter), Name of the class to place - */ - @Conditional("placeFlagHasClassName|(placeFlagHasImage,placeFlagHasCharacter)") - public String className; - /** - * If PlaceFlagHasCharacter, ID of character to place - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasCharacter") - public int characterId; - /** - * If PlaceFlagHasMatrix, Transform matrix data - */ - @Conditional("placeFlagHasMatrix") - public MATRIX matrix; - /** - * If PlaceFlagHasColorTransform, Color transform data - */ - @Conditional("placeFlagHasColorTransform") - public CXFORMWITHALPHA colorTransform; - /** - * If PlaceFlagHasRatio, Ratio - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasRatio") - public int ratio; - /** - * If PlaceFlagHasName, Name of character - */ - @Conditional("placeFlagHasName") - public String name; - /** - * If PlaceFlagHasClipDepth, Clip depth - */ - @SWFType(BasicType.UI16) - @Conditional("placeFlagHasClipDepth") - public int clipDepth; - /** - * If PlaceFlagHasFilterList, List of filters on this object - */ - @Conditional("placeFlagHasFilterList") - public List surfaceFilterList; - /** - * If PlaceFlagHasBlendMode, Blend mode - */ - @SWFType(BasicType.UI8) - @Conditional("placeFlagHasBlendMode") - public int blendMode; - /** - * If PlaceFlagHasCacheAsBitmap, 0 = Bitmap cache disabled, 1-255 = Bitmap - * cache enabled - */ - @SWFType(BasicType.UI8) - @Conditional("placeFlagHasCacheAsBitmap") - public int bitmapCache; - /** - * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data - */ - @Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5) - @Internal //TODO: make editable - public CLIPACTIONS clipActions; - /** - * If PlaceFlagHasVisible, 0 = Place invisible, 1 = Place visible - */ - @Conditional("placeFlagHasVisible") - public int visible; - /** - * If PlaceFlagHasVisible, Background color - */ - @Conditional("placeFlagOpaqueBackground") - public RGBA backgroundColor; - // FIXME bug found in ecoDrive.swf, - @Internal - private boolean bitmapCacheBug; - @Reserved - public boolean reserved; - - public static final int ID = 94; - public byte[] amfData; //TODO: Parse AMF data? - - @Override - public List getFilters() { - if (placeFlagHasFilterList) { - return surfaceFilterList; - } else { - return null; - } - } - - @Override - public int getClipDepth() { - if (placeFlagHasClipDepth) { - return clipDepth; - } - return -1; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - if (Configuration.debugCopy.get()) { - os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); - } - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); - sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); - sos.writeUB(1, placeFlagHasName ? 1 : 0); - sos.writeUB(1, placeFlagHasRatio ? 1 : 0); - sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); - sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); - sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); - sos.writeUB(1, placeFlagMove ? 1 : 0); - sos.writeUB(1, reserved ? 1 : 0); - sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 - sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 - sos.writeUB(1, placeFlagHasImage ? 1 : 0); - sos.writeUB(1, placeFlagHasClassName ? 1 : 0); - sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); - sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); - sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); - sos.writeUI16(depth); - if (placeFlagHasClassName || (placeFlagHasImage&&placeFlagHasCharacter)) { - sos.writeString(className); - } - if (placeFlagHasCharacter) { - sos.writeUI16(characterId); - } - if (placeFlagHasMatrix) { - sos.writeMatrix(matrix); - } - if (placeFlagHasColorTransform) { - sos.writeCXFORMWITHALPHA(colorTransform); - } - if (placeFlagHasRatio) { - sos.writeUI16(ratio); - } - if (placeFlagHasName) { - sos.writeString(name); - } - if (placeFlagHasClipDepth) { - sos.writeUI16(clipDepth); - } - if (placeFlagHasFilterList) { - sos.writeFILTERLIST(surfaceFilterList); - } - if (placeFlagHasBlendMode) { - sos.writeUI8(blendMode); - } - if (placeFlagHasCacheAsBitmap) { - if (!bitmapCacheBug) { - sos.writeUI8(bitmapCache); - } - } - if (placeFlagHasVisible) { - sos.writeUI8(visible); - } - if (placeFlagOpaqueBackground) { - sos.writeRGBA(backgroundColor); - } - if (placeFlagHasClipActions) { - sos.writeCLIPACTIONS(clipActions); - } - sos.close(); - } catch (IOException ex) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public PlaceObject4Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "PlaceObject4", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - placeFlagHasClipActions = sis.readUB(1) == 1; - placeFlagHasClipDepth = sis.readUB(1) == 1; - placeFlagHasName = sis.readUB(1) == 1; - placeFlagHasRatio = sis.readUB(1) == 1; - placeFlagHasColorTransform = sis.readUB(1) == 1; - placeFlagHasMatrix = sis.readUB(1) == 1; - placeFlagHasCharacter = sis.readUB(1) == 1; - placeFlagMove = sis.readUB(1) == 1; - reserved = sis.readUB(1) == 1; - placeFlagOpaqueBackground = sis.readUB(1) == 1; //SWF11 - placeFlagHasVisible = sis.readUB(1) == 1; //SWF11 - placeFlagHasImage = sis.readUB(1) == 1; - placeFlagHasClassName = sis.readUB(1) == 1; - placeFlagHasCacheAsBitmap = sis.readUB(1) == 1; - placeFlagHasBlendMode = sis.readUB(1) == 1; - placeFlagHasFilterList = sis.readUB(1) == 1; - - depth = sis.readUI16(); - if (placeFlagHasClassName || (placeFlagHasImage&&placeFlagHasCharacter)) { - className = sis.readString(); - } - if (placeFlagHasCharacter) { - characterId = sis.readUI16(); - } - if (placeFlagHasMatrix) { - matrix = sis.readMatrix(); - } - if (placeFlagHasColorTransform) { - colorTransform = sis.readCXFORMWITHALPHA(); - } - if (placeFlagHasRatio) { - ratio = sis.readUI16(); - } - if (placeFlagHasName) { - name = sis.readString(); - } - if (placeFlagHasClipDepth) { - clipDepth = sis.readUI16(); - } - if (placeFlagHasFilterList) { - surfaceFilterList = sis.readFILTERLIST(); - } - if (placeFlagHasBlendMode) { - blendMode = sis.readUI8(); - } - bitmapCacheBug = false; - if (placeFlagHasCacheAsBitmap) { - try { - bitmapCache = sis.readUI8(); - } catch (EndOfStreamException eex) { - bitmapCacheBug = true; - bitmapCache = 1; - } - } - - if (placeFlagHasVisible) { - visible = sis.readUI8(); - } - if (placeFlagOpaqueBackground) { - backgroundColor = sis.readRGBA(); - } - - if (placeFlagHasClipActions) { - clipActions = sis.readCLIPACTIONS(swf, this); - } - amfData = sis.readBytesEx(sis.available()); - } - - /** - * Returns all sub-items - * - * @return List of sub-items - */ - @Override - public List getSubItems() { - List ret = new ArrayList<>(); - if (placeFlagHasClipActions) { - ret.addAll(clipActions.clipActionRecords); - } - return ret; - } - - /** - * Returns number of sub-items - * - * @return Number of sub-items - */ - @Override - public int getItemCount() { - if (!placeFlagHasClipActions) { - return 0; - } - return clipActions.clipActionRecords.size(); - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - if (placeFlagHasCharacter) { - ret.add(characterId); - } - return ret; - } - - @Override - public int getCharacterId() { - if (placeFlagHasCharacter) { - return characterId; - } else { - return -1; - } - } - - @Override - public int getDepth() { - return depth; - } - - @Override - public MATRIX getMatrix() { - if (placeFlagHasMatrix) { - return matrix; - } else { - return null; - } - } - - @Override - public String getInstanceName() { - if (placeFlagHasName) { - return name; - } - return null; - } - - @Override - public ColorTransform getColorTransform() { - if (placeFlagHasColorTransform) { - return colorTransform; - } else { - return null; - } - } - - @Override - public int getBlendMode() { - return blendMode; - } - - @Override - public String getClassName() { - if (placeFlagHasClassName) { - return className; - } - return null; - } - - @Override - public boolean cacheAsBitmap() { - return placeFlagHasCacheAsBitmap; - } - - @Override - public boolean isVisible() { - if (placeFlagHasVisible) { - return visible == 1; - } - return true; - } - - @Override - public RGBA getBackgroundColor() { - if (placeFlagOpaqueBackground) { - return backgroundColor; - } - return null; - } - - @Override - public boolean flagMove() { - return placeFlagMove; - } - - @Override - public int getRatio() { - if (!placeFlagHasRatio) { - return -1; - } - return ratio; - } - - @Override - public void setInstanceName(String name) { - placeFlagHasName = true; - this.name = name; - } - - @Override - public void setClassName(String className) { - placeFlagHasClassName = true; - this.className = className; - } - - @Override - public String toString() { - if (placeFlagHasName) { - return super.toString() + " (" + name + ")"; - } else { - return super.toString(); - } - } - - @Override - public CLIPACTIONS getClipActions() { - if (placeFlagHasClipActions) { - return clipActions; - } else { - return null; - } - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.EndOfStreamException; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.abc.CopyOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.Container; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Same as PlaceObject3Tag except additional AMF data + * + * @author JPEXS + */ +public class PlaceObject4Tag extends CharacterIdTag implements Container, PlaceObjectTypeTag { + + /** + * @since SWF 5 has clip actions (sprite characters only) + */ + public boolean placeFlagHasClipActions; + /** + * Has clip depth + */ + public boolean placeFlagHasClipDepth; + /** + * Has name + */ + public boolean placeFlagHasName; + /** + * Has ratio + */ + public boolean placeFlagHasRatio; + /** + * Has color transform + */ + public boolean placeFlagHasColorTransform; + /** + * Has matrix + */ + public boolean placeFlagHasMatrix; + /** + * Places a character + */ + public boolean placeFlagHasCharacter; + /** + * Defines a character to be moved + */ + public boolean placeFlagMove; + /** + * Has class name or character ID of bitmap to place. If + * PlaceFlagHasClassName, use ClassName. If PlaceFlagHasCharacter, use + * CharacterId + */ + public boolean placeFlagHasImage; + /** + * Has class name of object to place + */ + public boolean placeFlagHasClassName; + /** + * Enables bitmap caching + */ + public boolean placeFlagHasCacheAsBitmap; + /** + * Has blend mode + */ + public boolean placeFlagHasBlendMode; + /** + * Has filter list + */ + public boolean placeFlagHasFilterList; + /** + * Has opaque background. SWF 11 and higher. + */ + public boolean placeFlagOpaqueBackground; + /** + * Has visibility flag. SWF 11 and higher. + */ + public boolean placeFlagHasVisible; + /** + * Depth of character + */ + @SWFType(BasicType.UI16) + public int depth; + /** + * If PlaceFlagHasClassName or (PlaceFlagHasImage and + * PlaceFlagHasCharacter), Name of the class to place + */ + @Conditional("placeFlagHasClassName|(placeFlagHasImage,placeFlagHasCharacter)") + public String className; + /** + * If PlaceFlagHasCharacter, ID of character to place + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasCharacter") + public int characterId; + /** + * If PlaceFlagHasMatrix, Transform matrix data + */ + @Conditional("placeFlagHasMatrix") + public MATRIX matrix; + /** + * If PlaceFlagHasColorTransform, Color transform data + */ + @Conditional("placeFlagHasColorTransform") + public CXFORMWITHALPHA colorTransform; + /** + * If PlaceFlagHasRatio, Ratio + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasRatio") + public int ratio; + /** + * If PlaceFlagHasName, Name of character + */ + @Conditional("placeFlagHasName") + public String name; + /** + * If PlaceFlagHasClipDepth, Clip depth + */ + @SWFType(BasicType.UI16) + @Conditional("placeFlagHasClipDepth") + public int clipDepth; + /** + * If PlaceFlagHasFilterList, List of filters on this object + */ + @Conditional("placeFlagHasFilterList") + public List surfaceFilterList; + /** + * If PlaceFlagHasBlendMode, Blend mode + */ + @SWFType(BasicType.UI8) + @Conditional("placeFlagHasBlendMode") + public int blendMode; + /** + * If PlaceFlagHasCacheAsBitmap, 0 = Bitmap cache disabled, 1-255 = Bitmap + * cache enabled + */ + @SWFType(BasicType.UI8) + @Conditional("placeFlagHasCacheAsBitmap") + public int bitmapCache; + /** + * @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data + */ + @Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5) + @Internal //TODO: make editable + public CLIPACTIONS clipActions; + /** + * If PlaceFlagHasVisible, 0 = Place invisible, 1 = Place visible + */ + @Conditional("placeFlagHasVisible") + public int visible; + /** + * If PlaceFlagHasVisible, Background color + */ + @Conditional("placeFlagOpaqueBackground") + public RGBA backgroundColor; + // FIXME bug found in ecoDrive.swf, + @Internal + private boolean bitmapCacheBug; + @Reserved + public boolean reserved; + + public static final int ID = 94; + public byte[] amfData; //TODO: Parse AMF data? + + @Override + public List getFilters() { + if (placeFlagHasFilterList) { + return surfaceFilterList; + } else { + return null; + } + } + + @Override + public int getClipDepth() { + if (placeFlagHasClipDepth) { + return clipDepth; + } + return -1; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + if (Configuration.debugCopy.get()) { + os = new CopyOutputStream(os, new ByteArrayInputStream(super.data)); + } + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUB(1, placeFlagHasClipActions ? 1 : 0); + sos.writeUB(1, placeFlagHasClipDepth ? 1 : 0); + sos.writeUB(1, placeFlagHasName ? 1 : 0); + sos.writeUB(1, placeFlagHasRatio ? 1 : 0); + sos.writeUB(1, placeFlagHasColorTransform ? 1 : 0); + sos.writeUB(1, placeFlagHasMatrix ? 1 : 0); + sos.writeUB(1, placeFlagHasCharacter ? 1 : 0); + sos.writeUB(1, placeFlagMove ? 1 : 0); + sos.writeUB(1, reserved ? 1 : 0); + sos.writeUB(1, placeFlagOpaqueBackground ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasVisible ? 1 : 0); //SWF11 + sos.writeUB(1, placeFlagHasImage ? 1 : 0); + sos.writeUB(1, placeFlagHasClassName ? 1 : 0); + sos.writeUB(1, placeFlagHasCacheAsBitmap ? 1 : 0); + sos.writeUB(1, placeFlagHasBlendMode ? 1 : 0); + sos.writeUB(1, placeFlagHasFilterList ? 1 : 0); + sos.writeUI16(depth); + if (placeFlagHasClassName || (placeFlagHasImage && placeFlagHasCharacter)) { + sos.writeString(className); + } + if (placeFlagHasCharacter) { + sos.writeUI16(characterId); + } + if (placeFlagHasMatrix) { + sos.writeMatrix(matrix); + } + if (placeFlagHasColorTransform) { + sos.writeCXFORMWITHALPHA(colorTransform); + } + if (placeFlagHasRatio) { + sos.writeUI16(ratio); + } + if (placeFlagHasName) { + sos.writeString(name); + } + if (placeFlagHasClipDepth) { + sos.writeUI16(clipDepth); + } + if (placeFlagHasFilterList) { + sos.writeFILTERLIST(surfaceFilterList); + } + if (placeFlagHasBlendMode) { + sos.writeUI8(blendMode); + } + if (placeFlagHasCacheAsBitmap) { + if (!bitmapCacheBug) { + sos.writeUI8(bitmapCache); + } + } + if (placeFlagHasVisible) { + sos.writeUI8(visible); + } + if (placeFlagOpaqueBackground) { + sos.writeRGBA(backgroundColor); + } + if (placeFlagHasClipActions) { + sos.writeCLIPACTIONS(clipActions); + } + sos.close(); + } catch (IOException ex) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public PlaceObject4Tag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "PlaceObject4", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + placeFlagHasClipActions = sis.readUB(1) == 1; + placeFlagHasClipDepth = sis.readUB(1) == 1; + placeFlagHasName = sis.readUB(1) == 1; + placeFlagHasRatio = sis.readUB(1) == 1; + placeFlagHasColorTransform = sis.readUB(1) == 1; + placeFlagHasMatrix = sis.readUB(1) == 1; + placeFlagHasCharacter = sis.readUB(1) == 1; + placeFlagMove = sis.readUB(1) == 1; + reserved = sis.readUB(1) == 1; + placeFlagOpaqueBackground = sis.readUB(1) == 1; //SWF11 + placeFlagHasVisible = sis.readUB(1) == 1; //SWF11 + placeFlagHasImage = sis.readUB(1) == 1; + placeFlagHasClassName = sis.readUB(1) == 1; + placeFlagHasCacheAsBitmap = sis.readUB(1) == 1; + placeFlagHasBlendMode = sis.readUB(1) == 1; + placeFlagHasFilterList = sis.readUB(1) == 1; + + depth = sis.readUI16(); + if (placeFlagHasClassName || (placeFlagHasImage && placeFlagHasCharacter)) { + className = sis.readString(); + } + if (placeFlagHasCharacter) { + characterId = sis.readUI16(); + } + if (placeFlagHasMatrix) { + matrix = sis.readMatrix(); + } + if (placeFlagHasColorTransform) { + colorTransform = sis.readCXFORMWITHALPHA(); + } + if (placeFlagHasRatio) { + ratio = sis.readUI16(); + } + if (placeFlagHasName) { + name = sis.readString(); + } + if (placeFlagHasClipDepth) { + clipDepth = sis.readUI16(); + } + if (placeFlagHasFilterList) { + surfaceFilterList = sis.readFILTERLIST(); + } + if (placeFlagHasBlendMode) { + blendMode = sis.readUI8(); + } + bitmapCacheBug = false; + if (placeFlagHasCacheAsBitmap) { + try { + bitmapCache = sis.readUI8(); + } catch (EndOfStreamException eex) { + bitmapCacheBug = true; + bitmapCache = 1; + } + } + + if (placeFlagHasVisible) { + visible = sis.readUI8(); + } + if (placeFlagOpaqueBackground) { + backgroundColor = sis.readRGBA(); + } + + if (placeFlagHasClipActions) { + clipActions = sis.readCLIPACTIONS(swf, this); + } + amfData = sis.readBytesEx(sis.available()); + } + + /** + * Returns all sub-items + * + * @return List of sub-items + */ + @Override + public List getSubItems() { + List ret = new ArrayList<>(); + if (placeFlagHasClipActions) { + ret.addAll(clipActions.clipActionRecords); + } + return ret; + } + + /** + * Returns number of sub-items + * + * @return Number of sub-items + */ + @Override + public int getItemCount() { + if (!placeFlagHasClipActions) { + return 0; + } + return clipActions.clipActionRecords.size(); + } + + @Override + public void getNeededCharacters(Set needed) { + if (placeFlagHasCharacter) { + needed.add(characterId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + // the place object tag will be removed + return false; + } + + @Override + public int getCharacterId() { + if (placeFlagHasCharacter) { + return characterId; + } else { + return -1; + } + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public MATRIX getMatrix() { + if (placeFlagHasMatrix) { + return matrix; + } else { + return null; + } + } + + @Override + public String getInstanceName() { + if (placeFlagHasName) { + return name; + } + return null; + } + + @Override + public ColorTransform getColorTransform() { + if (placeFlagHasColorTransform) { + return colorTransform; + } else { + return null; + } + } + + @Override + public int getBlendMode() { + return blendMode; + } + + @Override + public String getClassName() { + if (placeFlagHasClassName) { + return className; + } + return null; + } + + @Override + public boolean cacheAsBitmap() { + return placeFlagHasCacheAsBitmap; + } + + @Override + public boolean isVisible() { + if (placeFlagHasVisible) { + return visible == 1; + } + return true; + } + + @Override + public RGBA getBackgroundColor() { + if (placeFlagOpaqueBackground) { + return backgroundColor; + } + return null; + } + + @Override + public boolean flagMove() { + return placeFlagMove; + } + + @Override + public int getRatio() { + if (!placeFlagHasRatio) { + return -1; + } + return ratio; + } + + @Override + public void setInstanceName(String name) { + placeFlagHasName = true; + this.name = name; + } + + @Override + public void setClassName(String className) { + placeFlagHasClassName = true; + this.className = className; + } + + @Override + public String toString() { + if (placeFlagHasName) { + return super.toString() + " (" + name + ")"; + } else { + return super.toString(); + } + } + + @Override + public CLIPACTIONS getClipActions() { + if (placeFlagHasClipActions) { + return clipActions; + } else { + return null; + } + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java b/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java index 818b0a207..1132b7512 100644 --- a/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java +++ b/src/com/jpexs/decompiler/flash/tags/PlaceObjectTag.java @@ -1,210 +1,213 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CXFORM; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.Optional; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Adds character to the display list - * - * @author JPEXS - */ -public class PlaceObjectTag extends CharacterIdTag implements PlaceObjectTypeTag { - - /** - * ID of character to place - */ - @SWFType(BasicType.UI16) - public int characterId; - /** - * Depth of character - */ - @SWFType(BasicType.UI16) - public int depth; - /** - * Transform matrix data - */ - public MATRIX matrix; - /** - * Color transform data - */ - @Optional - public CXFORM colorTransform; - public static final int ID = 4; - - @Override - public List getFilters() { - return null; - } - - @Override - public int getClipDepth() { - return -1; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - @Override - public byte[] getData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStream os = baos; - SWFOutputStream sos = new SWFOutputStream(os, getVersion()); - try { - sos.writeUI16(characterId); - sos.writeUI16(depth); - sos.writeMatrix(matrix); - if (colorTransform != null) { - sos.writeCXFORM(colorTransform); - } - } catch (IOException e) { - } - return baos.toByteArray(); - } - - /** - * Constructor - * - * @param swf - * @param headerData - * @param data Data bytes - * @param pos - * @throws IOException - */ - public PlaceObjectTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { - super(swf, ID, "PlaceObject", headerData, data, pos); - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); - characterId = sis.readUI16(); - depth = sis.readUI16(); - matrix = sis.readMatrix(); - if (sis.available() > 0) { - colorTransform = sis.readCXFORM(); - } - } - - public PlaceObjectTag(SWF swf, int characterId, int depth, MATRIX matrix, CXFORM colorTransform) { - super(swf, ID, "PlaceObject", null, null, 0); - this.characterId = characterId; - this.depth = depth; - this.matrix = matrix; - this.colorTransform = colorTransform; - } - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - ret.add(characterId); - return ret; - } - - @Override - public int getCharacterId() { - return characterId; - } - - @Override - public int getDepth() { - return depth; - } - - @Override - public MATRIX getMatrix() { - return matrix; - } - - @Override - public String getInstanceName() { - return null; - } - - @Override - public ColorTransform getColorTransform() { - return colorTransform; - } - - @Override - public int getBlendMode() { - return 0; - } - - @Override - public boolean cacheAsBitmap() { - return false; - } - - @Override - public String getClassName() { - return null; - } - - @Override - public boolean isVisible() { - return true; - } - - @Override - public RGBA getBackgroundColor() { - return null; - } - - @Override - public boolean flagMove() { - return false; - } - - @Override - public int getRatio() { - return -1; - } - - @Override - public void setInstanceName(String name) { - //not supported - } - - @Override - public void setClassName(String className) { - //not supported - } - - @Override - public CLIPACTIONS getClipActions() { - return null; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CXFORM; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.Optional; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Set; + +/** + * Adds character to the display list + * + * @author JPEXS + */ +public class PlaceObjectTag extends CharacterIdTag implements PlaceObjectTypeTag { + + /** + * ID of character to place + */ + @SWFType(BasicType.UI16) + public int characterId; + /** + * Depth of character + */ + @SWFType(BasicType.UI16) + public int depth; + /** + * Transform matrix data + */ + public MATRIX matrix; + /** + * Color transform data + */ + @Optional + public CXFORM colorTransform; + public static final int ID = 4; + + @Override + public List getFilters() { + return null; + } + + @Override + public int getClipDepth() { + return -1; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + @Override + public byte[] getData() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream os = baos; + SWFOutputStream sos = new SWFOutputStream(os, getVersion()); + try { + sos.writeUI16(characterId); + sos.writeUI16(depth); + sos.writeMatrix(matrix); + if (colorTransform != null) { + sos.writeCXFORM(colorTransform); + } + } catch (IOException e) { + } + return baos.toByteArray(); + } + + /** + * Constructor + * + * @param swf + * @param headerData + * @param data Data bytes + * @param pos + * @throws IOException + */ + public PlaceObjectTag(SWF swf, byte[] headerData, byte[] data, long pos) throws IOException { + super(swf, ID, "PlaceObject", headerData, data, pos); + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(data), swf.version); + characterId = sis.readUI16(); + depth = sis.readUI16(); + matrix = sis.readMatrix(); + if (sis.available() > 0) { + colorTransform = sis.readCXFORM(); + } + } + + public PlaceObjectTag(SWF swf, int characterId, int depth, MATRIX matrix, CXFORM colorTransform) { + super(swf, ID, "PlaceObject", null, null, 0); + this.characterId = characterId; + this.depth = depth; + this.matrix = matrix; + this.colorTransform = colorTransform; + } + + @Override + public void getNeededCharacters(Set needed) { + needed.add(characterId); + } + + @Override + public boolean removeCharacter(int characterId) { + // the place object tag will be removed + return false; + } + + @Override + public int getCharacterId() { + return characterId; + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public MATRIX getMatrix() { + return matrix; + } + + @Override + public String getInstanceName() { + return null; + } + + @Override + public ColorTransform getColorTransform() { + return colorTransform; + } + + @Override + public int getBlendMode() { + return 0; + } + + @Override + public boolean cacheAsBitmap() { + return false; + } + + @Override + public String getClassName() { + return null; + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public RGBA getBackgroundColor() { + return null; + } + + @Override + public boolean flagMove() { + return false; + } + + @Override + public int getRatio() { + return -1; + } + + @Override + public void setInstanceName(String name) { + //not supported + } + + @Override + public void setClassName(String className) { + //not supported + } + + @Override + public CLIPACTIONS getClipActions() { + return null; + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/ShowFrameTag.java b/src/com/jpexs/decompiler/flash/tags/ShowFrameTag.java index aa0667c3b..a765fc0db 100644 --- a/src/com/jpexs/decompiler/flash/tags/ShowFrameTag.java +++ b/src/com/jpexs/decompiler/flash/tags/ShowFrameTag.java @@ -1,70 +1,69 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import java.util.ArrayList; -import java.util.List; - -/** - * Instructs Flash Player to display the contents of the display list - * - * @author JPEXS - */ -public class ShowFrameTag extends Tag { - - public static final int ID = 1; - - private static List nestedTagTypeIds = new ArrayList() { - { - add(PlaceObjectTag.ID); - add(PlaceObject2Tag.ID); - add(PlaceObject3Tag.ID); - add(PlaceObject4Tag.ID); - add(RemoveObjectTag.ID); - add(RemoveObject2Tag.ID); - add(FrameLabelTag.ID); - add(StartSoundTag.ID); - add(StartSound2Tag.ID); - add(VideoFrameTag.ID); - add(SoundStreamBlockTag.ID); - /*add(SoundStreamHeadTag.ID); - add(SoundStreamHead2Tag.ID);*/ - } - }; - public List innerTags; - - /** - * Constructor - * - * @param swf - * @param headerData - * @param pos - * @param data - */ - public ShowFrameTag(SWF swf, byte[] headerData, byte[] data, long pos) { - super(swf, ID, "ShowFrame", headerData, data, pos); - } - - public ShowFrameTag(SWF swf) { - super(swf, ID, "ShowFrame", null, null, 0); - } - - public static boolean isNestedTagType(int tagTypeId) { - return nestedTagTypeIds.contains(tagTypeId); - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import java.util.ArrayList; +import java.util.List; + +/** + * Instructs Flash Player to display the contents of the display list + * + * @author JPEXS + */ +public class ShowFrameTag extends Tag { + + public static final int ID = 1; + + private static List nestedTagTypeIds = new ArrayList() { + { + add(PlaceObjectTag.ID); + add(PlaceObject2Tag.ID); + add(PlaceObject3Tag.ID); + add(PlaceObject4Tag.ID); + add(RemoveObjectTag.ID); + add(RemoveObject2Tag.ID); + add(FrameLabelTag.ID); + add(StartSoundTag.ID); + add(StartSound2Tag.ID); + add(VideoFrameTag.ID); + add(SoundStreamBlockTag.ID); + /*add(SoundStreamHeadTag.ID); + add(SoundStreamHead2Tag.ID);*/ + } + }; + + /** + * Constructor + * + * @param swf + * @param headerData + * @param pos + * @param data + */ + public ShowFrameTag(SWF swf, byte[] headerData, byte[] data, long pos) { + super(swf, ID, "ShowFrame", headerData, data, pos); + } + + public ShowFrameTag(SWF swf) { + super(swf, ID, "ShowFrame", null, null, 0); + } + + public static boolean isNestedTagType(int tagTypeId) { + return nestedTagTypeIds.contains(tagTypeId); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/Tag.java b/src/com/jpexs/decompiler/flash/tags/Tag.java index 95a15af76..a46bc78bb 100644 --- a/src/com/jpexs/decompiler/flash/tags/Tag.java +++ b/src/com/jpexs/decompiler/flash/tags/Tag.java @@ -1,435 +1,448 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; -import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; -import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; -import com.jpexs.decompiler.flash.tags.gfx.ExporterInfoTag; -import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Represents Tag inside SWF file - */ -public class Tag implements NeedsCharacters, Exportable, ContainerItem, Serializable { - - /** - * Identifier of tag type - */ - protected int id; - /** - * Original header data in the tag - */ - protected byte[] headerData; - /** - * Data in the tag - */ - protected byte[] data; - /** - * If true, then Tag is written to the stream as longer than 0x3f even if it - * is not - */ - @Internal - public boolean forceWriteAsLong = false; - @Internal - private final long pos; - protected String tagName; - @Internal - public Tag previousTag; - @Internal - protected transient SWF swf; - @Internal - protected transient Timelined timelined; - @Internal - private boolean modified; - - public String getTagName() { - return tagName; - } - - public String getName() { - return tagName; - } - - @Override - public String getExportFileName() { - return getName(); - } - - /** - * Returns identifier of tag type - * - * @return Identifier of tag type - */ - public int getId() { - return id; - } - - @Override - public SWF getSwf() { - return swf; - } - - public void setSwf(SWF swf) { - this.swf = swf; - } - - public Timelined getTimelined() { - return timelined; - } - - public void setTimelined(Timelined timelined) { - this.timelined = timelined; - } - - /** - * Constructor - * - * @param swf - * @param id Tag type identifier - * @param name Tag name - * @param headerData - * @param data Bytes of data - * @param pos - */ - public Tag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { - this.id = id; - this.tagName = name; - this.headerData = headerData; - this.data = data; - this.pos = pos; - this.swf = swf; - if (swf == null) { - throw new Error("swf parameter cannot be null."); - } - if (data == null) { // it is tag build by constructor - this.data = new byte[0]; - modified = true; - } - } - - private static final Object lockObject = new Object(); - private static List knownTagIds; - private static List requiredTagIds; - - public static List getKnownTags() { - if (knownTagIds == null) { - synchronized (lockObject) { - if (knownTagIds == null) { - List tagIds = Arrays.asList( - CSMTextSettingsTag.ID, - DebugIDTag.ID, - DefineBinaryDataTag.ID, - DefineBitsJPEG2Tag.ID, - DefineBitsJPEG3Tag.ID, - DefineBitsJPEG4Tag.ID, - DefineBitsLossless2Tag.ID, - DefineBitsLosslessTag.ID, - DefineBitsTag.ID, - DefineButton2Tag.ID, - DefineButtonCxformTag.ID, - DefineButtonSoundTag.ID, - DefineButtonTag.ID, - DefineEditTextTag.ID, - DefineFont2Tag.ID, - DefineFont3Tag.ID, - DefineFont4Tag.ID, - DefineFontAlignZonesTag.ID, - DefineFontInfo2Tag.ID, - DefineFontInfoTag.ID, - DefineFontNameTag.ID, - DefineFontTag.ID, - DefineMorphShape2Tag.ID, - DefineMorphShapeTag.ID, - DefineScalingGridTag.ID, - DefineSceneAndFrameLabelDataTag.ID, - DefineShape2Tag.ID, - DefineShape3Tag.ID, - DefineShape4Tag.ID, - DefineShapeTag.ID, - DefineSoundTag.ID, - DefineSpriteTag.ID, - DefineText2Tag.ID, - DefineTextTag.ID, - DefineVideoStreamTag.ID, - DoABCDefineTag.ID, - DoABCTag.ID, - DoActionTag.ID, - DoInitActionTag.ID, - EnableDebugger2Tag.ID, - EnableDebuggerTag.ID, - EnableTelemetryTag.ID, - EndTag.ID, - ExportAssetsTag.ID, - FileAttributesTag.ID, - FrameLabelTag.ID, - ImportAssets2Tag.ID, - ImportAssetsTag.ID, - JPEGTablesTag.ID, - MetadataTag.ID, - PlaceObject2Tag.ID, - PlaceObject3Tag.ID, - PlaceObject4Tag.ID, - PlaceObjectTag.ID, - ProductInfoTag.ID, - ProtectTag.ID, - RemoveObject2Tag.ID, - RemoveObjectTag.ID, - ScriptLimitsTag.ID, - SetBackgroundColorTag.ID, - SetTabIndexTag.ID, - ShowFrameTag.ID, - SoundStreamBlockTag.ID, - SoundStreamHead2Tag.ID, - SoundStreamHeadTag.ID, - StartSound2Tag.ID, - StartSoundTag.ID, - SymbolClassTag.ID, - TagStub.ID, - VideoFrameTag.ID, - DefineCompactedFont.ID, - DefineExternalGradient.ID, - DefineExternalImage.ID, - DefineExternalImage2.ID, - DefineExternalSound.ID, - DefineExternalStreamSound.ID, - DefineGradientMap.ID, - DefineSubImage.ID, - ExporterInfoTag.ID, - FontTextureInfo.ID); - knownTagIds = tagIds; - } - } - } - return knownTagIds; - } - - public static List getRequiredTags() { - if (requiredTagIds == null) { - synchronized (lockObject) { - if (requiredTagIds == null) { - List tagIds = Arrays.asList( - DefineBinaryDataTag.ID, - DefineBitsJPEG2Tag.ID, - DefineBitsJPEG3Tag.ID, - DefineBitsJPEG4Tag.ID, - DefineBitsLossless2Tag.ID, - DefineBitsLosslessTag.ID, - DefineBitsTag.ID, - DefineButton2Tag.ID, - DefineButtonCxformTag.ID, - DefineButtonSoundTag.ID, - DefineButtonTag.ID, - DefineEditTextTag.ID, - DefineFont2Tag.ID, - DefineFont3Tag.ID, - DefineFont4Tag.ID, - DefineFontAlignZonesTag.ID, - DefineFontInfo2Tag.ID, - DefineFontInfoTag.ID, - DefineFontNameTag.ID, - DefineFontTag.ID, - DefineMorphShape2Tag.ID, - DefineMorphShapeTag.ID, - DefineScalingGridTag.ID, - DefineSceneAndFrameLabelDataTag.ID, - DefineShape2Tag.ID, - DefineShape3Tag.ID, - DefineShape4Tag.ID, - DefineShapeTag.ID, - DefineSoundTag.ID, - DefineSpriteTag.ID, - DefineText2Tag.ID, - DefineTextTag.ID, - DefineVideoStreamTag.ID, - DoABCDefineTag.ID, - DoABCTag.ID, - DoActionTag.ID, - DoInitActionTag.ID, - ShowFrameTag.ID); - requiredTagIds = tagIds; - } - } - } - return requiredTagIds; - } - - /** - * Gets data bytes - * - * @return Bytes of data - */ - public byte[] getData() { - return data; - } - - public int getVersion() { - if (swf == null) { - return SWF.DEFAULT_VERSION; - } - return swf.version; - } - - /** - * Gets original read header data - * - * @return Bytes of data - */ - public byte[] getOriginalHeaderData() { - return headerData; - } - - /** - * Gets original read data - * - * @return Bytes of data - */ - public byte[] getOriginalData() { - return data; - } - - public void createOriginalData() { - data = getData(); - headerData = getHeader(data); - } - - protected byte[] getHeader(byte[] data) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - - SWFOutputStream sos = new SWFOutputStream(baos, swf.version); - int tagLength = data.length; - int tagID = getId(); - int tagIDLength = (tagID << 6); - if ((tagLength <= 62) && (!forceWriteAsLong)) { - tagIDLength += tagLength; - sos.writeUI16(tagIDLength); - } else { - tagIDLength += 0x3f; - sos.writeUI16(tagIDLength); - sos.writeSI32(tagLength); - } - } catch (IOException iex) { - } - return baos.toByteArray(); - } - - public static byte[] getTagHeader(int tagIDTagLength, long tagLength, boolean writeLong, int version) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - - SWFOutputStream sos = new SWFOutputStream(baos, version); - sos.writeUI16(tagIDTagLength); - if (writeLong) { - sos.writeSI32(tagLength); - } - } catch (IOException iex) { - } - return baos.toByteArray(); - } - - /** - * Writes Tag value to the stream - * - * @param sos SWF output stream - * @throws IOException - */ - public void writeTag(SWFOutputStream sos) throws IOException { - if (isModified()) { - byte[] newData = getData(); - sos.write(getHeader(newData)); - sos.write(newData); - return; - } - - sos.write(headerData); - sos.write(data); - } - - /** - * Returns string representation of the object - * - * @return String representation of the object - */ - @Override - public String toString() { - return getName(); - } - - public final long getOrigDataLength() { - return data.length; - } - - public boolean hasSubTags() { - return false; - } - - public List getSubTags() { - return null; - } - - public long getPos() { - return pos; - } - - public void setModified(boolean value) { - modified = value; - } - - public boolean isModified() { - return modified; - } - - @Override - public Set getNeededCharacters() { - return new HashSet<>(); - } - - public Set getDeepNeededCharacters(Map characters) { - Set ret = new HashSet<>(); - Set needed = getNeededCharacters(); - for (int ch : needed) { - if (!characters.containsKey(ch)) { //TODO: use Import tag (?) - continue; - } - ret.add(ch); - ret.addAll(characters.get(ch).getDeepNeededCharacters(characters)); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalGradient; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalStreamSound; +import com.jpexs.decompiler.flash.tags.gfx.DefineGradientMap; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; +import com.jpexs.decompiler.flash.tags.gfx.ExporterInfoTag; +import com.jpexs.decompiler.flash.tags.gfx.FontTextureInfo; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents Tag inside SWF file + */ +public class Tag implements NeedsCharacters, Exportable, ContainerItem, Serializable { + + /** + * Identifier of tag type + */ + protected int id; + /** + * Original header data in the tag + */ + protected byte[] headerData; + /** + * Data in the tag + */ + protected byte[] data; + /** + * If true, then Tag is written to the stream as longer than 0x3f even if it + * is not + */ + @Internal + public boolean forceWriteAsLong = false; + @Internal + private final long pos; + protected String tagName; + @Internal + public Tag previousTag; + @Internal + protected transient SWF swf; + @Internal + protected transient Timelined timelined; + @Internal + private boolean modified; + + public String getTagName() { + return tagName; + } + + public String getName() { + return tagName; + } + + @Override + public String getExportFileName() { + return getName(); + } + + /** + * Returns identifier of tag type + * + * @return Identifier of tag type + */ + public int getId() { + return id; + } + + @Override + public SWF getSwf() { + return swf; + } + + public void setSwf(SWF swf) { + this.swf = swf; + } + + public Timelined getTimelined() { + return timelined; + } + + public void setTimelined(Timelined timelined) { + this.timelined = timelined; + } + + /** + * Constructor + * + * @param swf + * @param id Tag type identifier + * @param name Tag name + * @param headerData + * @param data Bytes of data + * @param pos + */ + public Tag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { + this.id = id; + this.tagName = name; + this.headerData = headerData; + this.data = data; + this.pos = pos; + this.swf = swf; + if (swf == null) { + throw new Error("swf parameter cannot be null."); + } + if (data == null) { // it is tag build by constructor + this.data = new byte[0]; + modified = true; + } + } + + private static final Object lockObject = new Object(); + private static List knownTagIds; + private static List requiredTagIds; + + public static List getKnownTags() { + if (knownTagIds == null) { + synchronized (lockObject) { + if (knownTagIds == null) { + List tagIds = Arrays.asList( + CSMTextSettingsTag.ID, + DebugIDTag.ID, + DefineBinaryDataTag.ID, + DefineBitsJPEG2Tag.ID, + DefineBitsJPEG3Tag.ID, + DefineBitsJPEG4Tag.ID, + DefineBitsLossless2Tag.ID, + DefineBitsLosslessTag.ID, + DefineBitsTag.ID, + DefineButton2Tag.ID, + DefineButtonCxformTag.ID, + DefineButtonSoundTag.ID, + DefineButtonTag.ID, + DefineEditTextTag.ID, + DefineFont2Tag.ID, + DefineFont3Tag.ID, + DefineFont4Tag.ID, + DefineFontAlignZonesTag.ID, + DefineFontInfo2Tag.ID, + DefineFontInfoTag.ID, + DefineFontNameTag.ID, + DefineFontTag.ID, + DefineMorphShape2Tag.ID, + DefineMorphShapeTag.ID, + DefineScalingGridTag.ID, + DefineSceneAndFrameLabelDataTag.ID, + DefineShape2Tag.ID, + DefineShape3Tag.ID, + DefineShape4Tag.ID, + DefineShapeTag.ID, + DefineSoundTag.ID, + DefineSpriteTag.ID, + DefineText2Tag.ID, + DefineTextTag.ID, + DefineVideoStreamTag.ID, + DoABCDefineTag.ID, + DoABCTag.ID, + DoActionTag.ID, + DoInitActionTag.ID, + EnableDebugger2Tag.ID, + EnableDebuggerTag.ID, + EnableTelemetryTag.ID, + EndTag.ID, + ExportAssetsTag.ID, + FileAttributesTag.ID, + FrameLabelTag.ID, + ImportAssets2Tag.ID, + ImportAssetsTag.ID, + JPEGTablesTag.ID, + MetadataTag.ID, + PlaceObject2Tag.ID, + PlaceObject3Tag.ID, + PlaceObject4Tag.ID, + PlaceObjectTag.ID, + ProductInfoTag.ID, + ProtectTag.ID, + RemoveObject2Tag.ID, + RemoveObjectTag.ID, + ScriptLimitsTag.ID, + SetBackgroundColorTag.ID, + SetTabIndexTag.ID, + ShowFrameTag.ID, + SoundStreamBlockTag.ID, + SoundStreamHead2Tag.ID, + SoundStreamHeadTag.ID, + StartSound2Tag.ID, + StartSoundTag.ID, + SymbolClassTag.ID, + TagStub.ID, + VideoFrameTag.ID, + DefineCompactedFont.ID, + DefineExternalGradient.ID, + DefineExternalImage.ID, + DefineExternalImage2.ID, + DefineExternalSound.ID, + DefineExternalStreamSound.ID, + DefineGradientMap.ID, + DefineSubImage.ID, + ExporterInfoTag.ID, + FontTextureInfo.ID); + knownTagIds = tagIds; + } + } + } + return knownTagIds; + } + + public static List getRequiredTags() { + if (requiredTagIds == null) { + synchronized (lockObject) { + if (requiredTagIds == null) { + List tagIds = Arrays.asList( + DefineBinaryDataTag.ID, + DefineBitsJPEG2Tag.ID, + DefineBitsJPEG3Tag.ID, + DefineBitsJPEG4Tag.ID, + DefineBitsLossless2Tag.ID, + DefineBitsLosslessTag.ID, + DefineBitsTag.ID, + DefineButton2Tag.ID, + DefineButtonCxformTag.ID, + DefineButtonSoundTag.ID, + DefineButtonTag.ID, + DefineEditTextTag.ID, + DefineFont2Tag.ID, + DefineFont3Tag.ID, + DefineFont4Tag.ID, + DefineFontAlignZonesTag.ID, + DefineFontInfo2Tag.ID, + DefineFontInfoTag.ID, + DefineFontNameTag.ID, + DefineFontTag.ID, + DefineMorphShape2Tag.ID, + DefineMorphShapeTag.ID, + DefineScalingGridTag.ID, + DefineSceneAndFrameLabelDataTag.ID, + DefineShape2Tag.ID, + DefineShape3Tag.ID, + DefineShape4Tag.ID, + DefineShapeTag.ID, + DefineSoundTag.ID, + DefineSpriteTag.ID, + DefineText2Tag.ID, + DefineTextTag.ID, + DefineVideoStreamTag.ID, + DoABCDefineTag.ID, + DoABCTag.ID, + DoActionTag.ID, + DoInitActionTag.ID, + ShowFrameTag.ID); + requiredTagIds = tagIds; + } + } + } + return requiredTagIds; + } + + /** + * Gets data bytes + * + * @return Bytes of data + */ + public byte[] getData() { + return data; + } + + public int getVersion() { + if (swf == null) { + return SWF.DEFAULT_VERSION; + } + return swf.version; + } + + /** + * Gets original read header data + * + * @return Bytes of data + */ + public byte[] getOriginalHeaderData() { + return headerData; + } + + /** + * Gets original read data + * + * @return Bytes of data + */ + public byte[] getOriginalData() { + return data; + } + + public void createOriginalData() { + data = getData(); + headerData = getHeader(data); + } + + protected byte[] getHeader(byte[] data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + + SWFOutputStream sos = new SWFOutputStream(baos, swf.version); + int tagLength = data.length; + int tagID = getId(); + int tagIDLength = (tagID << 6); + if ((tagLength <= 62) && (!forceWriteAsLong)) { + tagIDLength += tagLength; + sos.writeUI16(tagIDLength); + } else { + tagIDLength += 0x3f; + sos.writeUI16(tagIDLength); + sos.writeSI32(tagLength); + } + } catch (IOException iex) { + } + return baos.toByteArray(); + } + + public static byte[] getTagHeader(int tagIDTagLength, long tagLength, boolean writeLong, int version) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + + SWFOutputStream sos = new SWFOutputStream(baos, version); + sos.writeUI16(tagIDTagLength); + if (writeLong) { + sos.writeSI32(tagLength); + } + } catch (IOException iex) { + } + return baos.toByteArray(); + } + + /** + * Writes Tag value to the stream + * + * @param sos SWF output stream + * @throws IOException + */ + public void writeTag(SWFOutputStream sos) throws IOException { + if (isModified()) { + byte[] newData = getData(); + sos.write(getHeader(newData)); + sos.write(newData); + return; + } + + sos.write(headerData); + sos.write(data); + } + + /** + * Returns string representation of the object + * + * @return String representation of the object + */ + @Override + public String toString() { + return getName(); + } + + public final long getOrigDataLength() { + return data.length; + } + + public boolean hasSubTags() { + return false; + } + + public List getSubTags() { + return null; + } + + public long getPos() { + return pos; + } + + public void setModified(boolean value) { + modified = value; + } + + public boolean isModified() { + return modified; + } + + @Override + public void getNeededCharacters(Set needed) { + } + + @Override + public boolean removeCharacter(int characterId) { + return false; + } + + public void getNeededCharactersDeep(Set needed) { + Set visited = new HashSet<>(); + Set needed2 = new HashSet<>(); + getNeededCharacters(needed2); + + while (visited.size() != needed2.size()) { + for (Integer characterId : needed2) { + if (!visited.contains(characterId)) { + visited.add(characterId); + if (swf.characters.containsKey(characterId)) { + swf.characters.get(characterId).getNeededCharacters(needed2); + break; + } + } + } + } + + for (Integer characterId : needed2) { + if (swf.characters.containsKey(characterId)) { + needed.add(characterId); + } + } + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/UnknownTag.java b/src/com/jpexs/decompiler/flash/tags/UnknownTag.java index f32b9e67b..853b17c50 100644 --- a/src/com/jpexs/decompiler/flash/tags/UnknownTag.java +++ b/src/com/jpexs/decompiler/flash/tags/UnknownTag.java @@ -1,36 +1,36 @@ -/* - * Copyright (C) 2010-2014 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.tags; - -import com.jpexs.decompiler.flash.SWF; - -/** - * - * @author JPEXS - */ -public class UnknownTag extends Tag { - - public UnknownTag(SWF swf, int id, byte[] headerData, byte[] data, long pos) { - super(swf, id, "Unknown", headerData, data, pos); - } - - @Override - public String toString() { - return super.toString()+" (ID="+id+")"; - } - -} +/* + * Copyright (C) 2010-2014 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.tags; + +import com.jpexs.decompiler.flash.SWF; + +/** + * + * @author JPEXS + */ +public class UnknownTag extends Tag { + + public UnknownTag(SWF swf, int id, byte[] headerData, byte[] data, long pos) { + super(swf, id, "Unknown", headerData, data, pos); + } + + @Override + public String toString() { + return super.toString() + " (ID=" + id + ")"; + } + +} diff --git a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java index edfde1567..fc1936050 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java +++ b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java @@ -1,90 +1,90 @@ -/* - * Copyright (C) 2010-2014 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.tags.base; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import java.util.List; - -/** - * Object containing ASM source - * - * @author JPEXS - */ -public interface ASMSource extends TreeItem { - - /** - * Converts actions to ASM source - * - * @param exportMode PCode or hex? - * @param writer - * @param actions - * @return ASM source - * @throws java.lang.InterruptedException - */ - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException; - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - public boolean containsSource(); - - /** - * Returns actions associated with this object - * - * @return List of actions - * @throws java.lang.InterruptedException - */ - public List getActions() throws InterruptedException; - - /** - * Sets actions associated with this object - * - * @param actions Action list - */ - public void setActions(List actions); - - public void setModified(); - - public byte[] getActionBytes(); - - public void setActionBytes(byte[] actionBytes); - - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer); - - public long getPos(); - - public void addDisassemblyListener(DisassemblyListener listener); - - public void removeDisassemblyListener(DisassemblyListener listener); - - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer); - - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer); - - public int getPrefixLineCount(); - - public String removePrefixAndSuffix(String source); - - public Tag getSourceTag(); -} +/* + * Copyright (C) 2010-2014 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.tags.base; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import java.util.List; + +/** + * Object containing ASM source + * + * @author JPEXS + */ +public interface ASMSource extends TreeItem { + + /** + * Converts actions to ASM source + * + * @param exportMode PCode or hex? + * @param writer + * @param actions + * @return ASM source + * @throws java.lang.InterruptedException + */ + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException; + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + public boolean containsSource(); + + /** + * Returns actions associated with this object + * + * @return List of actions + * @throws java.lang.InterruptedException + */ + public List getActions() throws InterruptedException; + + /** + * Sets actions associated with this object + * + * @param actions Action list + */ + public void setActions(List actions); + + public void setModified(); + + public byte[] getActionBytes(); + + public void setActionBytes(byte[] actionBytes); + + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer); + + public long getPos(); + + public void addDisassemblyListener(DisassemblyListener listener); + + public void removeDisassemblyListener(DisassemblyListener listener); + + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer); + + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer); + + public int getPrefixLineCount(); + + public String removePrefixAndSuffix(String source); + + public Tag getSourceTag(); +} diff --git a/src/com/jpexs/decompiler/flash/tags/base/FontTag.java b/src/com/jpexs/decompiler/flash/tags/base/FontTag.java index bf04f5b88..130a2483c 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/FontTag.java +++ b/src/com/jpexs/decompiler/flash/tags/base/FontTag.java @@ -1,335 +1,335 @@ -/* - * Copyright (C) 2010-2014 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.tags.base; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.helpers.FontHelper; -import com.jpexs.decompiler.flash.tags.DefineFontNameTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphMetrics; -import java.awt.font.GlyphVector; -import java.awt.geom.Area; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * - * @author JPEXS - */ -public abstract class FontTag extends CharacterTag implements AloneTag, DrawableTag { - - protected final int previewSize = 500; - - public FontTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { - super(swf, id, name, headerData, data, pos); - } - - public abstract int getFontId(); - - public abstract List getGlyphShapeTable(); - - public abstract void addCharacter(char character, String fontName); - - public abstract char glyphToChar(int glyphIndex); - - public abstract int charToGlyph(char c); - - public abstract double getGlyphAdvance(int glyphIndex); - - public abstract int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex); - - public abstract int getGlyphWidth(int glyphIndex); - - public abstract String getFontName(); - - public abstract boolean isSmall(); - - public abstract boolean isBold(); - - public abstract boolean isItalic(); - - public abstract boolean isSmallEditable(); - - public abstract boolean isBoldEditable(); - - public abstract boolean isItalicEditable(); - - public abstract void setSmall(boolean value); - - public abstract void setBold(boolean value); - - public abstract void setItalic(boolean value); - - public abstract int getDivider(); - - public abstract int getAscent(); - - public abstract int getDescent(); - - public abstract int getLeading(); - - public static String[] fontNamesArray; - - public static List fontNames; - - public static String defaultFontName; - - static { - reload(); - } - - public boolean hasLayout() { - return false; - } - - public boolean containsChar(char character) { - return charToGlyph(character) > -1; - } - - public int getFontStyle() { - int fontStyle = 0; - if (isBold()) { - fontStyle |= Font.BOLD; - } - if (isItalic()) { - fontStyle |= Font.ITALIC; - } - return fontStyle; - } - - public abstract String getCharacters(List tags); - - @Override - public String getName() { - String nameAppend = ""; - if (exportName != null) { - nameAppend = ": " + exportName; - } - if (className != null) { - nameAppend = ": " + className; - } - String fontName = getFontName(); - if (fontName != null) { - nameAppend = ": " + fontName; - } - return tagName + " (" + getCharacterId() + nameAppend + ")"; - } - - public String getSystemFontName() { - Map fontPairs = Configuration.getFontPairs(); - String key = swf.getShortFileName() + "_" + getFontId() + "_" + getFontName(); - if (fontPairs.containsKey(key)) { - return fontPairs.get(key); - } - key = getFontName(); - if (fontPairs.containsKey(key)) { - return fontPairs.get(key); - } - return defaultFontName; - } - - public void shiftGlyphIndices(int fontId, int startIndex) { - List textRecords = new ArrayList<>(); - for (Tag t : swf.tags) { - if (t instanceof DefineTextTag) { - textRecords.addAll(((DefineTextTag) t).textRecords); - } - if (t instanceof DefineText2Tag) { - textRecords.addAll(((DefineText2Tag) t).textRecords); - } - } - int curFontId = 0; - for (TEXTRECORD tr : textRecords) { - if (tr.styleFlagsHasFont) { - curFontId = tr.fontId; - } - if (curFontId != fontId) { - continue; - } - for (GLYPHENTRY en : tr.glyphEntries) { - if (en == null) { //Currently edited - continue; - } - if (en.glyphIndex >= startIndex) { - en.glyphIndex++; - } - } - } - } - - public static float getSystemFontAdvance(String fontName, int fontStyle, int fontSize, Character character, Character nextCharacter) { - return getSystemFontAdvance(new Font(fontName, fontStyle, fontSize), character, nextCharacter); - } - - public static float getSystemFontAdvance(Font aFont, Character character, Character nextCharacter) { - GlyphVector gv = aFont.createGlyphVector(new FontRenderContext(aFont.getTransform(), true, true), "" + character + (nextCharacter == null ? "" : nextCharacter)); - GlyphMetrics gm = gv.getGlyphMetrics(0); - return gm.getAdvanceX(); - } - - public static void reload() { - fontNamesArray = FontHelper.getInstalledFontFamilyNames(); - fontNames = Arrays.asList(fontNamesArray); - if (fontNames.contains("Times New Roman")) { - defaultFontName = "Times New Roman"; - } else if (fontNames.contains("Arial")) { - defaultFontName = "Arial"; - } else { - defaultFontName = fontNames.get(0); - } - } - - public static String getFontNameWithFallback(String fontName) { - if (fontNames.contains(fontName)) { - return fontName; - } - if (fontNames.contains("Times New Roman")) { - return "Times New Roman"; - } - if (fontNames.contains("Arial")) { - return "Arial"; - } - //Fallback to DIALOG - return "Dialog"; - } - - public static String isFontInstalled(String fontName) { - if (fontNames.contains(fontName)) { - return fontName; - } - if (fontName.contains("_")) { - String beforeUnderscore = fontName.substring(0, fontName.indexOf('_')); - if (fontNames.contains(beforeUnderscore)) { - return beforeUnderscore; - } - } - return null; - } - - public static String findInstalledFontName(String fontName) { - if (fontNames.contains(fontName)) { - return fontName; - } - if (fontName.contains("_")) { - String beforeUnderscore = fontName.substring(0, fontName.indexOf('_')); - if (fontNames.contains(beforeUnderscore)) { - return beforeUnderscore; - } - } - return defaultFontName; - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - } - - @Override - public int getNumFrames() { - int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (frameCount < 1) { - frameCount = 1; - } - return frameCount; - } - - @Override - public boolean isSingleFrame() { - return true; - } - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - RECT r = getRect(); - return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight())); - } - - @Override - public RECT getRect() { - return new RECT(0, (int) (previewSize * SWF.unitDivisor), 0, (int) (previewSize * SWF.unitDivisor)); - } - - @Override - public String getCharacterExportFileName() { - return super.getCharacterExportFileName() + "_" + getFontName(); - } - - public DefineFontNameTag getFontNameTag() { - for (Tag t : swf.tags) { - if (t instanceof DefineFontNameTag) { - DefineFontNameTag dfn = (DefineFontNameTag) t; - if (dfn.fontId == getFontId()) { - return dfn; - } - } - } - return null; - } - - public String getCopyright() { - DefineFontNameTag dfn = getFontNameTag(); - if (dfn == null) { - return null; - } - return dfn.fontCopyright; - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - List shapes = getGlyphShapeTable(); - StringBuilder sb=new StringBuilder(); - sb.append("\tdefaultFill = textColor;\r\n"); - sb.append("\tswitch(ch){\r\n"); - for(int i=0;i. + */ +package com.jpexs.decompiler.flash.tags.base; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.helpers.FontHelper; +import com.jpexs.decompiler.flash.tags.DefineFontNameTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphMetrics; +import java.awt.font.GlyphVector; +import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * + * @author JPEXS + */ +public abstract class FontTag extends CharacterTag implements AloneTag, DrawableTag { + + protected final int previewSize = 500; + + public FontTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { + super(swf, id, name, headerData, data, pos); + } + + public abstract int getFontId(); + + public abstract List getGlyphShapeTable(); + + public abstract void addCharacter(char character, String fontName); + + public abstract char glyphToChar(int glyphIndex); + + public abstract int charToGlyph(char c); + + public abstract double getGlyphAdvance(int glyphIndex); + + public abstract int getGlyphKerningAdjustment(int glyphIndex, int nextGlyphIndex); + + public abstract int getGlyphWidth(int glyphIndex); + + public abstract String getFontName(); + + public abstract boolean isSmall(); + + public abstract boolean isBold(); + + public abstract boolean isItalic(); + + public abstract boolean isSmallEditable(); + + public abstract boolean isBoldEditable(); + + public abstract boolean isItalicEditable(); + + public abstract void setSmall(boolean value); + + public abstract void setBold(boolean value); + + public abstract void setItalic(boolean value); + + public abstract int getDivider(); + + public abstract int getAscent(); + + public abstract int getDescent(); + + public abstract int getLeading(); + + public static String[] fontNamesArray; + + public static List fontNames; + + public static String defaultFontName; + + static { + reload(); + } + + public boolean hasLayout() { + return false; + } + + public boolean containsChar(char character) { + return charToGlyph(character) > -1; + } + + public int getFontStyle() { + int fontStyle = 0; + if (isBold()) { + fontStyle |= Font.BOLD; + } + if (isItalic()) { + fontStyle |= Font.ITALIC; + } + return fontStyle; + } + + public abstract String getCharacters(List tags); + + @Override + public String getName() { + String nameAppend = ""; + if (exportName != null) { + nameAppend = ": " + exportName; + } + if (className != null) { + nameAppend = ": " + className; + } + String fontName = getFontName(); + if (fontName != null) { + nameAppend = ": " + fontName; + } + return tagName + " (" + getCharacterId() + nameAppend + ")"; + } + + public String getSystemFontName() { + Map fontPairs = Configuration.getFontPairs(); + String key = swf.getShortFileName() + "_" + getFontId() + "_" + getFontName(); + if (fontPairs.containsKey(key)) { + return fontPairs.get(key); + } + key = getFontName(); + if (fontPairs.containsKey(key)) { + return fontPairs.get(key); + } + return defaultFontName; + } + + public void shiftGlyphIndices(int fontId, int startIndex) { + List textRecords = new ArrayList<>(); + for (Tag t : swf.tags) { + if (t instanceof DefineTextTag) { + textRecords.addAll(((DefineTextTag) t).textRecords); + } + if (t instanceof DefineText2Tag) { + textRecords.addAll(((DefineText2Tag) t).textRecords); + } + } + int curFontId = 0; + for (TEXTRECORD tr : textRecords) { + if (tr.styleFlagsHasFont) { + curFontId = tr.fontId; + } + if (curFontId != fontId) { + continue; + } + for (GLYPHENTRY en : tr.glyphEntries) { + if (en == null) { //Currently edited + continue; + } + if (en.glyphIndex >= startIndex) { + en.glyphIndex++; + } + } + } + } + + public static float getSystemFontAdvance(String fontName, int fontStyle, int fontSize, Character character, Character nextCharacter) { + return getSystemFontAdvance(new Font(fontName, fontStyle, fontSize), character, nextCharacter); + } + + public static float getSystemFontAdvance(Font aFont, Character character, Character nextCharacter) { + GlyphVector gv = aFont.createGlyphVector(new FontRenderContext(aFont.getTransform(), true, true), "" + character + (nextCharacter == null ? "" : nextCharacter)); + GlyphMetrics gm = gv.getGlyphMetrics(0); + return gm.getAdvanceX(); + } + + public static void reload() { + fontNamesArray = FontHelper.getInstalledFontFamilyNames(); + fontNames = Arrays.asList(fontNamesArray); + if (fontNames.contains("Times New Roman")) { + defaultFontName = "Times New Roman"; + } else if (fontNames.contains("Arial")) { + defaultFontName = "Arial"; + } else { + defaultFontName = fontNames.get(0); + } + } + + public static String getFontNameWithFallback(String fontName) { + if (fontNames.contains(fontName)) { + return fontName; + } + if (fontNames.contains("Times New Roman")) { + return "Times New Roman"; + } + if (fontNames.contains("Arial")) { + return "Arial"; + } + //Fallback to DIALOG + return "Dialog"; + } + + public static String isFontInstalled(String fontName) { + if (fontNames.contains(fontName)) { + return fontName; + } + if (fontName.contains("_")) { + String beforeUnderscore = fontName.substring(0, fontName.indexOf('_')); + if (fontNames.contains(beforeUnderscore)) { + return beforeUnderscore; + } + } + return null; + } + + public static String findInstalledFontName(String fontName) { + if (fontNames.contains(fontName)) { + return fontName; + } + if (fontName.contains("_")) { + String beforeUnderscore = fontName.substring(0, fontName.indexOf('_')); + if (fontNames.contains(beforeUnderscore)) { + return beforeUnderscore; + } + } + return defaultFontName; + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + } + + @Override + public int getNumFrames() { + int frameCount = (getGlyphShapeTable().size() - 1) / SHAPERECORD.MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (frameCount < 1) { + frameCount = 1; + } + return frameCount; + } + + @Override + public boolean isSingleFrame() { + return true; + } + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + RECT r = getRect(); + return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight())); + } + + @Override + public RECT getRect() { + return new RECT(0, (int) (previewSize * SWF.unitDivisor), 0, (int) (previewSize * SWF.unitDivisor)); + } + + @Override + public String getCharacterExportFileName() { + return super.getCharacterExportFileName() + "_" + getFontName(); + } + + public DefineFontNameTag getFontNameTag() { + for (Tag t : swf.tags) { + if (t instanceof DefineFontNameTag) { + DefineFontNameTag dfn = (DefineFontNameTag) t; + if (dfn.fontId == getFontId()) { + return dfn; + } + } + } + return null; + } + + public String getCopyright() { + DefineFontNameTag dfn = getFontNameTag(); + if (dfn == null) { + return null; + } + return dfn.fontCopyright; + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + List shapes = getGlyphShapeTable(); + StringBuilder sb = new StringBuilder(); + sb.append("\tdefaultFill = textColor;\r\n"); + sb.append("\tswitch(ch){\r\n"); + for (int i = 0; i < shapes.size(); i++) { + char c = glyphToChar(i); + String cs = "" + c; + cs = cs.replace("\\", "\\\\").replace("\"", "\\\""); + sb.append("\t\tcase \"").append(cs).append("\":\r\n"); + CanvasShapeExporter exporter = new CanvasShapeExporter(null, unitDivisor, swf, shapes.get(i), new ColorTransform(), 0, 0); + exporter.export(); + sb.append("\t\t").append(exporter.getShapeData().replaceAll("\r\n", "\r\n\t\t")); + sb.append("\tbreak;\r\n"); + } + sb.append("\t}\r\n"); + return sb.toString(); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/base/NeedsCharacters.java b/src/com/jpexs/decompiler/flash/tags/base/NeedsCharacters.java index ea1949512..123e28453 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/NeedsCharacters.java +++ b/src/com/jpexs/decompiler/flash/tags/base/NeedsCharacters.java @@ -1,28 +1,30 @@ -/* - * Copyright (C) 2010-2014 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.tags.base; - -import java.util.Set; - -/** - * - * @author JPEXS - */ -public interface NeedsCharacters { - - public Set getNeededCharacters(); -} +/* + * Copyright (C) 2010-2014 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.tags.base; + +import java.util.Set; + +/** + * + * @author JPEXS + */ +public interface NeedsCharacters { + + public void getNeededCharacters(Set needed); + + public boolean removeCharacter(int characterId); +} diff --git a/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java b/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java index 6ef246c2c..17ea0f357 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java +++ b/src/com/jpexs/decompiler/flash/tags/base/ShapeTag.java @@ -1,79 +1,79 @@ -/* - * Copyright (C) 2010-2014 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.tags.base; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.helpers.SerializableImage; -import java.awt.Shape; -import java.io.IOException; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public abstract class ShapeTag extends CharacterTag implements BoundedTag, DrawableTag { - - public ShapeTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { - super(swf, id, name, headerData, data, pos); - } - - public abstract SHAPEWITHSTYLE getShapes(); - - public abstract int getShapeNum(); - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - return transformation.toTransform().createTransformedShape(getShapes().getOutline()); - } - - @Override - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - BitmapExporter.export(swf, getShapes(), null, image, transformation, colorTransform); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), exporter, null, colorTransform); - shapeExporter.export(); - } - - @Override - public String toHtmlCanvas(double unitDivisor) { - CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShapes(), new ColorTransform(), 0, 0); - cse.export(); - return cse.getShapeData(); - } - - @Override - public Set getNeededCharacters() { - Set ret= super.getNeededCharacters(); - ret.addAll(getShapes().getNeededCharacters()); - return ret; - } - - - -} +/* + * Copyright (C) 2010-2014 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.tags.base; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.helpers.SerializableImage; +import java.awt.Shape; +import java.io.IOException; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public abstract class ShapeTag extends CharacterTag implements BoundedTag, DrawableTag { + + public ShapeTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { + super(swf, id, name, headerData, data, pos); + } + + public abstract SHAPEWITHSTYLE getShapes(); + + public abstract int getShapeNum(); + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + return transformation.toTransform().createTransformedShape(getShapes().getOutline()); + } + + @Override + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + BitmapExporter.export(swf, getShapes(), null, image, transformation, colorTransform); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) throws IOException { + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, getShapes(), exporter, null, colorTransform); + shapeExporter.export(); + } + + @Override + public String toHtmlCanvas(double unitDivisor) { + CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, getShapes(), new ColorTransform(), 0, 0); + cse.export(); + return cse.getShapeData(); + } + + @Override + public void getNeededCharacters(Set needed) { + getShapes().getNeededCharacters(needed); + } + + @Override + public boolean removeCharacter(int characterId) { + return getShapes().removeCharacter(characterId); + } +} diff --git a/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index b30368e3d..d1afbe046 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -1,490 +1,489 @@ -/* - * Copyright (C) 2010-2014 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.tags.base; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.FontExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.text.ParseException; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.font.LineMetrics; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.w3c.dom.Element; - -/** - * - * @author JPEXS - */ -public abstract class TextTag extends CharacterTag implements BoundedTag, DrawableTag { - - public TextTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { - super(swf, id, name, headerData, data, pos); - } - - public abstract MATRIX getTextMatrix(); - - public abstract String getText(String separator); - - public abstract List getFontIds(); - - public abstract String getFormattedText(); - - // use the texts from the "texts" argument when it is not null - public abstract boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException; - - @Override - public abstract int getCharacterId(); - - public abstract RECT getBounds(); - - public abstract void setBounds(RECT r); - - private static void updateRect(RECT ret, int x, int y) { - if (x < ret.Xmin) { - ret.Xmin = x; - } - if (x > ret.Xmax) { - ret.Xmax = x; - } - if (y < ret.Ymin) { - ret.Ymin = y; - } - if (y > ret.Ymax) { - ret.Ymax = y; - } - } - - public static Map getTextRecordsAttributes(List list, List tags) { - Map att = new HashMap<>(); - RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - FontTag font = null; - int x = 0; - int y = 0; - int textHeight = 12; - int lineSpacing = 0; - double leading = 0; - double ascent = 0; - double descent = 0; - double lineDistance = 0; - - List glyphs = new ArrayList<>(); - boolean firstLine = true; - double top = 0; - List allLeftMargins = new ArrayList<>(); - List allLetterSpacings = new ArrayList<>(); - FontMetrics fontMetrics; - BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); - Graphics graphics = bi.getGraphics(); - Font aFont = null; - int currentLeftMargin; - for (int r = 0; r < list.size(); r++) { - TEXTRECORD rec = list.get(r); - if (rec.styleFlagsHasFont) { - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag ft = (FontTag) t; - if (ft.getFontId() == rec.fontId) { - font = ft; - } - } - } - textHeight = rec.textHeight; - glyphs = font.getGlyphShapeTable(); - - if (!font.hasLayout()) { - String fontName = FontTag.getFontNameWithFallback(font.getFontName()); - aFont = new Font(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor)); - fontMetrics = graphics.getFontMetrics(aFont); - LineMetrics lm = fontMetrics.getLineMetrics("A", graphics); - ascent = lm.getAscent(); - descent = lm.getDescent(); - leading = lm.getLeading(); - lineDistance = ascent + descent; - } else { - leading = ((double) font.getLeading() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - ascent = ((double) font.getAscent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - descent = ((double) font.getDescent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - lineDistance = ascent + descent; - } - } - currentLeftMargin = 0; - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - currentLeftMargin = x; - } - if (rec.styleFlagsHasYOffset) { - if (!firstLine) { - top += ascent + descent; - int topint = (int) (Math.round(top)*SWF.unitDivisor); - lineSpacing = rec.yOffset - topint; - top += lineSpacing/SWF.unitDivisor; - } else { - top = ascent; - } - y = rec.yOffset; - } - firstLine = false; - allLeftMargins.add(currentLeftMargin); - int letterSpacing = 0; - for (int e = 0; e < rec.glyphEntries.length; e++) { - GLYPHENTRY entry = rec.glyphEntries[e]; - GLYPHENTRY nextEntry = null; - if (e < rec.glyphEntries.length - 1) { - nextEntry = rec.glyphEntries[e + 1]; - } - RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); - rect.Xmax = (int) Math.round(((double) rect.Xmax * textHeight) / (font.getDivider() * 1024)); - rect.Xmin = (int) Math.round(((double) rect.Xmin * textHeight) / (font.getDivider() * 1024)); - rect.Ymax = (int) Math.round(((double) rect.Ymax * textHeight) / (font.getDivider() * 1024)); - rect.Ymin = (int) Math.round(((double) rect.Ymin * textHeight) / (font.getDivider() * 1024)); - updateRect(textBounds, x + rect.Xmin, y + rect.Ymin); - updateRect(textBounds, x + rect.Xmax, y + rect.Ymax); - int adv = entry.glyphAdvance; - - int defaultAdvance; - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextEntry != null) { - kerningAdjustment = font.getGlyphKerningAdjustment(entry.glyphIndex, nextEntry.glyphIndex); - } - defaultAdvance = (int) Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / 1024.0); - } else { - defaultAdvance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(aFont, font.glyphToChar(entry.glyphIndex), nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex))); - } - letterSpacing = adv - defaultAdvance; - x += adv/(font.getDivider()); - } - allLetterSpacings.add(letterSpacing); - } - att.put("indent", 0); //? - att.put("rightMargin", 0); //? - att.put("lineSpacing", lineSpacing); - att.put("textBounds", textBounds); - att.put("allLeftMargins", allLeftMargins); - att.put("allLetterSpacings", allLetterSpacings); - return att; - } - - public static SHAPE getBorderShape(RGB borderColor, RGB fillColor, RECT rect) { - SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); - shape.fillStyles = new FILLSTYLEARRAY(); - if (fillColor != null) { - shape.fillStyles.fillStyles = new FILLSTYLE[1]; - FILLSTYLE fillStyle = new FILLSTYLE(); - fillStyle.fillStyleType = FILLSTYLE.SOLID; - fillStyle.color = fillColor; - shape.fillStyles.fillStyles[0] = fillStyle; - } else { - shape.fillStyles.fillStyles = new FILLSTYLE[0]; - } - shape.lineStyles = new LINESTYLEARRAY(); - shape.lineStyles.lineStyles = new LINESTYLE[1]; - LINESTYLE lineStyle = new LINESTYLE(); - lineStyle.color = borderColor; - lineStyle.width = 20; - shape.lineStyles.lineStyles[0] = lineStyle; - shape.shapeRecords = new ArrayList<>(); - StyleChangeRecord style = new StyleChangeRecord(); - style.lineStyle = 1; - style.stateLineStyle = true; - if (fillColor != null) { - style.stateFillStyle0 = true; - style.fillStyle0 = 1; - } - style.stateMoveTo = true; - shape.shapeRecords.add(style); - StraightEdgeRecord top = new StraightEdgeRecord(); - top.generalLineFlag = true; - top.deltaX = rect.getWidth(); - StraightEdgeRecord right = new StraightEdgeRecord(); - right.generalLineFlag = true; - right.deltaY = rect.getHeight(); - StraightEdgeRecord bottom = new StraightEdgeRecord(); - bottom.generalLineFlag = true; - bottom.deltaX = -rect.getWidth(); - StraightEdgeRecord left = new StraightEdgeRecord(); - left.generalLineFlag = true; - left.deltaY = -rect.getHeight(); - shape.shapeRecords.add(top); - shape.shapeRecords.add(right); - shape.shapeRecords.add(bottom); - shape.shapeRecords.add(left); - shape.shapeRecords.add(new EndShapeRecord()); - return shape; - } - - public static void drawBorder(SWF swf, SerializableImage image, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { - Graphics2D g = (Graphics2D) image.getGraphics(); - Matrix mat = transformation.clone(); - mat = mat.concatenate(new Matrix(textMatrix)); - BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, colorTransform); - } - - public static void staticTextToImage(SWF swf, List textRecords, int numText, SerializableImage image, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { - Color textColor = new Color(0, 0, 0); - FontTag font = null; - int textHeight = 12; - int x = 0; - int y = 0; - List glyphs = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = colorTransform.apply(rec.textColorA.toColor()); - } else { - textColor = colorTransform.apply(rec.textColor.toColor()); - } - } - if (rec.styleFlagsHasFont) { - font = (FontTag) swf.characters.get(rec.fontId); - glyphs = font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - y = rec.yOffset; - } - - double rat = textHeight / 1024.0 / font.getDivider(); - - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = transformation.clone(); - mat = mat.concatenate(new Matrix(textMatrix)); - Matrix matTr = Matrix.getTranslateInstance(x, y); - mat = mat.concatenate(matTr); - mat = mat.concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1) { - // shapeNum: 1 - SHAPE shape = glyphs.get(entry.glyphIndex); - BitmapExporter.export(swf, shape, textColor, image, mat, colorTransform); - x += entry.glyphAdvance; - } - } - } - } - - public static String staticTextToHtmlCanvas(double unitDivisor, SWF swf, List textRecords, int numText, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { - Color textColor = new Color(0, 0, 0); - String ret = ""; - FontTag font = null; - int fontId = -1; - int textHeight = 12; - int x = 0; - int y = 0; - - List glyphs = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = colorTransform.apply(rec.textColorA.toColor()); - } else { - textColor = colorTransform.apply(rec.textColor.toColor()); - } - } - if (rec.styleFlagsHasFont) { - font = (FontTag) swf.characters.get(rec.fontId); - fontId = rec.fontId; - glyphs = font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - y = rec.yOffset; - } - - double rat = textHeight / 1024.0 / font.getDivider(); - - - ret += "\tvar textColor = "+CanvasShapeExporter.color(textColor)+";\r\n"; - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = (new Matrix(textMatrix).concatenate(Matrix.getTranslateInstance(x, y))).concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1) { - // shapeNum: 1 - ret += "\tctx.save();\r\n"; - ret += "\tctx.transform(" + mat.scaleX + "," + mat.rotateSkew0 + "," + mat.rotateSkew1 + "," + mat.scaleY + "," + mat.translateX + "," + mat.translateY + ");\r\n"; - ret += "\tfont"+fontId+"(ctx,\""+(""+font.glyphToChar(entry.glyphIndex)).replace("\\", "\\\\").replace("\"", "\\\"")+"\",textColor);\r\n"; - ret += "\tctx.restore();\r\n"; - x += entry.glyphAdvance; - } - } - } - return ret; - } - - public static void staticTextToSVG(SWF swf, List textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { - Color textColor = new Color(0, 0, 0); - FontTag font = null; - int textHeight = 12; - int x = 0; - int y = 0; - List glyphs = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = colorTransform.apply(rec.textColorA.toColor()); - } else { - textColor = colorTransform.apply(rec.textColor.toColor()); - } - } - if (rec.styleFlagsHasFont) { - font = (FontTag) swf.characters.get(rec.fontId); - glyphs = font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - int offsetX = 0; - int offsetY = 0; - if (rec.styleFlagsHasXOffset) { - offsetX = rec.xOffset; - x = offsetX; - } - if (rec.styleFlagsHasYOffset) { - offsetY = rec.yOffset; - y = offsetY; - } - - double rat = textHeight / 1024.0 / font.getDivider(); - - exporter.createSubGroup(new Matrix(textMatrix), null); - if (exporter.useTextTag) { - StringBuilder text = new StringBuilder(); - int totalAdvance = 0; - for (GLYPHENTRY entry : rec.glyphEntries) { - if (entry.glyphIndex != -1) { - char ch = font.glyphToChar(entry.glyphIndex); - text.append(ch); - totalAdvance += entry.glyphAdvance; - } - } - - boolean hasOffset = offsetX != 0 || offsetY != 0; - if (hasOffset) { - exporter.createSubGroup(Matrix.getTranslateInstance(offsetX, offsetY), null); - } - - Element textElement = exporter.createElement("text"); - textElement.setAttribute("font-size", Double.toString(rat * 1024)); - textElement.setAttribute("font-family", font.getFontName()); - textElement.setAttribute("textLength", Double.toString(totalAdvance / SWF.unitDivisor)); - textElement.setAttribute("lengthAdjust", "spacing"); - textElement.setTextContent(text.toString()); - - if (textColor != null) { - RGBA colorA = new RGBA(textColor); - textElement.setAttribute("fill", colorA.toHexRGB()); - if (colorA.alpha != 255) { - textElement.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); - } - } - - exporter.addToGroup(textElement); - FontExportMode fontExportMode = FontExportMode.WOFF; - exporter.addStyle(font.getFontName(), new FontExporter().exportFont(font, fontExportMode), fontExportMode); - - if (hasOffset) { - exporter.endGroup(); - } - } else { - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1) { - // shapeNum: 1 - SHAPE shape = glyphs.get(entry.glyphIndex); - char ch = font.glyphToChar(entry.glyphIndex); - - String charId = null; - Map chs; - if (exporter.exportedChars.containsKey(font)) { - chs = exporter.exportedChars.get(font); - if (chs.containsKey(ch)) { - charId = chs.get(ch); - } - } else { - chs = new HashMap<>(); - exporter.exportedChars.put(font, chs); - } - - if (charId == null) { - charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontName() + "_" + ch)); - exporter.createDefGroup(null, charId); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform); - shapeExporter.export(); - exporter.endGroup(); - chs.put(ch, charId); - } - - Element charImage = exporter.addUse(mat, bounds, charId); - if (textColor != null) { - RGBA colorA = new RGBA(textColor); - charImage.setAttribute("fill", colorA.toHexRGB()); - if (colorA.alpha != 255) { - charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); - } - } - x += entry.glyphAdvance; - } - } - } - exporter.endGroup(); - } - } - - @Override - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - RECT r = getBounds(); - return new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()); //TODO: match character shapes - } -} +/* + * Copyright (C) 2010-2014 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.tags.base; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.text.ParseException; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.LineMetrics; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.w3c.dom.Element; + +/** + * + * @author JPEXS + */ +public abstract class TextTag extends CharacterTag implements BoundedTag, DrawableTag { + + public TextTag(SWF swf, int id, String name, byte[] headerData, byte[] data, long pos) { + super(swf, id, name, headerData, data, pos); + } + + public abstract MATRIX getTextMatrix(); + + public abstract String getText(String separator); + + public abstract List getFontIds(); + + public abstract String getFormattedText(); + + // use the texts from the "texts" argument when it is not null + public abstract boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws ParseException; + + @Override + public abstract int getCharacterId(); + + public abstract RECT getBounds(); + + public abstract void setBounds(RECT r); + + private static void updateRect(RECT ret, int x, int y) { + if (x < ret.Xmin) { + ret.Xmin = x; + } + if (x > ret.Xmax) { + ret.Xmax = x; + } + if (y < ret.Ymin) { + ret.Ymin = y; + } + if (y > ret.Ymax) { + ret.Ymax = y; + } + } + + public static Map getTextRecordsAttributes(List list, List tags) { + Map att = new HashMap<>(); + RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + FontTag font = null; + int x = 0; + int y = 0; + int textHeight = 12; + int lineSpacing = 0; + double leading = 0; + double ascent = 0; + double descent = 0; + double lineDistance = 0; + + List glyphs = new ArrayList<>(); + boolean firstLine = true; + double top = 0; + List allLeftMargins = new ArrayList<>(); + List allLetterSpacings = new ArrayList<>(); + FontMetrics fontMetrics; + BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + Graphics graphics = bi.getGraphics(); + Font aFont = null; + int currentLeftMargin; + for (int r = 0; r < list.size(); r++) { + TEXTRECORD rec = list.get(r); + if (rec.styleFlagsHasFont) { + for (Tag t : tags) { + if (t instanceof FontTag) { + FontTag ft = (FontTag) t; + if (ft.getFontId() == rec.fontId) { + font = ft; + } + } + } + textHeight = rec.textHeight; + glyphs = font.getGlyphShapeTable(); + + if (!font.hasLayout()) { + String fontName = FontTag.getFontNameWithFallback(font.getFontName()); + aFont = new Font(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor)); + fontMetrics = graphics.getFontMetrics(aFont); + LineMetrics lm = fontMetrics.getLineMetrics("A", graphics); + ascent = lm.getAscent(); + descent = lm.getDescent(); + leading = lm.getLeading(); + lineDistance = ascent + descent; + } else { + leading = ((double) font.getLeading() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + ascent = ((double) font.getAscent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + descent = ((double) font.getDescent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + lineDistance = ascent + descent; + } + } + currentLeftMargin = 0; + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + currentLeftMargin = x; + } + if (rec.styleFlagsHasYOffset) { + if (!firstLine) { + top += ascent + descent; + int topint = (int) (Math.round(top) * SWF.unitDivisor); + lineSpacing = rec.yOffset - topint; + top += lineSpacing / SWF.unitDivisor; + } else { + top = ascent; + } + y = rec.yOffset; + } + firstLine = false; + allLeftMargins.add(currentLeftMargin); + int letterSpacing = 0; + for (int e = 0; e < rec.glyphEntries.length; e++) { + GLYPHENTRY entry = rec.glyphEntries[e]; + GLYPHENTRY nextEntry = null; + if (e < rec.glyphEntries.length - 1) { + nextEntry = rec.glyphEntries[e + 1]; + } + RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); + rect.Xmax = (int) Math.round(((double) rect.Xmax * textHeight) / (font.getDivider() * 1024)); + rect.Xmin = (int) Math.round(((double) rect.Xmin * textHeight) / (font.getDivider() * 1024)); + rect.Ymax = (int) Math.round(((double) rect.Ymax * textHeight) / (font.getDivider() * 1024)); + rect.Ymin = (int) Math.round(((double) rect.Ymin * textHeight) / (font.getDivider() * 1024)); + updateRect(textBounds, x + rect.Xmin, y + rect.Ymin); + updateRect(textBounds, x + rect.Xmax, y + rect.Ymax); + int adv = entry.glyphAdvance; + + int defaultAdvance; + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextEntry != null) { + kerningAdjustment = font.getGlyphKerningAdjustment(entry.glyphIndex, nextEntry.glyphIndex); + } + defaultAdvance = (int) Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / 1024.0); + } else { + defaultAdvance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(aFont, font.glyphToChar(entry.glyphIndex), nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex))); + } + letterSpacing = adv - defaultAdvance; + x += adv / (font.getDivider()); + } + allLetterSpacings.add(letterSpacing); + } + att.put("indent", 0); //? + att.put("rightMargin", 0); //? + att.put("lineSpacing", lineSpacing); + att.put("textBounds", textBounds); + att.put("allLeftMargins", allLeftMargins); + att.put("allLetterSpacings", allLetterSpacings); + return att; + } + + public static SHAPE getBorderShape(RGB borderColor, RGB fillColor, RECT rect) { + SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); + shape.fillStyles = new FILLSTYLEARRAY(); + if (fillColor != null) { + shape.fillStyles.fillStyles = new FILLSTYLE[1]; + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.fillStyleType = FILLSTYLE.SOLID; + fillStyle.color = fillColor; + shape.fillStyles.fillStyles[0] = fillStyle; + } else { + shape.fillStyles.fillStyles = new FILLSTYLE[0]; + } + shape.lineStyles = new LINESTYLEARRAY(); + shape.lineStyles.lineStyles = new LINESTYLE[1]; + LINESTYLE lineStyle = new LINESTYLE(); + lineStyle.color = borderColor; + lineStyle.width = 20; + shape.lineStyles.lineStyles[0] = lineStyle; + shape.shapeRecords = new ArrayList<>(); + StyleChangeRecord style = new StyleChangeRecord(); + style.lineStyle = 1; + style.stateLineStyle = true; + if (fillColor != null) { + style.stateFillStyle0 = true; + style.fillStyle0 = 1; + } + style.stateMoveTo = true; + shape.shapeRecords.add(style); + StraightEdgeRecord top = new StraightEdgeRecord(); + top.generalLineFlag = true; + top.deltaX = rect.getWidth(); + StraightEdgeRecord right = new StraightEdgeRecord(); + right.generalLineFlag = true; + right.deltaY = rect.getHeight(); + StraightEdgeRecord bottom = new StraightEdgeRecord(); + bottom.generalLineFlag = true; + bottom.deltaX = -rect.getWidth(); + StraightEdgeRecord left = new StraightEdgeRecord(); + left.generalLineFlag = true; + left.deltaY = -rect.getHeight(); + shape.shapeRecords.add(top); + shape.shapeRecords.add(right); + shape.shapeRecords.add(bottom); + shape.shapeRecords.add(left); + shape.shapeRecords.add(new EndShapeRecord()); + return shape; + } + + public static void drawBorder(SWF swf, SerializableImage image, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { + Graphics2D g = (Graphics2D) image.getGraphics(); + Matrix mat = transformation.clone(); + mat = mat.concatenate(new Matrix(textMatrix)); + BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, colorTransform); + } + + public static void staticTextToImage(SWF swf, List textRecords, int numText, SerializableImage image, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { + Color textColor = new Color(0, 0, 0); + FontTag font = null; + int textHeight = 12; + int x = 0; + int y = 0; + List glyphs = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = colorTransform.apply(rec.textColorA.toColor()); + } else { + textColor = colorTransform.apply(rec.textColor.toColor()); + } + } + if (rec.styleFlagsHasFont) { + font = (FontTag) swf.characters.get(rec.fontId); + glyphs = font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + y = rec.yOffset; + } + + double rat = textHeight / 1024.0 / font.getDivider(); + + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = transformation.clone(); + mat = mat.concatenate(new Matrix(textMatrix)); + Matrix matTr = Matrix.getTranslateInstance(x, y); + mat = mat.concatenate(matTr); + mat = mat.concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1) { + // shapeNum: 1 + SHAPE shape = glyphs.get(entry.glyphIndex); + BitmapExporter.export(swf, shape, textColor, image, mat, colorTransform); + x += entry.glyphAdvance; + } + } + } + } + + public static String staticTextToHtmlCanvas(double unitDivisor, SWF swf, List textRecords, int numText, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { + Color textColor = new Color(0, 0, 0); + String ret = ""; + FontTag font = null; + int fontId = -1; + int textHeight = 12; + int x = 0; + int y = 0; + + List glyphs = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = colorTransform.apply(rec.textColorA.toColor()); + } else { + textColor = colorTransform.apply(rec.textColor.toColor()); + } + } + if (rec.styleFlagsHasFont) { + font = (FontTag) swf.characters.get(rec.fontId); + fontId = rec.fontId; + glyphs = font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + y = rec.yOffset; + } + + double rat = textHeight / 1024.0 / font.getDivider(); + + ret += "\tvar textColor = " + CanvasShapeExporter.color(textColor) + ";\r\n"; + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = (new Matrix(textMatrix).concatenate(Matrix.getTranslateInstance(x, y))).concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1) { + // shapeNum: 1 + ret += "\tctx.save();\r\n"; + ret += "\tctx.transform(" + mat.scaleX + "," + mat.rotateSkew0 + "," + mat.rotateSkew1 + "," + mat.scaleY + "," + mat.translateX + "," + mat.translateY + ");\r\n"; + ret += "\tfont" + fontId + "(ctx,\"" + ("" + font.glyphToChar(entry.glyphIndex)).replace("\\", "\\\\").replace("\"", "\\\"") + "\",textColor);\r\n"; + ret += "\tctx.restore();\r\n"; + x += entry.glyphAdvance; + } + } + } + return ret; + } + + public static void staticTextToSVG(SWF swf, List textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { + Color textColor = new Color(0, 0, 0); + FontTag font = null; + int textHeight = 12; + int x = 0; + int y = 0; + List glyphs = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = colorTransform.apply(rec.textColorA.toColor()); + } else { + textColor = colorTransform.apply(rec.textColor.toColor()); + } + } + if (rec.styleFlagsHasFont) { + font = (FontTag) swf.characters.get(rec.fontId); + glyphs = font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + int offsetX = 0; + int offsetY = 0; + if (rec.styleFlagsHasXOffset) { + offsetX = rec.xOffset; + x = offsetX; + } + if (rec.styleFlagsHasYOffset) { + offsetY = rec.yOffset; + y = offsetY; + } + + double rat = textHeight / 1024.0 / font.getDivider(); + + exporter.createSubGroup(new Matrix(textMatrix), null); + if (exporter.useTextTag) { + StringBuilder text = new StringBuilder(); + int totalAdvance = 0; + for (GLYPHENTRY entry : rec.glyphEntries) { + if (entry.glyphIndex != -1) { + char ch = font.glyphToChar(entry.glyphIndex); + text.append(ch); + totalAdvance += entry.glyphAdvance; + } + } + + boolean hasOffset = offsetX != 0 || offsetY != 0; + if (hasOffset) { + exporter.createSubGroup(Matrix.getTranslateInstance(offsetX, offsetY), null); + } + + Element textElement = exporter.createElement("text"); + textElement.setAttribute("font-size", Double.toString(rat * 1024)); + textElement.setAttribute("font-family", font.getFontName()); + textElement.setAttribute("textLength", Double.toString(totalAdvance / SWF.unitDivisor)); + textElement.setAttribute("lengthAdjust", "spacing"); + textElement.setTextContent(text.toString()); + + if (textColor != null) { + RGBA colorA = new RGBA(textColor); + textElement.setAttribute("fill", colorA.toHexRGB()); + if (colorA.alpha != 255) { + textElement.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); + } + } + + exporter.addToGroup(textElement); + FontExportMode fontExportMode = FontExportMode.WOFF; + exporter.addStyle(font.getFontName(), new FontExporter().exportFont(font, fontExportMode), fontExportMode); + + if (hasOffset) { + exporter.endGroup(); + } + } else { + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1) { + // shapeNum: 1 + SHAPE shape = glyphs.get(entry.glyphIndex); + char ch = font.glyphToChar(entry.glyphIndex); + + String charId = null; + Map chs; + if (exporter.exportedChars.containsKey(font)) { + chs = exporter.exportedChars.get(font); + if (chs.containsKey(ch)) { + charId = chs.get(ch); + } + } else { + chs = new HashMap<>(); + exporter.exportedChars.put(font, chs); + } + + if (charId == null) { + charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontName() + "_" + ch)); + exporter.createDefGroup(null, charId); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform); + shapeExporter.export(); + exporter.endGroup(); + chs.put(ch, charId); + } + + Element charImage = exporter.addUse(mat, bounds, charId); + if (textColor != null) { + RGBA colorA = new RGBA(textColor); + charImage.setAttribute("fill", colorA.toHexRGB()); + if (colorA.alpha != 255) { + charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); + } + } + x += entry.glyphAdvance; + } + } + } + exporter.endGroup(); + } + } + + @Override + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + RECT r = getBounds(); + return new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()); //TODO: match character shapes + } +} diff --git a/src/com/jpexs/decompiler/flash/timeline/Frame.java b/src/com/jpexs/decompiler/flash/timeline/Frame.java index 33385416e..5344aa9b8 100644 --- a/src/com/jpexs/decompiler/flash/timeline/Frame.java +++ b/src/com/jpexs/decompiler/flash/timeline/Frame.java @@ -1,52 +1,54 @@ -/* - * Copyright (C) 2010-2014 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.timeline; - -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import java.util.ArrayList; -import java.util.List; -import java.util.TreeMap; - -/** - * - * @author JPEXS - */ -public class Frame { - - public TreeMap layers = new TreeMap<>(); - public DoActionTag action; - public RGB backgroundColor = new RGBA(0, 0, 0, 0); - public Timeline timeline; - public List sounds = new ArrayList<>(); - public List soundClasses = new ArrayList<>(); - - public Frame(Timeline timeline) { - this.timeline = timeline; - } - - public Frame(Frame obj) { - layers = new TreeMap<>(); - backgroundColor = obj.backgroundColor; - timeline = obj.timeline; - for (int depth : obj.layers.keySet()) { - layers.put(depth, new DepthState(obj.layers.get(depth), this, true)); - } - //Do not copy sounds - } -} +/* + * Copyright (C) 2010-2014 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.timeline; + +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +/** + * + * @author JPEXS + */ +public class Frame { + + public TreeMap layers = new TreeMap<>(); + public DoActionTag action; + public RGB backgroundColor = new RGBA(0, 0, 0, 0); + public Timeline timeline; + public List sounds = new ArrayList<>(); + public List soundClasses = new ArrayList<>(); + public List innerTags = new ArrayList<>(); + + public Frame(Timeline timeline) { + this.timeline = timeline; + } + + public Frame(Frame obj) { + layers = new TreeMap<>(); + backgroundColor = obj.backgroundColor; + timeline = obj.timeline; + for (int depth : obj.layers.keySet()) { + layers.put(depth, new DepthState(obj.layers.get(depth), this, true)); + } + //Do not copy sounds + } +} diff --git a/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/src/com/jpexs/decompiler/flash/timeline/Timeline.java index ed55d3d90..8b4005893 100644 --- a/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -1,377 +1,392 @@ -/* - * Copyright (C) 2010-2014 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.timeline; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.StartSound2Tag; -import com.jpexs.decompiler.flash.tags.StartSoundTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.helpers.SerializableImage; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.Area; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -/** - * - * @author JPEXS - */ -public class Timeline { - - public List frames = new ArrayList<>(); - public int id; - public SWF swf; - public RECT displayRect; - public int frameRate; - - public int getMaxDepth() { - int max_depth = 0; - for (Frame f : frames) { - for (int depth : f.layers.keySet()) { - if (depth > max_depth) { - max_depth = depth; - } - if(f.layers.get(depth).clipDepth > max_depth){ - max_depth = f.layers.get(depth).clipDepth; - } - } - } - return max_depth; - } - - public int getFrameCount() { - return frames.size(); - } - - public Timeline(SWF swf) { - this(swf, swf.tags, 0, swf.displayRect); - } - - public Timeline(SWF swf, List tags, int id, RECT displayRect) { - this.id = id; - this.swf = swf; - this.displayRect = displayRect; - this.frameRate = swf.frameRate; - Frame frame = new Frame(this); - for (Tag t : tags) { - if (t instanceof StartSoundTag) { - frame.sounds.add(((StartSoundTag) t).soundId); - } - if (t instanceof StartSound2Tag) { - frame.soundClasses.add(((StartSound2Tag) t).soundClassName); - } - if (t instanceof SetBackgroundColorTag) { - frame.backgroundColor = ((SetBackgroundColorTag) t).backgroundColor; - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - int depth = po.getDepth(); - if (!frame.layers.containsKey(depth)) { - frame.layers.put(depth, new DepthState(swf, frame)); - } - DepthState fl = frame.layers.get(depth); - int characterId = po.getCharacterId(); - if (characterId != -1) { - fl.characterId = characterId; - } - if (po.flagMove()) { - MATRIX matrix2 = po.getMatrix(); - if (matrix2 != null) { - fl.matrix = matrix2; - } - String instanceName2 = po.getInstanceName(); - if (instanceName2 != null) { - fl.instanceName = instanceName2; - } - ColorTransform colorTransForm2 = po.getColorTransform(); - if (colorTransForm2 != null) { - fl.colorTransForm = colorTransForm2; - } - - CLIPACTIONS clipActions2 = po.getClipActions(); - if (clipActions2 != null) { - fl.clipActions = clipActions2; - } - if (po.cacheAsBitmap()) { - fl.cacheAsBitmap = true; - } - int blendMode2 = po.getBlendMode(); - if (blendMode2 > 0) { - fl.blendMode = blendMode2; - } - List filters2 = po.getFilters(); - if (filters2 != null) { - fl.filters = filters2; - } - int ratio2 = po.getRatio(); - if (ratio2 > -1) { - fl.ratio = ratio2; - } - int clipDepth2 = po.getClipDepth(); - if (clipDepth2 > -1) { - fl.clipDepth = clipDepth2; - } - } else { - fl.matrix = po.getMatrix(); - fl.instanceName = po.getInstanceName(); - fl.colorTransForm = po.getColorTransform(); - fl.cacheAsBitmap = po.cacheAsBitmap(); - fl.blendMode = po.getBlendMode(); - fl.filters = po.getFilters(); - fl.ratio = po.getRatio(); - fl.clipActions = po.getClipActions(); - fl.clipDepth = po.getClipDepth(); - } - fl.key = true; - } - if (t instanceof RemoveTag) { - RemoveTag r = (RemoveTag) t; - int depth = r.getDepth(); - frame.layers.remove(depth); - } - if (t instanceof DoActionTag) { - frame.action = (DoActionTag) t; - } - if (t instanceof ShowFrameTag) { - frames.add(frame); - frame = new Frame(frame); - } - } - } - - public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { - SWF.frameToImage(this, frame, time, stateUnderCursor, mouseButton, image, transformation, colorTransform); - } - - public String toHtmlCanvas(double unitDivisor, List frames) { - return SWF.framesToHtmlCanvas(unitDivisor, this, frames, 0, null, 0, displayRect, new ColorTransform(), null); - } - - public void getSounds(int frame, int time, DepthState stateUnderCursor, int mouseButton, List sounds, List soundClasses) { - Frame fr = this.frames.get(frame); - sounds.addAll(fr.sounds); - soundClasses.addAll(fr.soundClasses); - int maxDepth = this.getMaxDepth(); - for (int d = maxDepth; d >= 0; d--) { - DepthState ds = fr.layers.get(d); - if (ds != null) { - CharacterTag c = swf.characters.get(ds.characterId); - if (c instanceof Timelined) { - int frameCount = ((Timelined) c).getTimeline().frames.size(); - if (frameCount == 0) { - continue; - } - int dframe = (time + ds.time) % frameCount; - if (c instanceof ButtonTag) { - ButtonTag bt = (ButtonTag) c; - dframe = ButtonTag.FRAME_UP; - if (stateUnderCursor == ds) { - if (mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - } - } - ((Timelined) c).getTimeline().getSounds(dframe, time + ds.time, stateUnderCursor, mouseButton, sounds, soundClasses); - } - } - } - } - - public void getObjectsOutlines(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation, List objs, List outlines) { - Frame fr = this.frames.get(frame); - Stack clips = new Stack<>(); - int maxDepth = this.getMaxDepth(); - for (int d = maxDepth; d >= 0; d--) { - Clip currentClip = null; - for (int i = clips.size() - 1; i >= 0; i--) { - Clip cl = clips.get(i); - if (cl.depth <= d) { - clips.remove(i); - } - } - if (!clips.isEmpty()) { - currentClip = clips.peek(); - } - DepthState ds = fr.layers.get(d); - if (ds == null) { - continue; - } - if (!ds.isVisible) { - continue; - } - CharacterTag c = swf.characters.get(ds.characterId); - if (c instanceof DrawableTag) { - Matrix m = new Matrix(ds.matrix); - m = m.preConcatenate(transformation); - - int dframe = 0; - if (c instanceof Timelined) { - int frameCount = ((Timelined) c).getTimeline().frames.size(); - if (frameCount == 0) { - return; - } - dframe = ds.time % frameCount; - if (c instanceof ButtonTag) { - ButtonTag bt = (ButtonTag) c; - dframe = ButtonTag.FRAME_HITTEST; - /*dframe = ButtonTag.FRAME_UP; - if (stateUnderCursor == ds) { - if (mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - }*/ - } - } - Shape cshape = ((DrawableTag) c).getOutline(dframe, ds.time + time, ds.ratio, stateUnderCursor, mouseButton, m); - - Area addArea = new Area(cshape); - if (currentClip != null) { - Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); - a.subtract(new Area(currentClip.shape)); - addArea.subtract(a); - } - if (ds.clipDepth > -1) { - Clip clip = new Clip(addArea, ds.clipDepth); - clips.push(clip); - } else { - objs.add(ds); - outlines.add(addArea); - } - if (c instanceof Timelined) { - ((Timelined) c).getTimeline().getObjectsOutlines(dframe, time + ds.time, ds.ratio, stateUnderCursor, mouseButton, m, objs, outlines); - } - } - } - } - - public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { - Frame fr = this.frames.get(frame); - Area area = new Area(); - Stack clips = new Stack<>(); - int maxDepth = this.getMaxDepth(); - for (int d = maxDepth; d >= 0; d--) { - Clip currentClip = null; - for (int i = clips.size() - 1; i >= 0; i--) { - Clip cl = clips.get(i); - if (cl.depth <= d) { - clips.remove(i); - } - } - if (!clips.isEmpty()) { - currentClip = clips.peek(); - } - DepthState ds = fr.layers.get(d); - if (ds == null) { - continue; - } - if (!ds.isVisible) { - continue; - } - CharacterTag c = swf.characters.get(ds.characterId); - if (c instanceof DrawableTag) { - Matrix m = new Matrix(ds.matrix); - m = m.preConcatenate(transformation); - - int dframe = 0; - if (c instanceof Timelined) { - dframe = (time + ds.time) % ((Timelined) c).getTimeline().frames.size(); - if (c instanceof ButtonTag) { - ButtonTag bt = (ButtonTag) c; - dframe = ButtonTag.FRAME_UP; - if (stateUnderCursor == ds) { - if (mouseButton > 0) { - dframe = ButtonTag.FRAME_DOWN; - } else { - dframe = ButtonTag.FRAME_OVER; - } - } - } - } - Shape cshape = ((DrawableTag) c).getOutline(dframe, time + ds.time, ds.ratio, stateUnderCursor, mouseButton, m); - - Area addArea = new Area(cshape); - if (currentClip != null) { - Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); - a.subtract(new Area(currentClip.shape)); - addArea.subtract(a); - } - if (ds.clipDepth > -1) { - Clip clip = new Clip(addArea, ds.clipDepth); - clips.push(clip); - } else { - area.add(addArea); - } - } - } - return area; - } - - public boolean isSingleFrame() { - for (int i = 0; i < frames.size(); i++) { - if (!isSingleFrame(i)) { - return false; - } - } - return true; - } - - private boolean isSingleFrame(int frame) { - Frame frameObj = frames.get(frame); - int maxDepth = this.getMaxDepth(); - for (int i = 1; i <= maxDepth; i++) { - if (!frameObj.layers.containsKey(i)) { - continue; - } - DepthState layer = frameObj.layers.get(i); - if (!swf.characters.containsKey(layer.characterId)) { - continue; - } - if (!layer.isVisible) { - continue; - } - CharacterTag character = swf.characters.get(layer.characterId); - if (character instanceof DrawableTag) { - DrawableTag drawable = (DrawableTag) character; - if (!drawable.isSingleFrame()) { - return false; - } - } - } - - return true; - } -} +/* + * Copyright (C) 2010-2014 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.timeline; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.StartSound2Tag; +import com.jpexs.decompiler.flash.tags.StartSoundTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.helpers.SerializableImage; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * + * @author JPEXS + */ +public class Timeline { + + public List frames = new ArrayList<>(); + public int id; + public SWF swf; + public RECT displayRect; + public int frameRate; + + public int getMaxDepth() { + int max_depth = 0; + for (Frame f : frames) { + for (int depth : f.layers.keySet()) { + if (depth > max_depth) { + max_depth = depth; + } + if (f.layers.get(depth).clipDepth > max_depth) { + max_depth = f.layers.get(depth).clipDepth; + } + } + } + return max_depth; + } + + public int getFrameCount() { + return frames.size(); + } + + public Timeline(SWF swf) { + this(swf, swf.tags, 0, swf.displayRect); + } + + public Timeline(SWF swf, List tags, int id, RECT displayRect) { + this.id = id; + this.swf = swf; + this.displayRect = displayRect; + this.frameRate = swf.frameRate; + Frame frame = new Frame(this); + boolean tagAdded = false; + for (Tag t : tags) { + if (ShowFrameTag.isNestedTagType(t.getId())) { + frame.innerTags.add(t); + tagAdded = true; + } + if (t instanceof StartSoundTag) { + frame.sounds.add(((StartSoundTag) t).soundId); + tagAdded = true; + } + if (t instanceof StartSound2Tag) { + frame.soundClasses.add(((StartSound2Tag) t).soundClassName); + tagAdded = true; + } + if (t instanceof SetBackgroundColorTag) { + frame.backgroundColor = ((SetBackgroundColorTag) t).backgroundColor; + tagAdded = true; + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int depth = po.getDepth(); + if (!frame.layers.containsKey(depth)) { + frame.layers.put(depth, new DepthState(swf, frame)); + } + DepthState fl = frame.layers.get(depth); + int characterId = po.getCharacterId(); + if (characterId != -1) { + fl.characterId = characterId; + } + if (po.flagMove()) { + MATRIX matrix2 = po.getMatrix(); + if (matrix2 != null) { + fl.matrix = matrix2; + } + String instanceName2 = po.getInstanceName(); + if (instanceName2 != null) { + fl.instanceName = instanceName2; + } + ColorTransform colorTransForm2 = po.getColorTransform(); + if (colorTransForm2 != null) { + fl.colorTransForm = colorTransForm2; + } + + CLIPACTIONS clipActions2 = po.getClipActions(); + if (clipActions2 != null) { + fl.clipActions = clipActions2; + } + if (po.cacheAsBitmap()) { + fl.cacheAsBitmap = true; + } + int blendMode2 = po.getBlendMode(); + if (blendMode2 > 0) { + fl.blendMode = blendMode2; + } + List filters2 = po.getFilters(); + if (filters2 != null) { + fl.filters = filters2; + } + int ratio2 = po.getRatio(); + if (ratio2 > -1) { + fl.ratio = ratio2; + } + int clipDepth2 = po.getClipDepth(); + if (clipDepth2 > -1) { + fl.clipDepth = clipDepth2; + } + } else { + fl.matrix = po.getMatrix(); + fl.instanceName = po.getInstanceName(); + fl.colorTransForm = po.getColorTransform(); + fl.cacheAsBitmap = po.cacheAsBitmap(); + fl.blendMode = po.getBlendMode(); + fl.filters = po.getFilters(); + fl.ratio = po.getRatio(); + fl.clipActions = po.getClipActions(); + fl.clipDepth = po.getClipDepth(); + } + fl.key = true; + tagAdded = true; + } + if (t instanceof RemoveTag) { + RemoveTag r = (RemoveTag) t; + int depth = r.getDepth(); + frame.layers.remove(depth); + tagAdded = true; + } + if (t instanceof DoActionTag) { + frame.action = (DoActionTag) t; + tagAdded = true; + } + if (t instanceof ShowFrameTag) { + frames.add(frame); + frame = new Frame(frame); + tagAdded = false; + } + } + if (tagAdded) { + frames.add(frame); + } + } + + public void toImage(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, SerializableImage image, Matrix transformation, ColorTransform colorTransform) { + SWF.frameToImage(this, frame, time, stateUnderCursor, mouseButton, image, transformation, colorTransform); + } + + public String toHtmlCanvas(double unitDivisor, List frames) { + return SWF.framesToHtmlCanvas(unitDivisor, this, frames, 0, null, 0, displayRect, new ColorTransform(), null); + } + + public void getSounds(int frame, int time, DepthState stateUnderCursor, int mouseButton, List sounds, List soundClasses) { + Frame fr = this.frames.get(frame); + sounds.addAll(fr.sounds); + soundClasses.addAll(fr.soundClasses); + int maxDepth = this.getMaxDepth(); + for (int d = maxDepth; d >= 0; d--) { + DepthState ds = fr.layers.get(d); + if (ds != null) { + CharacterTag c = swf.characters.get(ds.characterId); + if (c instanceof Timelined) { + int frameCount = ((Timelined) c).getTimeline().frames.size(); + if (frameCount == 0) { + continue; + } + int dframe = (time + ds.time) % frameCount; + if (c instanceof ButtonTag) { + ButtonTag bt = (ButtonTag) c; + dframe = ButtonTag.FRAME_UP; + if (stateUnderCursor == ds) { + if (mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + } + } + ((Timelined) c).getTimeline().getSounds(dframe, time + ds.time, stateUnderCursor, mouseButton, sounds, soundClasses); + } + } + } + } + + public void getObjectsOutlines(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation, List objs, List outlines) { + Frame fr = this.frames.get(frame); + Stack clips = new Stack<>(); + int maxDepth = this.getMaxDepth(); + for (int d = maxDepth; d >= 0; d--) { + Clip currentClip = null; + for (int i = clips.size() - 1; i >= 0; i--) { + Clip cl = clips.get(i); + if (cl.depth <= d) { + clips.remove(i); + } + } + if (!clips.isEmpty()) { + currentClip = clips.peek(); + } + DepthState ds = fr.layers.get(d); + if (ds == null) { + continue; + } + if (!ds.isVisible) { + continue; + } + CharacterTag c = swf.characters.get(ds.characterId); + if (c instanceof DrawableTag) { + Matrix m = new Matrix(ds.matrix); + m = m.preConcatenate(transformation); + + int dframe = 0; + if (c instanceof Timelined) { + int frameCount = ((Timelined) c).getTimeline().frames.size(); + if (frameCount == 0) { + return; + } + dframe = ds.time % frameCount; + if (c instanceof ButtonTag) { + ButtonTag bt = (ButtonTag) c; + dframe = ButtonTag.FRAME_HITTEST; + /*dframe = ButtonTag.FRAME_UP; + if (stateUnderCursor == ds) { + if (mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + }*/ + } + } + Shape cshape = ((DrawableTag) c).getOutline(dframe, ds.time + time, ds.ratio, stateUnderCursor, mouseButton, m); + + Area addArea = new Area(cshape); + if (currentClip != null) { + Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); + a.subtract(new Area(currentClip.shape)); + addArea.subtract(a); + } + if (ds.clipDepth > -1) { + Clip clip = new Clip(addArea, ds.clipDepth); + clips.push(clip); + } else { + objs.add(ds); + outlines.add(addArea); + } + if (c instanceof Timelined) { + ((Timelined) c).getTimeline().getObjectsOutlines(dframe, time + ds.time, ds.ratio, stateUnderCursor, mouseButton, m, objs, outlines); + } + } + } + } + + public Shape getOutline(int frame, int time, int ratio, DepthState stateUnderCursor, int mouseButton, Matrix transformation) { + Frame fr = this.frames.get(frame); + Area area = new Area(); + Stack clips = new Stack<>(); + int maxDepth = this.getMaxDepth(); + for (int d = maxDepth; d >= 0; d--) { + Clip currentClip = null; + for (int i = clips.size() - 1; i >= 0; i--) { + Clip cl = clips.get(i); + if (cl.depth <= d) { + clips.remove(i); + } + } + if (!clips.isEmpty()) { + currentClip = clips.peek(); + } + DepthState ds = fr.layers.get(d); + if (ds == null) { + continue; + } + if (!ds.isVisible) { + continue; + } + CharacterTag c = swf.characters.get(ds.characterId); + if (c instanceof DrawableTag) { + Matrix m = new Matrix(ds.matrix); + m = m.preConcatenate(transformation); + + int dframe = 0; + if (c instanceof Timelined) { + dframe = (time + ds.time) % ((Timelined) c).getTimeline().frames.size(); + if (c instanceof ButtonTag) { + ButtonTag bt = (ButtonTag) c; + dframe = ButtonTag.FRAME_UP; + if (stateUnderCursor == ds) { + if (mouseButton > 0) { + dframe = ButtonTag.FRAME_DOWN; + } else { + dframe = ButtonTag.FRAME_OVER; + } + } + } + } + Shape cshape = ((DrawableTag) c).getOutline(dframe, time + ds.time, ds.ratio, stateUnderCursor, mouseButton, m); + + Area addArea = new Area(cshape); + if (currentClip != null) { + Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight())); + a.subtract(new Area(currentClip.shape)); + addArea.subtract(a); + } + if (ds.clipDepth > -1) { + Clip clip = new Clip(addArea, ds.clipDepth); + clips.push(clip); + } else { + area.add(addArea); + } + } + } + return area; + } + + public boolean isSingleFrame() { + for (int i = 0; i < frames.size(); i++) { + if (!isSingleFrame(i)) { + return false; + } + } + return true; + } + + private boolean isSingleFrame(int frame) { + Frame frameObj = frames.get(frame); + int maxDepth = this.getMaxDepth(); + for (int i = 1; i <= maxDepth; i++) { + if (!frameObj.layers.containsKey(i)) { + continue; + } + DepthState layer = frameObj.layers.get(i); + if (!swf.characters.containsKey(layer.characterId)) { + continue; + } + if (!layer.isVisible) { + continue; + } + CharacterTag character = swf.characters.get(layer.characterId); + if (character instanceof DrawableTag) { + DrawableTag drawable = (DrawableTag) character; + if (!drawable.isSingleFrame()) { + return false; + } + } + } + + return true; + } +} diff --git a/src/com/jpexs/decompiler/flash/treeitems/FrameNodeItem.java b/src/com/jpexs/decompiler/flash/treeitems/FrameNodeItem.java index 063cf5de4..d65cd9cd8 100644 --- a/src/com/jpexs/decompiler/flash/treeitems/FrameNodeItem.java +++ b/src/com/jpexs/decompiler/flash/treeitems/FrameNodeItem.java @@ -1,69 +1,62 @@ -/* - * Copyright (C) 2010-2014 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.treeitems; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.Tag; - -/** - * - * @author JPEXS - */ -public class FrameNodeItem implements TreeItem { - - private final SWF swf; - private final int frame; - private final Tag parent; - private final ShowFrameTag showFrameTag; - private final boolean display; - - public FrameNodeItem(SWF swf, int frame, Tag parent, ShowFrameTag showFrameTag, boolean display) { - - this.swf = swf; - this.frame = frame; - this.parent = parent; - this.showFrameTag = showFrameTag; - this.display = display; - } - - @Override - public SWF getSwf() { - return swf; - } - - public boolean isDisplayed() { - return display; - } - - @Override - public String toString() { - return "frame " + frame; - } - - public int getFrame() { - return frame; - } - - public Tag getParent() { - return parent; - } - - public ShowFrameTag getShowFrameTag() { - return showFrameTag; - } -} +/* + * Copyright (C) 2010-2014 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.treeitems; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.tags.Tag; + +/** + * + * @author JPEXS + */ +public class FrameNodeItem implements TreeItem { + + private final SWF swf; + private final int frame; + private final Tag parent; + private final boolean display; + + public FrameNodeItem(SWF swf, int frame, Tag parent, boolean display) { + + this.swf = swf; + this.frame = frame; + this.parent = parent; + this.display = display; + } + + @Override + public SWF getSwf() { + return swf; + } + + public boolean isDisplayed() { + return display; + } + + @Override + public String toString() { + return "frame " + frame; + } + + public int getFrame() { + return frame; + } + + public Tag getParent() { + return parent; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index c2d9be522..4569fe18f 100644 --- a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -1,321 +1,320 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Actions to execute at particular button events - * - * @author JPEXS - */ -public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem, Serializable { - - private final SWF swf; - private final Tag tag; - private final long pos; - - @Override - public SWF getSwf() { - return swf; - } - - @Override - public long getPos() { - return pos; - } - - public BUTTONCONDACTION(SWF swf, InputStream is, long containerOffset, Tag tag) throws IOException { - this.swf = swf; - this.tag = tag; - SWFInputStream sis = new SWFInputStream(is, swf.version); - pos = containerOffset; - int condActionSize = sis.readUI16(); - isLast = condActionSize <= 0; - condIdleToOverDown = sis.readUB(1) == 1; - condOutDownToIdle = sis.readUB(1) == 1; - condOutDownToOverDown = sis.readUB(1) == 1; - condOverDownToOutDown = sis.readUB(1) == 1; - condOverDownToOverUp = sis.readUB(1) == 1; - condOverUpToOverDown = sis.readUB(1) == 1; - condOverUpToIddle = sis.readUB(1) == 1; - condIdleToOverUp = sis.readUB(1) == 1; - condKeyPress = (int) sis.readUB(7); - condOverDownToIddle = sis.readUB(1) == 1; - if (condActionSize <= 0) { - actionBytes = sis.readBytesEx(sis.available()); - } else { - actionBytes = sis.readBytesEx(condActionSize - 4); - } - } - /** - * Is this BUTTONCONDACTION last in the list? - */ - @Internal - public boolean isLast; - /** - * Idle to OverDown - */ - public boolean condIdleToOverDown; - /** - * OutDown to Idle - */ - public boolean condOutDownToIdle; - /** - * OutDown to OverDown - */ - public boolean condOutDownToOverDown; - /** - * OverDown to OutDown - */ - public boolean condOverDownToOutDown; - /** - * OverDown to OverUp - */ - public boolean condOverDownToOverUp; - /** - * OverUp to OverDown - */ - public boolean condOverUpToOverDown; - /** - * OverUp to Idle - */ - public boolean condOverUpToIddle; - /** - * Idle to OverUp - */ - public boolean condIdleToOverUp; - /** - * @since SWF 4 key code - */ - @SWFType(value = BasicType.UB, count = 7) - @Conditional(minSwfVersion = 4) - public int condKeyPress; - /** - * OverDown to Idle - */ - public boolean condOverDownToIddle; - /** - * Actions to perform - */ - //public List actions; - /** - * Actions to perform in byte array - */ - @Internal - public byte[] actionBytes; - - /** - * Sets actions associated with this object - * - * @param actions Action list - */ - /*public void setActions(List actions) { - this.actions = actions; - }*/ - /** - * Returns a string representation of the object - * - * @return a string representation of the object. - */ - @Override - public String toString() { - return "BUTTONCONDACTION"; - } - - /** - * Converts actions to ASM source - * - * @param actions - * @param writer - * @return ASM source - * @throws java.lang.InterruptedException - */ - @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { - if (actions == null) { - actions = getActions(); - } - return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + 4, toString()/*FIXME?*/); - } - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - @Override - public boolean containsSource() { - return true; - } - - /** - * Returns actions associated with this object - * - * @return List of actions - * @throws java.lang.InterruptedException - */ - @Override - public List getActions() throws InterruptedException { - try { - List list = ActionListReader.readActionListTimeout(listeners, getPos() + 4, new MemoryInputStream(actionBytes), swf.version, 0, -1, toString()/*FIXME?*/); - return list; - - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); - return new ArrayList<>(); - } - } - - @Override - public void setActions(List actions) { - actionBytes = Action.actionsToBytes(actions, true, swf.version); - } - - @Override - public byte[] getActionBytes() { - return actionBytes; - } - - @Override - public void setActionBytes(byte[] actionBytes) { - this.actionBytes = actionBytes; - } - - @Override - public void setModified() { - if (tag != null) { - tag.setModified(true); - } - } - - @Override - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { - return Helper.byteArrayToHexWithHeader(writer, actionBytes); - } - - List listeners = new ArrayList<>(); - - @Override - public void addDisassemblyListener(DisassemblyListener listener) { - listeners.add(listener); - } - - @Override - public void removeDisassemblyListener(DisassemblyListener listener) { - listeners.remove(listener); - } - - private String getHeader(boolean asFilename) { - List events = new ArrayList<>(); - if (condOverUpToOverDown) { - events.add("press"); - } - if (condOverDownToOverUp) { - events.add("release"); - } - if (condOutDownToIdle) { - events.add("releaseOutside"); - } - if (condIdleToOverUp) { - events.add("rollOver"); - } - if (condOverUpToIddle) { - events.add("rollOut"); - } - if (condOverDownToOutDown) { - events.add("dragOut"); - } - if (condOutDownToOverDown) { - events.add("dragOver"); - } - if (condKeyPress > 0) { - if (asFilename) { - events.add("keyPress " + Helper.makeFileName(CLIPACTIONRECORD.keyToString(condKeyPress).replace("<", "").replace(">", "")) + ""); - } else { - events.add("keyPress \"" + CLIPACTIONRECORD.keyToString(condKeyPress) + "\""); - } - } - String onStr = ""; - for (int i = 0; i < events.size(); i++) { - if (i > 0) { - onStr += ", "; - } - onStr += events.get(i); - } - return "on(" + onStr + ")"; - } - - @Override - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { - writer.appendNoHilight(getHeader(false)); - writer.appendNoHilight("{").newLine(); - return writer.indent(); - } - - @Override - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { - writer.unindent(); - return writer.appendNoHilight("}").newLine(); - } - - @Override - public int getPrefixLineCount() { - return 1; - } - - @Override - public String removePrefixAndSuffix(String source) { - return Helper.unindentRows(1, 1, source); - } - - @Override - public String getExportFileName() { - return getHeader(true); - } - - @Override - public Tag getSourceTag() { - return tag; - } - - -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Actions to execute at particular button events + * + * @author JPEXS + */ +public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem, Serializable { + + private final SWF swf; + private final Tag tag; + private final long pos; + + @Override + public SWF getSwf() { + return swf; + } + + @Override + public long getPos() { + return pos; + } + + public BUTTONCONDACTION(SWF swf, InputStream is, long containerOffset, Tag tag) throws IOException { + this.swf = swf; + this.tag = tag; + SWFInputStream sis = new SWFInputStream(is, swf.version); + pos = containerOffset; + int condActionSize = sis.readUI16(); + isLast = condActionSize <= 0; + condIdleToOverDown = sis.readUB(1) == 1; + condOutDownToIdle = sis.readUB(1) == 1; + condOutDownToOverDown = sis.readUB(1) == 1; + condOverDownToOutDown = sis.readUB(1) == 1; + condOverDownToOverUp = sis.readUB(1) == 1; + condOverUpToOverDown = sis.readUB(1) == 1; + condOverUpToIddle = sis.readUB(1) == 1; + condIdleToOverUp = sis.readUB(1) == 1; + condKeyPress = (int) sis.readUB(7); + condOverDownToIddle = sis.readUB(1) == 1; + if (condActionSize <= 0) { + actionBytes = sis.readBytesEx(sis.available()); + } else { + actionBytes = sis.readBytesEx(condActionSize - 4); + } + } + /** + * Is this BUTTONCONDACTION last in the list? + */ + @Internal + public boolean isLast; + /** + * Idle to OverDown + */ + public boolean condIdleToOverDown; + /** + * OutDown to Idle + */ + public boolean condOutDownToIdle; + /** + * OutDown to OverDown + */ + public boolean condOutDownToOverDown; + /** + * OverDown to OutDown + */ + public boolean condOverDownToOutDown; + /** + * OverDown to OverUp + */ + public boolean condOverDownToOverUp; + /** + * OverUp to OverDown + */ + public boolean condOverUpToOverDown; + /** + * OverUp to Idle + */ + public boolean condOverUpToIddle; + /** + * Idle to OverUp + */ + public boolean condIdleToOverUp; + /** + * @since SWF 4 key code + */ + @SWFType(value = BasicType.UB, count = 7) + @Conditional(minSwfVersion = 4) + public int condKeyPress; + /** + * OverDown to Idle + */ + public boolean condOverDownToIddle; + /** + * Actions to perform + */ + //public List actions; + /** + * Actions to perform in byte array + */ + @Internal + public byte[] actionBytes; + + /** + * Sets actions associated with this object + * + * @param actions Action list + */ + /*public void setActions(List actions) { + this.actions = actions; + }*/ + /** + * Returns a string representation of the object + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return "BUTTONCONDACTION"; + } + + /** + * Converts actions to ASM source + * + * @param actions + * @param writer + * @return ASM source + * @throws java.lang.InterruptedException + */ + @Override + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + if (actions == null) { + actions = getActions(); + } + return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + 4, toString()/*FIXME?*/); + } + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + @Override + public boolean containsSource() { + return true; + } + + /** + * Returns actions associated with this object + * + * @return List of actions + * @throws java.lang.InterruptedException + */ + @Override + public List getActions() throws InterruptedException { + try { + List list = ActionListReader.readActionListTimeout(listeners, getPos() + 4, new MemoryInputStream(actionBytes), swf.version, 0, -1, toString()/*FIXME?*/); + return list; + + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); + return new ArrayList<>(); + } + } + + @Override + public void setActions(List actions) { + actionBytes = Action.actionsToBytes(actions, true, swf.version); + } + + @Override + public byte[] getActionBytes() { + return actionBytes; + } + + @Override + public void setActionBytes(byte[] actionBytes) { + this.actionBytes = actionBytes; + } + + @Override + public void setModified() { + if (tag != null) { + tag.setModified(true); + } + } + + @Override + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { + return Helper.byteArrayToHexWithHeader(writer, actionBytes); + } + + List listeners = new ArrayList<>(); + + @Override + public void addDisassemblyListener(DisassemblyListener listener) { + listeners.add(listener); + } + + @Override + public void removeDisassemblyListener(DisassemblyListener listener) { + listeners.remove(listener); + } + + private String getHeader(boolean asFilename) { + List events = new ArrayList<>(); + if (condOverUpToOverDown) { + events.add("press"); + } + if (condOverDownToOverUp) { + events.add("release"); + } + if (condOutDownToIdle) { + events.add("releaseOutside"); + } + if (condIdleToOverUp) { + events.add("rollOver"); + } + if (condOverUpToIddle) { + events.add("rollOut"); + } + if (condOverDownToOutDown) { + events.add("dragOut"); + } + if (condOutDownToOverDown) { + events.add("dragOver"); + } + if (condKeyPress > 0) { + if (asFilename) { + events.add("keyPress " + Helper.makeFileName(CLIPACTIONRECORD.keyToString(condKeyPress).replace("<", "").replace(">", "")) + ""); + } else { + events.add("keyPress \"" + CLIPACTIONRECORD.keyToString(condKeyPress) + "\""); + } + } + String onStr = ""; + for (int i = 0; i < events.size(); i++) { + if (i > 0) { + onStr += ", "; + } + onStr += events.get(i); + } + return "on(" + onStr + ")"; + } + + @Override + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { + writer.appendNoHilight(getHeader(false)); + writer.appendNoHilight("{").newLine(); + return writer.indent(); + } + + @Override + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { + writer.unindent(); + return writer.appendNoHilight("}").newLine(); + } + + @Override + public int getPrefixLineCount() { + return 1; + } + + @Override + public String removePrefixAndSuffix(String source) { + return Helper.unindentRows(1, 1, source); + } + + @Override + public String getExportFileName() { + return getHeader(true); + } + + @Override + public Tag getSourceTag() { + return tag; + } + +} diff --git a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 56e464fe2..39d96070b 100644 --- a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -1,290 +1,289 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.DisassemblyListener; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ContainerItem; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Event handler - * - * @author JPEXS - */ -public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem, Serializable { - - public static String keyToString(int key) { - if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) { - return CLIPACTIONRECORD.KEYNAMES[key]; - } else { - return "" + (char) key; - } - } - public static final String KEYNAMES[] = { - null, - "", - "", - "", - "", - "", - "", - null, - "", - null, - null, - null, - null, - "", - "", - "", - "", - "", - "", - "", - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "" - }; - - @Internal - private final SWF swf; - @Internal - private final Tag tag; - @Internal - private long pos; - @Internal - private long hdrPos; - - //Constructor for Generic tag editor. TODO:Handle this somehow better - public CLIPACTIONRECORD() { - swf = null; - tag = null; - eventFlags = new CLIPEVENTFLAGS(); - actionBytes = new byte[0]; - hdrPos = 0; - } - - public CLIPACTIONRECORD(SWF swf, InputStream is, long pos, Tag tag) throws IOException { - this.swf = swf; - this.tag = tag; - SWFInputStream sis = new SWFInputStream(is, swf.version); - eventFlags = sis.readCLIPEVENTFLAGS(); - if (eventFlags.isClear()) { - return; - } - long actionRecordSize = sis.readUI32(); - if (eventFlags.clipEventKeyPress) { - keyCode = sis.readUI8(); - actionRecordSize--; - } - hdrPos = sis.getPos(); - actionBytes = sis.readBytesEx(actionRecordSize); - this.pos = pos; - - } - - @Override - public SWF getSwf() { - return swf; - } - - @Override - public long getPos() { - return pos; - } - /** - * Events to which this handler applies - */ - public CLIPEVENTFLAGS eventFlags; - /** - * If EventFlags contain ClipEventKeyPress: Key code to trap - */ - @Conditional("eventFlags.clipEventKeyPress") - public int keyCode; - /** - * Actions to perform - */ - //public List actions; - @Internal - public byte[] actionBytes; - - /** - * Returns a string representation of the object - * - * @return a string representation of the object. - */ - @Override - public String toString() { - return eventFlags.getHeader(keyCode, false); - } - - /** - * Returns header with events converted to string - * - * @return String representation of events - */ - public String getHeader() { - String ret; - ret = eventFlags.toString(); - if (eventFlags.clipEventKeyPress) { - ret = ret.replace("keyPress", "keyPress<" + keyCode + ">"); - } - return ret; - } - - /** - * Converts actions to ASM source - * - * @param actions - * @param writer - * @return ASM source - * @throws java.lang.InterruptedException - */ - @Override - public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { - if (actions == null) { - actions = getActions(); - } - return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + hdrPos, toString()/*FIXME?*/); - } - - /** - * Whether or not this object contains ASM source - * - * @return True when contains - */ - @Override - public boolean containsSource() { - return true; - } - - @Override - public List getActions() throws InterruptedException { - try { - List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), swf.version, 0, -1, toString()/*FIXME?*/); - return list; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); - return new ArrayList<>(); - } - } - - @Override - public void setActions(List actions) { - actionBytes = Action.actionsToBytes(actions, true, swf.version); - } - - @Override - public byte[] getActionBytes() { - return actionBytes; - } - - @Override - public void setActionBytes(byte[] actionBytes) { - this.actionBytes = actionBytes; - } - - @Override - public void setModified() { - if (tag != null) { - tag.setModified(true); - } - } - - @Override - public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { - return Helper.byteArrayToHexWithHeader(writer, actionBytes); - } - - List listeners = new ArrayList<>(); - - @Override - public void addDisassemblyListener(DisassemblyListener listener) { - listeners.add(listener); - } - - @Override - public void removeDisassemblyListener(DisassemblyListener listener) { - listeners.remove(listener); - } - - @Override - public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { - writer.appendNoHilight(eventFlags.getHeader(keyCode, false)); - writer.appendNoHilight("{").newLine(); - return writer.indent(); - } - - @Override - public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { - writer.unindent(); - return writer.appendNoHilight("}").newLine(); - } - - @Override - public int getPrefixLineCount() { - return 1; - } - - @Override - public String removePrefixAndSuffix(String source) { - return Helper.unindentRows(1, 1, source); - } - - @Override - public String getExportFileName() { - return eventFlags.getHeader(keyCode, true); - } - - @Override - public Tag getSourceTag() { - return tag; - } - - -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.DisassemblyListener; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ContainerItem; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Event handler + * + * @author JPEXS + */ +public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem, Serializable { + + public static String keyToString(int key) { + if ((key < CLIPACTIONRECORD.KEYNAMES.length) && (key > 0) && (CLIPACTIONRECORD.KEYNAMES[key] != null)) { + return CLIPACTIONRECORD.KEYNAMES[key]; + } else { + return "" + (char) key; + } + } + public static final String KEYNAMES[] = { + null, + "", + "", + "", + "", + "", + "", + null, + "", + null, + null, + null, + null, + "", + "", + "", + "", + "", + "", + "", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "" + }; + + @Internal + private final SWF swf; + @Internal + private final Tag tag; + @Internal + private long pos; + @Internal + private long hdrPos; + + //Constructor for Generic tag editor. TODO:Handle this somehow better + public CLIPACTIONRECORD() { + swf = null; + tag = null; + eventFlags = new CLIPEVENTFLAGS(); + actionBytes = new byte[0]; + hdrPos = 0; + } + + public CLIPACTIONRECORD(SWF swf, InputStream is, long pos, Tag tag) throws IOException { + this.swf = swf; + this.tag = tag; + SWFInputStream sis = new SWFInputStream(is, swf.version); + eventFlags = sis.readCLIPEVENTFLAGS(); + if (eventFlags.isClear()) { + return; + } + long actionRecordSize = sis.readUI32(); + if (eventFlags.clipEventKeyPress) { + keyCode = sis.readUI8(); + actionRecordSize--; + } + hdrPos = sis.getPos(); + actionBytes = sis.readBytesEx(actionRecordSize); + this.pos = pos; + + } + + @Override + public SWF getSwf() { + return swf; + } + + @Override + public long getPos() { + return pos; + } + /** + * Events to which this handler applies + */ + public CLIPEVENTFLAGS eventFlags; + /** + * If EventFlags contain ClipEventKeyPress: Key code to trap + */ + @Conditional("eventFlags.clipEventKeyPress") + public int keyCode; + /** + * Actions to perform + */ + //public List actions; + @Internal + public byte[] actionBytes; + + /** + * Returns a string representation of the object + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return eventFlags.getHeader(keyCode, false); + } + + /** + * Returns header with events converted to string + * + * @return String representation of events + */ + public String getHeader() { + String ret; + ret = eventFlags.toString(); + if (eventFlags.clipEventKeyPress) { + ret = ret.replace("keyPress", "keyPress<" + keyCode + ">"); + } + return ret; + } + + /** + * Converts actions to ASM source + * + * @param actions + * @param writer + * @return ASM source + * @throws java.lang.InterruptedException + */ + @Override + public GraphTextWriter getASMSource(ScriptExportMode exportMode, GraphTextWriter writer, List actions) throws InterruptedException { + if (actions == null) { + actions = getActions(); + } + return Action.actionsToString(listeners, 0, actions, null, swf.version, exportMode, writer, getPos() + hdrPos, toString()/*FIXME?*/); + } + + /** + * Whether or not this object contains ASM source + * + * @return True when contains + */ + @Override + public boolean containsSource() { + return true; + } + + @Override + public List getActions() throws InterruptedException { + try { + List list = ActionListReader.readActionListTimeout(listeners, getPos() + hdrPos, new MemoryInputStream(actionBytes), swf.version, 0, -1, toString()/*FIXME?*/); + return list; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + Logger.getLogger(BUTTONCONDACTION.class.getName()).log(Level.SEVERE, null, ex); + return new ArrayList<>(); + } + } + + @Override + public void setActions(List actions) { + actionBytes = Action.actionsToBytes(actions, true, swf.version); + } + + @Override + public byte[] getActionBytes() { + return actionBytes; + } + + @Override + public void setActionBytes(byte[] actionBytes) { + this.actionBytes = actionBytes; + } + + @Override + public void setModified() { + if (tag != null) { + tag.setModified(true); + } + } + + @Override + public GraphTextWriter getActionBytesAsHex(GraphTextWriter writer) { + return Helper.byteArrayToHexWithHeader(writer, actionBytes); + } + + List listeners = new ArrayList<>(); + + @Override + public void addDisassemblyListener(DisassemblyListener listener) { + listeners.add(listener); + } + + @Override + public void removeDisassemblyListener(DisassemblyListener listener) { + listeners.remove(listener); + } + + @Override + public GraphTextWriter getActionSourcePrefix(GraphTextWriter writer) { + writer.appendNoHilight(eventFlags.getHeader(keyCode, false)); + writer.appendNoHilight("{").newLine(); + return writer.indent(); + } + + @Override + public GraphTextWriter getActionSourceSuffix(GraphTextWriter writer) { + writer.unindent(); + return writer.appendNoHilight("}").newLine(); + } + + @Override + public int getPrefixLineCount() { + return 1; + } + + @Override + public String removePrefixAndSuffix(String source) { + return Helper.unindentRows(1, 1, source); + } + + @Override + public String getExportFileName() { + return eventFlags.getHeader(keyCode, true); + } + + @Override + public Tag getSourceTag() { + return tag; + } + +} diff --git a/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java b/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java index c9f40fa93..631d57f1e 100644 --- a/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java +++ b/src/com/jpexs/decompiler/flash/types/FILLSTYLE.java @@ -1,74 +1,86 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.ConditionalType; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class FILLSTYLE implements NeedsCharacters, Serializable { - - @SWFType(BasicType.UI8) - public int fillStyleType; - public static final int SOLID = 0x0; - public static final int LINEAR_GRADIENT = 0x10; - public static final int RADIAL_GRADIENT = 0x12; - public static final int FOCAL_RADIAL_GRADIENT = 0x13; - public static final int REPEATING_BITMAP = 0x40; - public static final int CLIPPED_BITMAP = 0x41; - public static final int NON_SMOOTHED_REPEATING_BITMAP = 0x42; - public static final int NON_SMOOTHED_CLIPPED_BITMAP = 0x43; - @Internal - public boolean inShape3; - @ConditionalType(type = RGBA.class, tags = DefineShape3Tag.ID) - public RGB color; - - @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT, FOCAL_RADIAL_GRADIENT}) - public MATRIX gradientMatrix; - - @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT, FOCAL_RADIAL_GRADIENT}) - @ConditionalType(value = "fillStyleType", type = FOCALGRADIENT.class, options = {FOCAL_RADIAL_GRADIENT}) - public GRADIENT gradient; - - @Conditional(value = "fillStyleType", options = {REPEATING_BITMAP, CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) - public int bitmapId; - - @Conditional(value = "fillStyleType", options = {REPEATING_BITMAP, CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) - public MATRIX bitmapMatrix; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - if ((fillStyleType == REPEATING_BITMAP) - || (fillStyleType == CLIPPED_BITMAP) - || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) - || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { - ret.add(bitmapId); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.ConditionalType; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class FILLSTYLE implements NeedsCharacters, Serializable { + + @SWFType(BasicType.UI8) + public int fillStyleType; + public static final int SOLID = 0x0; + public static final int LINEAR_GRADIENT = 0x10; + public static final int RADIAL_GRADIENT = 0x12; + public static final int FOCAL_RADIAL_GRADIENT = 0x13; + public static final int REPEATING_BITMAP = 0x40; + public static final int CLIPPED_BITMAP = 0x41; + public static final int NON_SMOOTHED_REPEATING_BITMAP = 0x42; + public static final int NON_SMOOTHED_CLIPPED_BITMAP = 0x43; + @Internal + public boolean inShape3; + @ConditionalType(type = RGBA.class, tags = DefineShape3Tag.ID) + public RGB color; + + @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT, FOCAL_RADIAL_GRADIENT}) + public MATRIX gradientMatrix; + + @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT, FOCAL_RADIAL_GRADIENT}) + @ConditionalType(value = "fillStyleType", type = FOCALGRADIENT.class, options = {FOCAL_RADIAL_GRADIENT}) + public GRADIENT gradient; + + @Conditional(value = "fillStyleType", options = {REPEATING_BITMAP, CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) + public int bitmapId; + + @Conditional(value = "fillStyleType", options = {REPEATING_BITMAP, CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) + public MATRIX bitmapMatrix; + + @Override + public void getNeededCharacters(Set needed) { + if ((fillStyleType == REPEATING_BITMAP) + || (fillStyleType == CLIPPED_BITMAP) + || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) + || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { + needed.add(bitmapId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + if (bitmapId == characterId) { + if ((fillStyleType == REPEATING_BITMAP) + || (fillStyleType == CLIPPED_BITMAP) + || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) + || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { + fillStyleType = SOLID; + } + bitmapId = 0; + return true; + } + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java b/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java index c8733bdd8..d197068b7 100644 --- a/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java +++ b/src/com/jpexs/decompiler/flash/types/FILLSTYLEARRAY.java @@ -1,40 +1,47 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class FILLSTYLEARRAY implements NeedsCharacters, Serializable { - - public FILLSTYLE[] fillStyles; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - for (FILLSTYLE fs : fillStyles) { - ret.addAll(fs.getNeededCharacters()); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class FILLSTYLEARRAY implements NeedsCharacters, Serializable { + + public FILLSTYLE[] fillStyles; + + @Override + public void getNeededCharacters(Set needed) { + for (FILLSTYLE fs : fillStyles) { + fs.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (FILLSTYLE fs : fillStyles) { + modified |= fs.removeCharacter(characterId); + } + return modified; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/LINESTYLE.java b/src/com/jpexs/decompiler/flash/types/LINESTYLE.java index c9d6a553b..0228c6c6b 100644 --- a/src/com/jpexs/decompiler/flash/types/LINESTYLE.java +++ b/src/com/jpexs/decompiler/flash/types/LINESTYLE.java @@ -1,42 +1,47 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.types.annotations.ConditionalType; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class LINESTYLE implements Serializable { - - @SWFType(BasicType.UI16) - public int width; - - @ConditionalType(tags = {DefineShape3Tag.ID, DefineShape4Tag.ID}, type = RGBA.class) - public RGB color; - - public Set getNeededCharacters() { - return new HashSet<>(); - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.types.annotations.ConditionalType; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class LINESTYLE implements NeedsCharacters, Serializable { + + @SWFType(BasicType.UI16) + public int width; + + @ConditionalType(tags = {DefineShape3Tag.ID, DefineShape4Tag.ID}, type = RGBA.class) + public RGB color; + + @Override + public void getNeededCharacters(Set needed) { + } + + @Override + public boolean removeCharacter(int characterId) { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java b/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java index 659f29d15..50fd4e160 100644 --- a/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java +++ b/src/com/jpexs/decompiler/flash/types/LINESTYLE2.java @@ -1,67 +1,68 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.Reserved; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class LINESTYLE2 extends LINESTYLE implements Serializable { - - @SWFType(value = BasicType.UB, count = 2) - public int startCapStyle; - @SWFType(value = BasicType.UB, count = 2) - public int joinStyle; - public static final int ROUND_JOIN = 0; - public static final int BEVEL_JOIN = 1; - public static final int MITER_JOIN = 2; - public boolean hasFillFlag; - public boolean noHScaleFlag; - public boolean noVScaleFlag; - public boolean pixelHintingFlag; - @Reserved - @SWFType(value = BasicType.UB, count = 5) - public int reserved; - public boolean noClose; - @SWFType(value = BasicType.UB, count = 2) - public int endCapStyle; - public static final int ROUND_CAP = 0; - public static final int NO_CAP = 1; - public static final int SQUARE_CAP = 2; - - @SWFType(BasicType.UI16) - @Conditional(value = "joinStyle", options = MITER_JOIN) - public int miterLimitFactor; - public FILLSTYLE fillType; - - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - if(hasFillFlag){ - ret.addAll(fillType.getNeededCharacters()); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.Reserved; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class LINESTYLE2 extends LINESTYLE implements Serializable { + + @SWFType(value = BasicType.UB, count = 2) + public int startCapStyle; + @SWFType(value = BasicType.UB, count = 2) + public int joinStyle; + public static final int ROUND_JOIN = 0; + public static final int BEVEL_JOIN = 1; + public static final int MITER_JOIN = 2; + public boolean hasFillFlag; + public boolean noHScaleFlag; + public boolean noVScaleFlag; + public boolean pixelHintingFlag; + @Reserved + @SWFType(value = BasicType.UB, count = 5) + public int reserved; + public boolean noClose; + @SWFType(value = BasicType.UB, count = 2) + public int endCapStyle; + public static final int ROUND_CAP = 0; + public static final int NO_CAP = 1; + public static final int SQUARE_CAP = 2; + + @SWFType(BasicType.UI16) + @Conditional(value = "joinStyle", options = MITER_JOIN) + public int miterLimitFactor; + public FILLSTYLE fillType; + + @Override + public void getNeededCharacters(Set needed) { + if (hasFillFlag) { + fillType.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + return fillType.removeCharacter(characterId); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java b/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java index 6d6fb219e..94c491207 100644 --- a/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java +++ b/src/com/jpexs/decompiler/flash/types/LINESTYLEARRAY.java @@ -1,38 +1,46 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class LINESTYLEARRAY implements Serializable { - - public LINESTYLE[] lineStyles = new LINESTYLE[0]; - - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - for (LINESTYLE ls : lineStyles) { - ret.addAll(ls.getNeededCharacters()); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class LINESTYLEARRAY implements NeedsCharacters, Serializable { + + public LINESTYLE[] lineStyles = new LINESTYLE[0]; + + @Override + public void getNeededCharacters(Set needed) { + for (LINESTYLE ls : lineStyles) { + ls.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (LINESTYLE ls : lineStyles) { + modified |= ls.removeCharacter(characterId); + } + return modified; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java b/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java index 46e616ba3..da33071a2 100644 --- a/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java +++ b/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLE.java @@ -1,144 +1,156 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class MORPHFILLSTYLE implements NeedsCharacters, Serializable { - - @SWFType(BasicType.UI8) - public int fillStyleType; - - public static final int SOLID = 0x0; - public static final int LINEAR_GRADIENT = 0x10; - public static final int RADIAL_GRADIENT = 0x12; - public static final int FOCAL_RADIAL_GRADIENT = 0x13; - public static final int REPEATING_BITMAP = 0x40; - public static final int CLIPPED_BITMAP = 0x41; - public static final int NON_SMOOTHED_REPEATING_BITMAP = 0x42; - public static final int NON_SMOOTHED_CLIPPED_BITMAP = 0x43; - - @Conditional(value = "fillStyleType", options = {SOLID}) - public RGBA startColor; - - @Conditional(value = "fillStyleType", options = {SOLID}) - public RGBA endColor; - - @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) - public MATRIX startGradientMatrix; - - @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) - public MATRIX endGradientMatrix; - - @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) - public MORPHGRADIENT gradient; - - @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) - public int bitmapId; - - @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) - public MATRIX startBitmapMatrix; - - @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) - public MATRIX endBitmapMatrix; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - if ((fillStyleType == REPEATING_BITMAP) - || (fillStyleType == CLIPPED_BITMAP) - || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) - || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { - ret.add(bitmapId); - } - return ret; - } - - private MATRIX morphMatrix(MATRIX a, MATRIX b, int ratio) { - if (a == null) { - return null; - } - if (b == null) { - return null; - } - MATRIX ret = new MATRIX(); - double ratio_d = ratio / 65535.0; - ret.scaleX = (int) Math.round(a.getScaleX() + (b.getScaleX() - a.getScaleX()) * ratio_d); - ret.scaleY = (int) Math.round(a.getScaleY() + (b.getScaleY() - a.getScaleY()) * ratio_d); - ret.rotateSkew0 = (int) Math.round(a.getRotateSkew0() + (b.getRotateSkew0() - a.getRotateSkew0()) * ratio_d); - ret.rotateSkew1 = (int) Math.round(a.getRotateSkew1() + (b.getRotateSkew1() - a.getRotateSkew1()) * ratio_d); - ret.translateX = (int) Math.round(a.translateX + (b.translateX - a.translateX) * ratio_d); - ret.translateY = (int) Math.round(a.translateY + (b.translateY - a.translateY) * ratio_d); - ret.hasRotate = true; - ret.hasScale = true; - return ret; - } - - public FILLSTYLE getFillStyleAt(int ratio) { - FILLSTYLE ret = new FILLSTYLE(); - ret.bitmapId = bitmapId; - if (startBitmapMatrix != null) { - ret.bitmapMatrix = morphMatrix(startBitmapMatrix, endBitmapMatrix, ratio); - } - if (startColor != null) { - ret.color = MORPHGRADIENT.morphColor(startColor, endColor, ratio); - } - ret.fillStyleType = fillStyleType; - if (gradient != null) { - ret.gradient = gradient.getGradientAt(ratio); - } - if (startGradientMatrix != null) { - ret.gradientMatrix = morphMatrix(startGradientMatrix, endGradientMatrix, ratio); - } - return ret; - } - - public FILLSTYLE getStartFillStyle() { - FILLSTYLE ret = new FILLSTYLE(); - ret.bitmapId = bitmapId; - ret.bitmapMatrix = startBitmapMatrix; - ret.color = startColor; - ret.fillStyleType = fillStyleType; - if (gradient != null) { - ret.gradient = gradient.getStartGradient(); - } - ret.gradientMatrix = startGradientMatrix; - return ret; - } - - public FILLSTYLE getEndFillStyle() { - FILLSTYLE ret = new FILLSTYLE(); - ret.bitmapId = bitmapId; - ret.bitmapMatrix = endBitmapMatrix; - ret.color = endColor; - ret.fillStyleType = fillStyleType; - if (gradient != null) { - ret.gradient = gradient.getEndGradient(); - } - ret.gradientMatrix = endGradientMatrix; - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class MORPHFILLSTYLE implements NeedsCharacters, Serializable { + + @SWFType(BasicType.UI8) + public int fillStyleType; + + public static final int SOLID = 0x0; + public static final int LINEAR_GRADIENT = 0x10; + public static final int RADIAL_GRADIENT = 0x12; + public static final int FOCAL_RADIAL_GRADIENT = 0x13; + public static final int REPEATING_BITMAP = 0x40; + public static final int CLIPPED_BITMAP = 0x41; + public static final int NON_SMOOTHED_REPEATING_BITMAP = 0x42; + public static final int NON_SMOOTHED_CLIPPED_BITMAP = 0x43; + + @Conditional(value = "fillStyleType", options = {SOLID}) + public RGBA startColor; + + @Conditional(value = "fillStyleType", options = {SOLID}) + public RGBA endColor; + + @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) + public MATRIX startGradientMatrix; + + @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) + public MATRIX endGradientMatrix; + + @Conditional(value = "fillStyleType", options = {LINEAR_GRADIENT, RADIAL_GRADIENT}) + public MORPHGRADIENT gradient; + + @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) + public int bitmapId; + + @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) + public MATRIX startBitmapMatrix; + + @Conditional(value = "fillStyleType", options = {CLIPPED_BITMAP, NON_SMOOTHED_REPEATING_BITMAP, NON_SMOOTHED_CLIPPED_BITMAP}) + public MATRIX endBitmapMatrix; + + @Override + public void getNeededCharacters(Set needed) { + if ((fillStyleType == REPEATING_BITMAP) + || (fillStyleType == CLIPPED_BITMAP) + || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) + || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { + needed.add(bitmapId); + } + } + + @Override + public boolean removeCharacter(int characterId) { + if (bitmapId == characterId) { + if ((fillStyleType == REPEATING_BITMAP) + || (fillStyleType == CLIPPED_BITMAP) + || (fillStyleType == NON_SMOOTHED_REPEATING_BITMAP) + || (fillStyleType == NON_SMOOTHED_CLIPPED_BITMAP)) { + fillStyleType = SOLID; + } + bitmapId = 0; + return true; + } + return false; + } + + private MATRIX morphMatrix(MATRIX a, MATRIX b, int ratio) { + if (a == null) { + return null; + } + if (b == null) { + return null; + } + MATRIX ret = new MATRIX(); + double ratio_d = ratio / 65535.0; + ret.scaleX = (int) Math.round(a.getScaleX() + (b.getScaleX() - a.getScaleX()) * ratio_d); + ret.scaleY = (int) Math.round(a.getScaleY() + (b.getScaleY() - a.getScaleY()) * ratio_d); + ret.rotateSkew0 = (int) Math.round(a.getRotateSkew0() + (b.getRotateSkew0() - a.getRotateSkew0()) * ratio_d); + ret.rotateSkew1 = (int) Math.round(a.getRotateSkew1() + (b.getRotateSkew1() - a.getRotateSkew1()) * ratio_d); + ret.translateX = (int) Math.round(a.translateX + (b.translateX - a.translateX) * ratio_d); + ret.translateY = (int) Math.round(a.translateY + (b.translateY - a.translateY) * ratio_d); + ret.hasRotate = true; + ret.hasScale = true; + return ret; + } + + public FILLSTYLE getFillStyleAt(int ratio) { + FILLSTYLE ret = new FILLSTYLE(); + ret.bitmapId = bitmapId; + if (startBitmapMatrix != null) { + ret.bitmapMatrix = morphMatrix(startBitmapMatrix, endBitmapMatrix, ratio); + } + if (startColor != null) { + ret.color = MORPHGRADIENT.morphColor(startColor, endColor, ratio); + } + ret.fillStyleType = fillStyleType; + if (gradient != null) { + ret.gradient = gradient.getGradientAt(ratio); + } + if (startGradientMatrix != null) { + ret.gradientMatrix = morphMatrix(startGradientMatrix, endGradientMatrix, ratio); + } + return ret; + } + + public FILLSTYLE getStartFillStyle() { + FILLSTYLE ret = new FILLSTYLE(); + ret.bitmapId = bitmapId; + ret.bitmapMatrix = startBitmapMatrix; + ret.color = startColor; + ret.fillStyleType = fillStyleType; + if (gradient != null) { + ret.gradient = gradient.getStartGradient(); + } + ret.gradientMatrix = startGradientMatrix; + return ret; + } + + public FILLSTYLE getEndFillStyle() { + FILLSTYLE ret = new FILLSTYLE(); + ret.bitmapId = bitmapId; + ret.bitmapMatrix = endBitmapMatrix; + ret.color = endColor; + ret.fillStyleType = fillStyleType; + if (gradient != null) { + ret.gradient = gradient.getEndGradient(); + } + ret.gradientMatrix = endGradientMatrix; + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLEARRAY.java b/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLEARRAY.java index d7f731c82..2702bb1ae 100644 --- a/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLEARRAY.java +++ b/src/com/jpexs/decompiler/flash/types/MORPHFILLSTYLEARRAY.java @@ -1,67 +1,73 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class MORPHFILLSTYLEARRAY implements NeedsCharacters, Serializable { - - public MORPHFILLSTYLE[] fillStyles; - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - for (MORPHFILLSTYLE fs : fillStyles) { - ret.addAll(fs.getNeededCharacters()); - } - return ret; - } - - public FILLSTYLEARRAY getFillStylesAt(int ratio) { - FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); - ret.fillStyles = new FILLSTYLE[fillStyles.length]; - for (int m = 0; m < fillStyles.length; m++) { - ret.fillStyles[m] = fillStyles[m].getFillStyleAt(ratio); - } - return ret; - } - - public FILLSTYLEARRAY getStartFillStyles() { - FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); - ret.fillStyles = new FILLSTYLE[fillStyles.length]; - for (int m = 0; m < fillStyles.length; m++) { - ret.fillStyles[m] = fillStyles[m].getStartFillStyle(); - } - return ret; - } - - public FILLSTYLEARRAY getEndFillStyles() { - FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); - ret.fillStyles = new FILLSTYLE[fillStyles.length]; - for (int m = 0; m < fillStyles.length; m++) { - ret.fillStyles[m] = fillStyles[m].getEndFillStyle(); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class MORPHFILLSTYLEARRAY implements NeedsCharacters, Serializable { + + public MORPHFILLSTYLE[] fillStyles; + + @Override + public void getNeededCharacters(Set needed) { + for (MORPHFILLSTYLE fs : fillStyles) { + fs.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (MORPHFILLSTYLE fs : fillStyles) { + modified |= fs.removeCharacter(characterId); + } + return modified; + } + + public FILLSTYLEARRAY getFillStylesAt(int ratio) { + FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); + ret.fillStyles = new FILLSTYLE[fillStyles.length]; + for (int m = 0; m < fillStyles.length; m++) { + ret.fillStyles[m] = fillStyles[m].getFillStyleAt(ratio); + } + return ret; + } + + public FILLSTYLEARRAY getStartFillStyles() { + FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); + ret.fillStyles = new FILLSTYLE[fillStyles.length]; + for (int m = 0; m < fillStyles.length; m++) { + ret.fillStyles[m] = fillStyles[m].getStartFillStyle(); + } + return ret; + } + + public FILLSTYLEARRAY getEndFillStyles() { + FILLSTYLEARRAY ret = new FILLSTYLEARRAY(); + ret.fillStyles = new FILLSTYLE[fillStyles.length]; + for (int m = 0; m < fillStyles.length; m++) { + ret.fillStyles[m] = fillStyles[m].getEndFillStyle(); + } + return ret; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/SHAPE.java b/src/com/jpexs/decompiler/flash/types/SHAPE.java index 757aaa9b4..bcfacb99f 100644 --- a/src/com/jpexs/decompiler/flash/types/SHAPE.java +++ b/src/com/jpexs/decompiler/flash/types/SHAPE.java @@ -1,65 +1,71 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.exporters.shape.PathExporter; -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import java.awt.Shape; -import java.awt.geom.Area; -import java.awt.geom.GeneralPath; -import java.io.Serializable; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class SHAPE implements NeedsCharacters, Serializable { - - @SWFType(value = BasicType.UB, count = 4) - public int numFillBits; - @SWFType(value = BasicType.UB, count = 4) - public int numLineBits; - public List shapeRecords; - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - for (SHAPERECORD r : shapeRecords) { - ret.addAll(r.getNeededCharacters()); - } - return ret; - } - - public RECT getBounds() { - return SHAPERECORD.getBounds(shapeRecords); - } - - public Shape getOutline() { - List paths = PathExporter.export(this); - Area area = new Area(); - for (GeneralPath path : paths) { - area.add(new Area(path)); - } - - return area; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.exporters.shape.PathExporter; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import java.awt.Shape; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class SHAPE implements NeedsCharacters, Serializable { + + @SWFType(value = BasicType.UB, count = 4) + public int numFillBits; + @SWFType(value = BasicType.UB, count = 4) + public int numLineBits; + public List shapeRecords; + + @Override + public void getNeededCharacters(Set needed) { + for (SHAPERECORD r : shapeRecords) { + r.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + for (SHAPERECORD r : shapeRecords) { + modified |= r.removeCharacter(characterId); + } + return modified; + } + + public RECT getBounds() { + return SHAPERECORD.getBounds(shapeRecords); + } + + public Shape getOutline() { + List paths = PathExporter.export(this); + Area area = new Area(); + for (GeneralPath path : paths) { + area.add(new Area(path)); + } + + return area; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java b/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java index f433ec4c2..135ae8c1f 100644 --- a/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java +++ b/src/com/jpexs/decompiler/flash/types/SHAPEWITHSTYLE.java @@ -1,44 +1,52 @@ -/* - * Copyright (C) 2010-2014 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.types; - -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author JPEXS - */ -public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializable { - - public FILLSTYLEARRAY fillStyles; - public LINESTYLEARRAY lineStyles; - - @Override - public Set getNeededCharacters() { - Set ret = new HashSet<>(); - ret.addAll(fillStyles.getNeededCharacters()); - ret.addAll(lineStyles.getNeededCharacters()); - for (SHAPERECORD r : shapeRecords) { - ret.addAll(r.getNeededCharacters()); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.types; + +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import java.io.Serializable; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class SHAPEWITHSTYLE extends SHAPE implements NeedsCharacters, Serializable { + + public FILLSTYLEARRAY fillStyles; + public LINESTYLEARRAY lineStyles; + + @Override + public void getNeededCharacters(Set needed) { + fillStyles.getNeededCharacters(needed); + lineStyles.getNeededCharacters(needed); + for (SHAPERECORD r : shapeRecords) { + r.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + boolean modified = false; + modified |= fillStyles.removeCharacter(characterId); + modified |= lineStyles.removeCharacter(characterId); + for (SHAPERECORD r : shapeRecords) { + modified |= r.removeCharacter(characterId); + } + return modified; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java b/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java index 73d20957d..4c0a9faf0 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java +++ b/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java @@ -1,114 +1,114 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; - -/** - * The Bevel filter creates a smooth bevel on display list objects. - * - * @author JPEXS - */ -public class BEVELFILTER extends FILTER { - - /** - * Color of the shadow - */ - public RGBA shadowColor; - /** - * Color of the highlight - */ - public RGBA highlightColor; - /** - * Horizontal blur amount - */ - @SWFType(BasicType.FIXED) - public double blurX; - - /** - * Vertical blur amount - */ - @SWFType(BasicType.FIXED) - public double blurY; - /** - * Radian angle of the drop shadow - */ - @SWFType(BasicType.FIXED) - public double angle; - /** - * Distance of the drop shadow - */ - @SWFType(BasicType.FIXED) - public double distance; - /** - * Strength of the drop shadow - */ - @SWFType(BasicType.FIXED8) - public float strength; - /** - * Inner shadow mode - */ - public boolean innerShadow; - /** - * Knockout mode - */ - public boolean knockout; - /** - * Composite source - */ - public boolean compositeSource; - /** - * OnTop mode - */ - public boolean onTop; - /** - * Number of blur passes - */ - @SWFType(value = BasicType.UB, count = 4) - public int passes; - - /** - * Constructor - */ - public BEVELFILTER() { - super(3); - } - - @Override - public SerializableImage apply(SerializableImage src) { - int type = Filtering.INNER; - if (onTop && !innerShadow) { - type = Filtering.FULL; - } else if (!innerShadow) { - type = Filtering.OUTER; - } - return Filtering.bevel(src, (int) blurX, (int) blurY, strength, type, highlightColor.toColor(), shadowColor.toColor(), (int) (angle * 180 / Math.PI), (float) distance, knockout, passes); - } - - @Override - public double getDeltaX() { - return blurX + (distance * Math.cos(angle)); - } - - @Override - public double getDeltaY() { - return blurY +(distance * Math.sin(angle)); - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; + +/** + * The Bevel filter creates a smooth bevel on display list objects. + * + * @author JPEXS + */ +public class BEVELFILTER extends FILTER { + + /** + * Color of the shadow + */ + public RGBA shadowColor; + /** + * Color of the highlight + */ + public RGBA highlightColor; + /** + * Horizontal blur amount + */ + @SWFType(BasicType.FIXED) + public double blurX; + + /** + * Vertical blur amount + */ + @SWFType(BasicType.FIXED) + public double blurY; + /** + * Radian angle of the drop shadow + */ + @SWFType(BasicType.FIXED) + public double angle; + /** + * Distance of the drop shadow + */ + @SWFType(BasicType.FIXED) + public double distance; + /** + * Strength of the drop shadow + */ + @SWFType(BasicType.FIXED8) + public float strength; + /** + * Inner shadow mode + */ + public boolean innerShadow; + /** + * Knockout mode + */ + public boolean knockout; + /** + * Composite source + */ + public boolean compositeSource; + /** + * OnTop mode + */ + public boolean onTop; + /** + * Number of blur passes + */ + @SWFType(value = BasicType.UB, count = 4) + public int passes; + + /** + * Constructor + */ + public BEVELFILTER() { + super(3); + } + + @Override + public SerializableImage apply(SerializableImage src) { + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + return Filtering.bevel(src, (int) blurX, (int) blurY, strength, type, highlightColor.toColor(), shadowColor.toColor(), (int) (angle * 180 / Math.PI), (float) distance, knockout, passes); + } + + @Override + public double getDeltaX() { + return blurX + (distance * Math.cos(angle)); + } + + @Override + public double getDeltaY() { + return blurY + (distance * Math.sin(angle)); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java b/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java index 66daae4b1..be1d8b76f 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java +++ b/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java @@ -1,99 +1,99 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; - -/** - * Drop shadow filter based on the same median filter as the blur filter - * - * @author JPEXS - */ -public class DROPSHADOWFILTER extends FILTER { - - /** - * Color of the shadow - */ - public RGBA dropShadowColor; - /** - * Horizontal blur amount - */ - @SWFType(BasicType.FIXED) - public double blurX; - /** - * Vertical blur amount - */ - @SWFType(BasicType.FIXED) - public double blurY; - /** - * Radian angle of the drop shadow - */ - @SWFType(BasicType.FIXED) - public double angle; - /** - * Distance of the drop shadow - */ - @SWFType(BasicType.FIXED) - public double distance; - /** - * Strength of the drop shadow - */ - @SWFType(BasicType.FIXED8) - public float strength; - /** - * Inner shadow mode - */ - public boolean innerShadow; - /** - * Knockout mode - */ - public boolean knockout; - /** - * Composite source - */ - public boolean compositeSource; - /** - * Number of blur passes - */ - @SWFType(value = BasicType.UB, count = 5) - public int passes; - - /** - * Constructor - */ - public DROPSHADOWFILTER() { - super(0); - } - - @Override - public SerializableImage apply(SerializableImage src) { - return Filtering.dropShadow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, dropShadowColor.toColor(), innerShadow, passes, strength, knockout); - } - - @Override - public double getDeltaX() { - return blurX + (distance * Math.cos(angle)); - } - - @Override - public double getDeltaY() { - return blurY +(distance * Math.sin(angle)); - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; + +/** + * Drop shadow filter based on the same median filter as the blur filter + * + * @author JPEXS + */ +public class DROPSHADOWFILTER extends FILTER { + + /** + * Color of the shadow + */ + public RGBA dropShadowColor; + /** + * Horizontal blur amount + */ + @SWFType(BasicType.FIXED) + public double blurX; + /** + * Vertical blur amount + */ + @SWFType(BasicType.FIXED) + public double blurY; + /** + * Radian angle of the drop shadow + */ + @SWFType(BasicType.FIXED) + public double angle; + /** + * Distance of the drop shadow + */ + @SWFType(BasicType.FIXED) + public double distance; + /** + * Strength of the drop shadow + */ + @SWFType(BasicType.FIXED8) + public float strength; + /** + * Inner shadow mode + */ + public boolean innerShadow; + /** + * Knockout mode + */ + public boolean knockout; + /** + * Composite source + */ + public boolean compositeSource; + /** + * Number of blur passes + */ + @SWFType(value = BasicType.UB, count = 5) + public int passes; + + /** + * Constructor + */ + public DROPSHADOWFILTER() { + super(0); + } + + @Override + public SerializableImage apply(SerializableImage src) { + return Filtering.dropShadow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, dropShadowColor.toColor(), innerShadow, passes, strength, knockout); + } + + @Override + public double getDeltaX() { + return blurX + (distance * Math.cos(angle)); + } + + @Override + public double getDeltaY() { + return blurY + (distance * Math.sin(angle)); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/Filtering.java b/src/com/jpexs/decompiler/flash/types/filters/Filtering.java index 2296d4cbc..284dc3920 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/Filtering.java +++ b/src/com/jpexs/decompiler/flash/types/filters/Filtering.java @@ -1,612 +1,603 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.helpers.SerializableImage; -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.LinearGradientPaint; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; -import java.awt.image.BandCombineOp; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.awt.image.ConvolveOp; -import java.awt.image.Kernel; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; - -/** - * - * @author JPEXS - */ -public class Filtering { - - public static final int INNER = 1; - public static final int OUTER = 2; - public static final int FULL = 3; - - private static void boxBlurHorizontal(int[] pixels, int[] mask, int w, int h, int radius) { - int index = 0; - int[] newColors = new int[w]; - - for (int y = 0; y < h; y++) { - int hits = 0; - long r = 0; - long g = 0; - long b = 0; - long a = 0; - for (int x = -radius; x < w; x++) { - int oldPixel = x - radius - 1; - if (oldPixel >= 0) { - - int color = pixels[index + oldPixel]; - if ((mask == null) || (((mask[index + oldPixel] >> 24) & 0xff) > 0)) { - if (color != 0) { - a -= (color >> 24) & 0xff; - r -= ((color >> 16) & 0xff); - g -= ((color >> 8) & 0xff); - b -= ((color) & 0xff); - - } - hits--; - } - } - - int newPixel = x + radius; - if (newPixel < w) { - int color = pixels[index + newPixel]; - if ((mask == null) || (((mask[index + newPixel] >> 24) & 0xff) > 0)) { - if (color != 0) { - a += (color >> 24) & 0xff; - r += ((color >> 16) & 0xff); - g += ((color >> 8) & 0xff); - b += ((color) & 0xff); - } - hits++; - } - } - - if (x >= 0) { - if ((mask == null) || (((mask[index + x] >> 24) & 0xff) > 0)) { - if (hits == 0) { - newColors[x] = 0; - } else { - newColors[x] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits)).getRGB(); - - } - } else { - newColors[x] = 0; - } - } - } - System.arraycopy(newColors, 0, pixels, index, w); - - index += w; - } - } - - private static void boxBlurVertical(int[] pixels, int[] mask, int w, int h, int radius) { - int[] newColors = new int[h]; - int oldPixelOffset = -(radius + 1) * w; - int newPixelOffset = (radius) * w; - - for (int x = 0; x < w; x++) { - int hits = 0; - long r = 0; - long g = 0; - long b = 0; - long a = 0; - int index = -radius * w + x; - for (int y = -radius; y < h; y++) { - int oldPixel = y - radius - 1; - if (oldPixel >= 0) { - int color = pixels[index + oldPixelOffset]; - if ((mask == null) || (((mask[index + oldPixelOffset] >> 24) & 0xff) > 0)) { - if (color != 0) { - a -= (color >> 24) & 0xff; - r -= ((color >> 16) & 0xff); - g -= ((color >> 8) & 0xff); - b -= ((color) & 0xff); - - } - hits--; - } - - } - - int newPixel = y + radius; - if (newPixel < h) { - if ((mask == null) || (((mask[index + newPixelOffset] >> 24) & 0xff) > 0)) { - int color = pixels[index + newPixelOffset]; - if (color != 0) { - a += (color >> 24) & 0xff; - r += ((color >> 16) & 0xff); - g += ((color >> 8) & 0xff); - b += ((color) & 0xff); - - } - hits++; - } - } - - if (y >= 0) { - if ((mask == null) || (((mask[y * w + x] >> 24) & 0xff) > 0)) { - if (hits == 0) { - newColors[y] = 0; - } else { - newColors[y] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits) & 0xff).getRGB(); - } - } else { - newColors[y] = 0; - } - } - - index += w; - } - - for (int y = 0; y < h; y++) { - pixels[y * w + x] = newColors[y]; - } - } - } - - private static void premultiply(int[] p) { - int length = p.length; - int offset = 0; - length += offset; - for (int i = offset; i < length; i++) { - int rgb = p[i]; - int a = rgb >> 24 & 0xff; - int r = rgb >> 16 & 0xff; - int g = rgb >> 8 & 0xff; - int b = rgb & 0xff; - float f = (float) a * 0.003921569F; - r = (int) ((float) r * f); - g = (int) ((float) g * f); - b = (int) ((float) b * f); - p[i] = a << 24 | r << 16 | g << 8 | b; - } - } - - private static void unpremultiply(int[] p) { - int length = p.length; - int offset = 0; - length += offset; - for (int i = offset; i < length; i++) { - int rgb = p[i]; - int a = rgb >> 24 & 0xff; - int r = rgb >> 16 & 0xff; - int g = rgb >> 8 & 0xff; - int b = rgb & 0xff; - if (a == 0 || a == 255) { - continue; - } - float f = 255F / (float) a; - r = (int) ((float) r * f); - g = (int) ((float) g * f); - b = (int) ((float) b * f); - if (r > 255) { - r = 255; - } - if (g > 255) { - g = 255; - } - if (b > 255) { - b = 255; - } - p[i] = a << 24 | r << 16 | g << 8 | b; - } - } - - public static SerializableImage blur(SerializableImage src, int hRadius, int vRadius, int iterations) { - return new SerializableImage(blur(src.getBufferedImage(), hRadius, vRadius, iterations, null)); - } - - private static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations, int[] mask) { - int width = src.getWidth(); - int height = src.getHeight(); - - BufferedImage dst = new BufferedImage(width, height, src.getType()); - - int[] inPixels = getRGB(src, 0, 0, width, height); - premultiply(inPixels); - - for (int i = 0; i < iterations; i++) { - boxBlurHorizontal(inPixels, mask, width, height, hRadius / 2); - boxBlurVertical(inPixels, mask, width, height, vRadius / 2); - } - unpremultiply(inPixels); - setRGB(dst, 0, 0, width, height, inPixels); - return dst; - } - - public static SerializableImage bevel(SerializableImage src, int blurX, int blurY, float strength, int type, Color highlightColor, Color shadowColor, float angle, float distance, boolean knockout, int iterations) { - return new SerializableImage(gradientBevel(src.getBufferedImage(), new Color[]{ - shadowColor, - new Color(shadowColor.getRed(), shadowColor.getGreen(), shadowColor.getBlue(), 0), - new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 0), - highlightColor - }, new float[]{0, 127f / 255f, 128f / 255f, 1}, blurX, blurY, strength, type, angle, distance, knockout, iterations)); - } - - public static SerializableImage gradientBevel(SerializableImage src, Color[] colors, float[] ratios, int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) { - return new SerializableImage(gradientBevel(src.getBufferedImage(), colors, ratios, blurX, blurY, strength, type, angle, distance, knockout, iterations)); - } - - private static BufferedImage gradientBevel(BufferedImage src, Color[] colors, float[] ratios, int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) { - int width = src.getWidth(); - int height = src.getHeight(); - BufferedImage retImg = new BufferedImage(width,height,src.getType()); - int srcPixels[] = getRGB(src, 0, 0, width, height); - - int revPixels[] = new int[srcPixels.length]; - for (int i = 0; i < srcPixels.length; i++) { - revPixels[i] = (srcPixels[i]&0xffffff)+((255-((srcPixels[i]>>24) & 0xff))<<24); - } - - BufferedImage gradient = new BufferedImage(512, 1,src.getType()); - Graphics2D gg = gradient.createGraphics(); - - - Point p1 = new Point(0, 0); - Point p2 = new Point(511, 0); - gg.setPaint(new LinearGradientPaint(p1,p2, ratios, colors)); - gg.fill(new Rectangle(512, 1)); - int[] gradientPixels = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight()); - - BufferedImage shadowInner = null; - BufferedImage hilightInner = null; - if(type!=OUTER){ - BufferedImage hilightIm = dropShadow(src,0, 0, angle, distance, Color.red, true, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src - BufferedImage shadowIm = dropShadow(src, 0, 0, angle + 180, distance, Color.blue, true, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src); - BufferedImage h2 = new BufferedImage(width, height, src.getType()); - BufferedImage s2 = new BufferedImage(width, height, src.getType()); - Graphics2D hc=h2.createGraphics(); - Graphics2D sc=s2.createGraphics(); - hc.drawImage(hilightIm,0,0,null); - hc.setComposite(AlphaComposite.DstOut); - hc.drawImage(shadowIm,0,0,null); - - sc.drawImage(shadowIm,0,0,null); - sc.setComposite(AlphaComposite.DstOut); - sc.drawImage(hilightIm,0,0,null); - shadowInner = s2; - hilightInner = h2; - } - BufferedImage shadowOuter = null; - BufferedImage hilightOuter = null; - if(type!=INNER){ - BufferedImage hilightIm = dropShadow(src,0, 0, angle + 180, distance, Color.red, false, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src - BufferedImage shadowIm = dropShadow(src, 0, 0, angle, distance, Color.blue, false, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src); - BufferedImage h2 = new BufferedImage(width, height, src.getType()); - BufferedImage s2 = new BufferedImage(width, height, src.getType()); - Graphics2D hc=h2.createGraphics(); - Graphics2D sc=s2.createGraphics(); - hc.drawImage(hilightIm,0,0,null); - hc.setComposite(AlphaComposite.DstOut); - hc.drawImage(shadowIm,0,0,null); - - sc.drawImage(shadowIm,0,0,null); - sc.setComposite(AlphaComposite.DstOut); - sc.drawImage(hilightIm,0,0,null); - shadowOuter = s2; - hilightOuter = h2; - } - - BufferedImage hilightIm = null; - BufferedImage shadowIm = null; - switch(type) - { - case OUTER: - hilightIm = hilightOuter; - shadowIm = shadowOuter; - break; - case INNER: - hilightIm = hilightInner; - shadowIm = shadowInner; - break; - case FULL: - hilightIm = hilightInner; - shadowIm = shadowInner; - Graphics2D hc=hilightIm.createGraphics(); - hc.setComposite(AlphaComposite.SrcOver); - hc.drawImage(hilightOuter,0,0,null); - Graphics2D sc=shadowIm.createGraphics(); - sc.setComposite(AlphaComposite.SrcOver); - sc.drawImage(shadowOuter,0,0,null); - break; - } - - int mask[] = null; - if(type == INNER){ - mask = srcPixels; - } - if(type == OUTER){ - mask = revPixels; - } - - Graphics2D retc = retImg.createGraphics(); - retc.setColor(Color.black); - retc.fillRect(0,0,width,height); - retc.setComposite(AlphaComposite.SrcOver); - retc.drawImage(shadowIm,0,0,null); - retc.drawImage(hilightIm,0,0,null); - - /*if(true) - return retImg; - */ - retImg = blur(retImg,blurX,blurY,iterations,mask); - int ret[] = getRGB(retImg, 0, 0, width, height); - - for (int i = 0; i < srcPixels.length; i++) { - int ah = (int) (new Color(ret[i]).getRed()* strength); - int as = (int) (new Color(ret[i]).getBlue() * strength); - int ra = cut(ah-as,-255,255); - ret[i] = gradientPixels[255 + ra]; - } - setRGB(retImg, 0, 0, width, height, ret); - - - if (!knockout) { - Graphics2D g = retImg.createGraphics(); - g.setComposite(AlphaComposite.DstOver); - g.drawImage(src, 0, 0, null); - } - return retImg; - } - - public static SerializableImage glow(SerializableImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) { - return new SerializableImage(dropShadow(src.getBufferedImage(), blurX, blurY, 45, 0, color, inner, iterations, strength, knockout)); - } - - public static SerializableImage dropShadow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) { - return new SerializableImage(dropShadow(src.getBufferedImage(), blurX, blurY, angle, distance, color, inner, iterations, strength, knockout)); - } - - private static int cut(int val, int min, int max) { - if (val > max) { - val = max; - } - if (val < min) { - val = min; - } - return val; - } - - private static BufferedImage dropShadow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) { - int width = src.getWidth(); - int height = src.getHeight(); - int[] srcPixels = getRGB(src, 0, 0, width, height); - int shadow[] = new int[srcPixels.length]; - for (int i = 0; i < srcPixels.length; i++) { - int alpha = (srcPixels[i]>>24) & 0xff; - if (inner) { - alpha = 255 - alpha; - } - shadow[i] = new Color(color.getRed(), color.getGreen(), color.getBlue(), cut(color.getAlpha() * alpha * strength)).getRGB(); - } - Color colorFirst = Color.BLACK; - Color colorAlpha = new Color(0, 0, 0, 0); - double angleRad = angle / 180 * Math.PI; - double moveX = (distance * Math.cos(angleRad)); - double moveY = (distance * Math.sin(angleRad)); - shadow = moveRGB(width, height, shadow, moveX, moveY, inner ? colorFirst : colorAlpha); - - BufferedImage retCanvas = new BufferedImage(width, height, src.getType()); - setRGB(retCanvas, 0, 0, width, height, shadow); - if (blurX > 0 || blurY > 0) { - retCanvas = blur(retCanvas, blurX, blurY, iterations, null); - } - shadow = getRGB(retCanvas, 0, 0, width, height); - - for (int i = 0; i < shadow.length; i++) { - int mask = (srcPixels[i]>>24) & 0xff; - if (!inner) { - mask = 255 - mask; - } - shadow[i] = shadow[i] & 0xffffff + ((mask * ((shadow[i]>>24) & 0xff) / 255)<<24); - } - setRGB(retCanvas, 0, 0, width, height, shadow); - - if (!knockout) { - Graphics2D g = retCanvas.createGraphics(); - g.setComposite(AlphaComposite.DstOver); - g.drawImage(src, 0, 0, null); - } - - return retCanvas; - } - - public static SerializableImage gradientGlow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) { - return new SerializableImage(gradientGlow(src.getBufferedImage(), blurX, blurY, angle, distance, colors, ratios, type, iterations, strength, knockout)); - } - - private static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) { - - int width = src.getWidth(); - int height = src.getHeight(); - BufferedImage retCanvas = new BufferedImage(width, height, src.getType()); - Graphics2D retImg = retCanvas.createGraphics(); - - BufferedImage gradCanvas = new BufferedImage(256,1,src.getType()); - - Graphics2D gg = gradCanvas.createGraphics(); - - - Point p1 = new Point(0, 0); - Point p2 = new Point(255, 0); - gg.setPaint(new LinearGradientPaint(p1,p2, ratios, colors)); - gg.fill(new Rectangle(256, 1)); - int[] gradientPixels = getRGB(gradCanvas, 0, 0, gradCanvas.getWidth(), gradCanvas.getHeight()); - - double angleRad = angle / 180 * Math.PI; - double moveX = (distance * Math.cos(angleRad)); - double moveY = (distance * Math.sin(angleRad)); - int srcPixels[] = getRGB(src, 0, 0, width, height); - int revPixels[] = new int[srcPixels.length]; - for (int i = 0; i < srcPixels.length; i++) { - revPixels[i] = (srcPixels[i]&0xffffff)+((255-((srcPixels[i]>>24) & 0xff))<<24); - } - int shadow[] = new int[srcPixels.length]; - for (int i = 0; i < srcPixels.length; i++) { - shadow[i] = 0+((cut(strength*((srcPixels[i]>>24) & 0xff)))<<24); - } - Color colorAlpha = new Color(0,0,0,0); - shadow = moveRGB(width, height, shadow, moveX, moveY, colorAlpha); - - setRGB(retCanvas, 0, 0, width, height, shadow); - - int mask[] = null; - if(type == INNER){ - mask = srcPixels; - } - if(type == OUTER){ - mask = revPixels; - } - - - retCanvas = blur(retCanvas, blurX, blurY, iterations,mask); - shadow = getRGB(retCanvas, 0, 0, width, height); - - if(mask!=null){ - for (int i = 0; i < mask.length; i++) { - int m = (mask[i]>>24); - if(m == 0){ - shadow[i] = 0; - } - } - } - - - - - - for (int i = 0; i < shadow.length; i++) { - int a = (shadow[i]>>24) & 0xff; - shadow[i] = gradientPixels[a]; - } - - setRGB(retCanvas,0,0,width,height,shadow); - - if (!knockout) { - retImg = retCanvas.createGraphics(); - retImg.setComposite(AlphaComposite.DstOver); - retImg.drawImage(src,0,0,null); - } - - return retCanvas; - } - - private static int[] getRGB(BufferedImage image, int x, int y, int width, int height) { - int type = image.getType(); - if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { - return (int[]) image.getRaster().getDataElements(x, y, width, height, null); - } - return image.getRGB(x, y, width, height, null, 0, width); - } - - private static void setRGB(BufferedImage image, int x, int y, int width, int height, int[] pixels) { - int type = image.getType(); - if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { - image.getRaster().setDataElements(x, y, width, height, pixels); - } else { - image.setRGB(x, y, width, height, pixels, 0, width); - } - } - - private static int[] moveRGB(int width, int height, int[] rgb, double deltaX, double deltaY, Color fill) { - BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - setRGB(img, 0, 0, width, height, rgb); - BufferedImage retImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D g = (Graphics2D) retImg.getGraphics(); - g.setPaint(fill); - g.fillRect(0, 0, width, height); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g.setTransform(AffineTransform.getTranslateInstance(deltaX, deltaY)); - g.setComposite(AlphaComposite.Src); - g.drawImage(img, 0, 0, null); - return getRGB(retImg, 0, 0, width, height); - } - - public static SerializableImage convolution(SerializableImage src, float[] matrix, int w, int h) { - BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); - BufferedImageOp op = new ConvolveOp(new Kernel(w, h, matrix), ConvolveOp.EDGE_ZERO_FILL, new RenderingHints(null)); - op.filter(src.getBufferedImage(), dst); - return new SerializableImage(dst); - } - - public static SerializableImage colorMatrix(SerializableImage src, float[][] matrix) { - BandCombineOp changeColors = new BandCombineOp(matrix, new RenderingHints(null)); - Raster sourceRaster = src.getRaster(); - WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster(); - changeColors.filter(sourceRaster, displayRaster); - return new SerializableImage(src.getColorModel(), displayRaster, true, null); - } - - private static int cut(double val) { - int i = (int) val; - if (i < 0) { - i = 0; - } - if (i > 255) { - i = 255; - } - return i; - } - - public static Color colorEffect(Color color, - int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm, - int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) { - int rgb = color.getRGB(); - int a = (rgb >> 24) & 0xff; - int r = (rgb >> 16) & 0xff; - int g = (rgb >> 8) & 0xff; - int b = (rgb) & 0xff; - r = Math.max(0, Math.min(((r * redMultTerm) / 255) + redAddTerm, 255)); - g = Math.max(0, Math.min(((g * greenMultTerm) / 255) + greenAddTerm, 255)); - b = Math.max(0, Math.min(((b * blueMultTerm) / 255) + blueAddTerm, 255)); - a = Math.max(0, Math.min(((a * alphaMultTerm) / 255) + alphaAddTerm, 255)); - return new Color(r, g, b, a); - } - - public static SerializableImage colorEffect(SerializableImage src, - int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm, - int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) { - BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); - int rgb[] = getRGB(src.getBufferedImage(), 0, 0, src.getWidth(), src.getHeight()); - for (int i = 0; i < rgb.length; i++) { - int a = (rgb[i] >> 24) & 0xff; - int r = (rgb[i] >> 16) & 0xff; - int g = (rgb[i] >> 8) & 0xff; - int b = (rgb[i]) & 0xff; - r = Math.max(0, Math.min(((r * redMultTerm) / 256) + redAddTerm, 255)); - g = Math.max(0, Math.min(((g * greenMultTerm) / 256) + greenAddTerm, 255)); - b = Math.max(0, Math.min(((b * blueMultTerm) / 256) + blueAddTerm, 255)); - a = Math.max(0, Math.min(((a * alphaMultTerm) / 256) + alphaAddTerm, 255)); - rgb[i] = (a << 24) | (r << 16) | (g << 8) | (b); - } - setRGB(dst, 0, 0, src.getWidth(), src.getHeight(), rgb); - return new SerializableImage(dst); - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.helpers.SerializableImage; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.LinearGradientPaint; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.BandCombineOp; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * + * @author JPEXS + */ +public class Filtering { + + public static final int INNER = 1; + public static final int OUTER = 2; + public static final int FULL = 3; + + private static void boxBlurHorizontal(int[] pixels, int[] mask, int w, int h, int radius) { + int index = 0; + int[] newColors = new int[w]; + + for (int y = 0; y < h; y++) { + int hits = 0; + long r = 0; + long g = 0; + long b = 0; + long a = 0; + for (int x = -radius; x < w; x++) { + int oldPixel = x - radius - 1; + if (oldPixel >= 0) { + + int color = pixels[index + oldPixel]; + if ((mask == null) || (((mask[index + oldPixel] >> 24) & 0xff) > 0)) { + if (color != 0) { + a -= (color >> 24) & 0xff; + r -= ((color >> 16) & 0xff); + g -= ((color >> 8) & 0xff); + b -= ((color) & 0xff); + + } + hits--; + } + } + + int newPixel = x + radius; + if (newPixel < w) { + int color = pixels[index + newPixel]; + if ((mask == null) || (((mask[index + newPixel] >> 24) & 0xff) > 0)) { + if (color != 0) { + a += (color >> 24) & 0xff; + r += ((color >> 16) & 0xff); + g += ((color >> 8) & 0xff); + b += ((color) & 0xff); + } + hits++; + } + } + + if (x >= 0) { + if ((mask == null) || (((mask[index + x] >> 24) & 0xff) > 0)) { + if (hits == 0) { + newColors[x] = 0; + } else { + newColors[x] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits)).getRGB(); + + } + } else { + newColors[x] = 0; + } + } + } + System.arraycopy(newColors, 0, pixels, index, w); + + index += w; + } + } + + private static void boxBlurVertical(int[] pixels, int[] mask, int w, int h, int radius) { + int[] newColors = new int[h]; + int oldPixelOffset = -(radius + 1) * w; + int newPixelOffset = (radius) * w; + + for (int x = 0; x < w; x++) { + int hits = 0; + long r = 0; + long g = 0; + long b = 0; + long a = 0; + int index = -radius * w + x; + for (int y = -radius; y < h; y++) { + int oldPixel = y - radius - 1; + if (oldPixel >= 0) { + int color = pixels[index + oldPixelOffset]; + if ((mask == null) || (((mask[index + oldPixelOffset] >> 24) & 0xff) > 0)) { + if (color != 0) { + a -= (color >> 24) & 0xff; + r -= ((color >> 16) & 0xff); + g -= ((color >> 8) & 0xff); + b -= ((color) & 0xff); + + } + hits--; + } + + } + + int newPixel = y + radius; + if (newPixel < h) { + if ((mask == null) || (((mask[index + newPixelOffset] >> 24) & 0xff) > 0)) { + int color = pixels[index + newPixelOffset]; + if (color != 0) { + a += (color >> 24) & 0xff; + r += ((color >> 16) & 0xff); + g += ((color >> 8) & 0xff); + b += ((color) & 0xff); + + } + hits++; + } + } + + if (y >= 0) { + if ((mask == null) || (((mask[y * w + x] >> 24) & 0xff) > 0)) { + if (hits == 0) { + newColors[y] = 0; + } else { + newColors[y] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits) & 0xff).getRGB(); + } + } else { + newColors[y] = 0; + } + } + + index += w; + } + + for (int y = 0; y < h; y++) { + pixels[y * w + x] = newColors[y]; + } + } + } + + private static void premultiply(int[] p) { + int length = p.length; + int offset = 0; + length += offset; + for (int i = offset; i < length; i++) { + int rgb = p[i]; + int a = rgb >> 24 & 0xff; + int r = rgb >> 16 & 0xff; + int g = rgb >> 8 & 0xff; + int b = rgb & 0xff; + float f = (float) a * 0.003921569F; + r = (int) ((float) r * f); + g = (int) ((float) g * f); + b = (int) ((float) b * f); + p[i] = a << 24 | r << 16 | g << 8 | b; + } + } + + private static void unpremultiply(int[] p) { + int length = p.length; + int offset = 0; + length += offset; + for (int i = offset; i < length; i++) { + int rgb = p[i]; + int a = rgb >> 24 & 0xff; + int r = rgb >> 16 & 0xff; + int g = rgb >> 8 & 0xff; + int b = rgb & 0xff; + if (a == 0 || a == 255) { + continue; + } + float f = 255F / (float) a; + r = (int) ((float) r * f); + g = (int) ((float) g * f); + b = (int) ((float) b * f); + if (r > 255) { + r = 255; + } + if (g > 255) { + g = 255; + } + if (b > 255) { + b = 255; + } + p[i] = a << 24 | r << 16 | g << 8 | b; + } + } + + public static SerializableImage blur(SerializableImage src, int hRadius, int vRadius, int iterations) { + return new SerializableImage(blur(src.getBufferedImage(), hRadius, vRadius, iterations, null)); + } + + private static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations, int[] mask) { + int width = src.getWidth(); + int height = src.getHeight(); + + BufferedImage dst = new BufferedImage(width, height, src.getType()); + + int[] inPixels = getRGB(src, 0, 0, width, height); + premultiply(inPixels); + + for (int i = 0; i < iterations; i++) { + boxBlurHorizontal(inPixels, mask, width, height, hRadius / 2); + boxBlurVertical(inPixels, mask, width, height, vRadius / 2); + } + unpremultiply(inPixels); + setRGB(dst, 0, 0, width, height, inPixels); + return dst; + } + + public static SerializableImage bevel(SerializableImage src, int blurX, int blurY, float strength, int type, Color highlightColor, Color shadowColor, float angle, float distance, boolean knockout, int iterations) { + return new SerializableImage(gradientBevel(src.getBufferedImage(), new Color[]{ + shadowColor, + new Color(shadowColor.getRed(), shadowColor.getGreen(), shadowColor.getBlue(), 0), + new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 0), + highlightColor + }, new float[]{0, 127f / 255f, 128f / 255f, 1}, blurX, blurY, strength, type, angle, distance, knockout, iterations)); + } + + public static SerializableImage gradientBevel(SerializableImage src, Color[] colors, float[] ratios, int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) { + return new SerializableImage(gradientBevel(src.getBufferedImage(), colors, ratios, blurX, blurY, strength, type, angle, distance, knockout, iterations)); + } + + private static BufferedImage gradientBevel(BufferedImage src, Color[] colors, float[] ratios, int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) { + int width = src.getWidth(); + int height = src.getHeight(); + BufferedImage retImg = new BufferedImage(width, height, src.getType()); + int srcPixels[] = getRGB(src, 0, 0, width, height); + + int revPixels[] = new int[srcPixels.length]; + for (int i = 0; i < srcPixels.length; i++) { + revPixels[i] = (srcPixels[i] & 0xffffff) + ((255 - ((srcPixels[i] >> 24) & 0xff)) << 24); + } + + BufferedImage gradient = new BufferedImage(512, 1, src.getType()); + Graphics2D gg = gradient.createGraphics(); + + Point p1 = new Point(0, 0); + Point p2 = new Point(511, 0); + gg.setPaint(new LinearGradientPaint(p1, p2, ratios, colors)); + gg.fill(new Rectangle(512, 1)); + int[] gradientPixels = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight()); + + BufferedImage shadowInner = null; + BufferedImage hilightInner = null; + if (type != OUTER) { + BufferedImage hilightIm = dropShadow(src, 0, 0, angle, distance, Color.red, true, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src + BufferedImage shadowIm = dropShadow(src, 0, 0, angle + 180, distance, Color.blue, true, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src); + BufferedImage h2 = new BufferedImage(width, height, src.getType()); + BufferedImage s2 = new BufferedImage(width, height, src.getType()); + Graphics2D hc = h2.createGraphics(); + Graphics2D sc = s2.createGraphics(); + hc.drawImage(hilightIm, 0, 0, null); + hc.setComposite(AlphaComposite.DstOut); + hc.drawImage(shadowIm, 0, 0, null); + + sc.drawImage(shadowIm, 0, 0, null); + sc.setComposite(AlphaComposite.DstOut); + sc.drawImage(hilightIm, 0, 0, null); + shadowInner = s2; + hilightInner = h2; + } + BufferedImage shadowOuter = null; + BufferedImage hilightOuter = null; + if (type != INNER) { + BufferedImage hilightIm = dropShadow(src, 0, 0, angle + 180, distance, Color.red, false, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src + BufferedImage shadowIm = dropShadow(src, 0, 0, angle, distance, Color.blue, false, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src); + BufferedImage h2 = new BufferedImage(width, height, src.getType()); + BufferedImage s2 = new BufferedImage(width, height, src.getType()); + Graphics2D hc = h2.createGraphics(); + Graphics2D sc = s2.createGraphics(); + hc.drawImage(hilightIm, 0, 0, null); + hc.setComposite(AlphaComposite.DstOut); + hc.drawImage(shadowIm, 0, 0, null); + + sc.drawImage(shadowIm, 0, 0, null); + sc.setComposite(AlphaComposite.DstOut); + sc.drawImage(hilightIm, 0, 0, null); + shadowOuter = s2; + hilightOuter = h2; + } + + BufferedImage hilightIm = null; + BufferedImage shadowIm = null; + switch (type) { + case OUTER: + hilightIm = hilightOuter; + shadowIm = shadowOuter; + break; + case INNER: + hilightIm = hilightInner; + shadowIm = shadowInner; + break; + case FULL: + hilightIm = hilightInner; + shadowIm = shadowInner; + Graphics2D hc = hilightIm.createGraphics(); + hc.setComposite(AlphaComposite.SrcOver); + hc.drawImage(hilightOuter, 0, 0, null); + Graphics2D sc = shadowIm.createGraphics(); + sc.setComposite(AlphaComposite.SrcOver); + sc.drawImage(shadowOuter, 0, 0, null); + break; + } + + int mask[] = null; + if (type == INNER) { + mask = srcPixels; + } + if (type == OUTER) { + mask = revPixels; + } + + Graphics2D retc = retImg.createGraphics(); + retc.setColor(Color.black); + retc.fillRect(0, 0, width, height); + retc.setComposite(AlphaComposite.SrcOver); + retc.drawImage(shadowIm, 0, 0, null); + retc.drawImage(hilightIm, 0, 0, null); + + /*if(true) + return retImg; + */ + retImg = blur(retImg, blurX, blurY, iterations, mask); + int ret[] = getRGB(retImg, 0, 0, width, height); + + for (int i = 0; i < srcPixels.length; i++) { + int ah = (int) (new Color(ret[i]).getRed() * strength); + int as = (int) (new Color(ret[i]).getBlue() * strength); + int ra = cut(ah - as, -255, 255); + ret[i] = gradientPixels[255 + ra]; + } + setRGB(retImg, 0, 0, width, height, ret); + + if (!knockout) { + Graphics2D g = retImg.createGraphics(); + g.setComposite(AlphaComposite.DstOver); + g.drawImage(src, 0, 0, null); + } + return retImg; + } + + public static SerializableImage glow(SerializableImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) { + return new SerializableImage(dropShadow(src.getBufferedImage(), blurX, blurY, 45, 0, color, inner, iterations, strength, knockout)); + } + + public static SerializableImage dropShadow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) { + return new SerializableImage(dropShadow(src.getBufferedImage(), blurX, blurY, angle, distance, color, inner, iterations, strength, knockout)); + } + + private static int cut(int val, int min, int max) { + if (val > max) { + val = max; + } + if (val < min) { + val = min; + } + return val; + } + + private static BufferedImage dropShadow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) { + int width = src.getWidth(); + int height = src.getHeight(); + int[] srcPixels = getRGB(src, 0, 0, width, height); + int shadow[] = new int[srcPixels.length]; + for (int i = 0; i < srcPixels.length; i++) { + int alpha = (srcPixels[i] >> 24) & 0xff; + if (inner) { + alpha = 255 - alpha; + } + shadow[i] = new Color(color.getRed(), color.getGreen(), color.getBlue(), cut(color.getAlpha() * alpha * strength)).getRGB(); + } + Color colorFirst = Color.BLACK; + Color colorAlpha = new Color(0, 0, 0, 0); + double angleRad = angle / 180 * Math.PI; + double moveX = (distance * Math.cos(angleRad)); + double moveY = (distance * Math.sin(angleRad)); + shadow = moveRGB(width, height, shadow, moveX, moveY, inner ? colorFirst : colorAlpha); + + BufferedImage retCanvas = new BufferedImage(width, height, src.getType()); + setRGB(retCanvas, 0, 0, width, height, shadow); + if (blurX > 0 || blurY > 0) { + retCanvas = blur(retCanvas, blurX, blurY, iterations, null); + } + shadow = getRGB(retCanvas, 0, 0, width, height); + + for (int i = 0; i < shadow.length; i++) { + int mask = (srcPixels[i] >> 24) & 0xff; + if (!inner) { + mask = 255 - mask; + } + shadow[i] = shadow[i] & 0xffffff + ((mask * ((shadow[i] >> 24) & 0xff) / 255) << 24); + } + setRGB(retCanvas, 0, 0, width, height, shadow); + + if (!knockout) { + Graphics2D g = retCanvas.createGraphics(); + g.setComposite(AlphaComposite.DstOver); + g.drawImage(src, 0, 0, null); + } + + return retCanvas; + } + + public static SerializableImage gradientGlow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) { + return new SerializableImage(gradientGlow(src.getBufferedImage(), blurX, blurY, angle, distance, colors, ratios, type, iterations, strength, knockout)); + } + + private static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) { + + int width = src.getWidth(); + int height = src.getHeight(); + BufferedImage retCanvas = new BufferedImage(width, height, src.getType()); + Graphics2D retImg = retCanvas.createGraphics(); + + BufferedImage gradCanvas = new BufferedImage(256, 1, src.getType()); + + Graphics2D gg = gradCanvas.createGraphics(); + + Point p1 = new Point(0, 0); + Point p2 = new Point(255, 0); + gg.setPaint(new LinearGradientPaint(p1, p2, ratios, colors)); + gg.fill(new Rectangle(256, 1)); + int[] gradientPixels = getRGB(gradCanvas, 0, 0, gradCanvas.getWidth(), gradCanvas.getHeight()); + + double angleRad = angle / 180 * Math.PI; + double moveX = (distance * Math.cos(angleRad)); + double moveY = (distance * Math.sin(angleRad)); + int srcPixels[] = getRGB(src, 0, 0, width, height); + int revPixels[] = new int[srcPixels.length]; + for (int i = 0; i < srcPixels.length; i++) { + revPixels[i] = (srcPixels[i] & 0xffffff) + ((255 - ((srcPixels[i] >> 24) & 0xff)) << 24); + } + int shadow[] = new int[srcPixels.length]; + for (int i = 0; i < srcPixels.length; i++) { + shadow[i] = 0 + ((cut(strength * ((srcPixels[i] >> 24) & 0xff))) << 24); + } + Color colorAlpha = new Color(0, 0, 0, 0); + shadow = moveRGB(width, height, shadow, moveX, moveY, colorAlpha); + + setRGB(retCanvas, 0, 0, width, height, shadow); + + int mask[] = null; + if (type == INNER) { + mask = srcPixels; + } + if (type == OUTER) { + mask = revPixels; + } + + retCanvas = blur(retCanvas, blurX, blurY, iterations, mask); + shadow = getRGB(retCanvas, 0, 0, width, height); + + if (mask != null) { + for (int i = 0; i < mask.length; i++) { + int m = (mask[i] >> 24); + if (m == 0) { + shadow[i] = 0; + } + } + } + + for (int i = 0; i < shadow.length; i++) { + int a = (shadow[i] >> 24) & 0xff; + shadow[i] = gradientPixels[a]; + } + + setRGB(retCanvas, 0, 0, width, height, shadow); + + if (!knockout) { + retImg = retCanvas.createGraphics(); + retImg.setComposite(AlphaComposite.DstOver); + retImg.drawImage(src, 0, 0, null); + } + + return retCanvas; + } + + private static int[] getRGB(BufferedImage image, int x, int y, int width, int height) { + int type = image.getType(); + if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { + return (int[]) image.getRaster().getDataElements(x, y, width, height, null); + } + return image.getRGB(x, y, width, height, null, 0, width); + } + + private static void setRGB(BufferedImage image, int x, int y, int width, int height, int[] pixels) { + int type = image.getType(); + if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) { + image.getRaster().setDataElements(x, y, width, height, pixels); + } else { + image.setRGB(x, y, width, height, pixels, 0, width); + } + } + + private static int[] moveRGB(int width, int height, int[] rgb, double deltaX, double deltaY, Color fill) { + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + setRGB(img, 0, 0, width, height, rgb); + BufferedImage retImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) retImg.getGraphics(); + g.setPaint(fill); + g.fillRect(0, 0, width, height); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.setTransform(AffineTransform.getTranslateInstance(deltaX, deltaY)); + g.setComposite(AlphaComposite.Src); + g.drawImage(img, 0, 0, null); + return getRGB(retImg, 0, 0, width, height); + } + + public static SerializableImage convolution(SerializableImage src, float[] matrix, int w, int h) { + BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + BufferedImageOp op = new ConvolveOp(new Kernel(w, h, matrix), ConvolveOp.EDGE_ZERO_FILL, new RenderingHints(null)); + op.filter(src.getBufferedImage(), dst); + return new SerializableImage(dst); + } + + public static SerializableImage colorMatrix(SerializableImage src, float[][] matrix) { + BandCombineOp changeColors = new BandCombineOp(matrix, new RenderingHints(null)); + Raster sourceRaster = src.getRaster(); + WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster(); + changeColors.filter(sourceRaster, displayRaster); + return new SerializableImage(src.getColorModel(), displayRaster, true, null); + } + + private static int cut(double val) { + int i = (int) val; + if (i < 0) { + i = 0; + } + if (i > 255) { + i = 255; + } + return i; + } + + public static Color colorEffect(Color color, + int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm, + int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) { + int rgb = color.getRGB(); + int a = (rgb >> 24) & 0xff; + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = (rgb) & 0xff; + r = Math.max(0, Math.min(((r * redMultTerm) / 255) + redAddTerm, 255)); + g = Math.max(0, Math.min(((g * greenMultTerm) / 255) + greenAddTerm, 255)); + b = Math.max(0, Math.min(((b * blueMultTerm) / 255) + blueAddTerm, 255)); + a = Math.max(0, Math.min(((a * alphaMultTerm) / 255) + alphaAddTerm, 255)); + return new Color(r, g, b, a); + } + + public static SerializableImage colorEffect(SerializableImage src, + int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm, + int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) { + BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + int rgb[] = getRGB(src.getBufferedImage(), 0, 0, src.getWidth(), src.getHeight()); + for (int i = 0; i < rgb.length; i++) { + int a = (rgb[i] >> 24) & 0xff; + int r = (rgb[i] >> 16) & 0xff; + int g = (rgb[i] >> 8) & 0xff; + int b = (rgb[i]) & 0xff; + r = Math.max(0, Math.min(((r * redMultTerm) / 256) + redAddTerm, 255)); + g = Math.max(0, Math.min(((g * greenMultTerm) / 256) + greenAddTerm, 255)); + b = Math.max(0, Math.min(((b * blueMultTerm) / 256) + blueAddTerm, 255)); + a = Math.max(0, Math.min(((a * alphaMultTerm) / 256) + alphaAddTerm, 255)); + rgb[i] = (a << 24) | (r << 16) | (g << 8) | (b); + } + setRGB(dst, 0, 0, src.getWidth(), src.getHeight(), rgb); + return new SerializableImage(dst); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java b/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java index 07609e3c0..5612b789c 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java +++ b/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java @@ -1,89 +1,89 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; - -/** - * Glow filter - * - * @author JPEXS - */ -public class GLOWFILTER extends FILTER { - - /** - * Color of the shadow - */ - public RGBA glowColor; - /** - * Horizontal blur amount - */ - @SWFType(BasicType.FIXED) - public double blurX; - /** - * Vertical blur amount - */ - @SWFType(BasicType.FIXED) - public double blurY; - /** - * Strength of the glow - */ - @SWFType(BasicType.FIXED8) - public float strength; - /** - * Inner glow mode - */ - public boolean innerGlow; - /** - * Knockout mode - */ - public boolean knockout; - /** - * Composite source - */ - public boolean compositeSource; - /** - * Number of blur passes - */ - @SWFType(value = BasicType.UB, count = 5) - public int passes; - - /** - * Constructor - */ - public GLOWFILTER() { - super(2); - } - - @Override - public SerializableImage apply(SerializableImage src) { - return Filtering.glow(src, (int) blurX, (int) blurY, strength, glowColor.toColor(), innerGlow, knockout, passes); - } - - @Override - public double getDeltaX() { - return blurX; - } - - @Override - public double getDeltaY() { - return blurY; - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; + +/** + * Glow filter + * + * @author JPEXS + */ +public class GLOWFILTER extends FILTER { + + /** + * Color of the shadow + */ + public RGBA glowColor; + /** + * Horizontal blur amount + */ + @SWFType(BasicType.FIXED) + public double blurX; + /** + * Vertical blur amount + */ + @SWFType(BasicType.FIXED) + public double blurY; + /** + * Strength of the glow + */ + @SWFType(BasicType.FIXED8) + public float strength; + /** + * Inner glow mode + */ + public boolean innerGlow; + /** + * Knockout mode + */ + public boolean knockout; + /** + * Composite source + */ + public boolean compositeSource; + /** + * Number of blur passes + */ + @SWFType(value = BasicType.UB, count = 5) + public int passes; + + /** + * Constructor + */ + public GLOWFILTER() { + super(2); + } + + @Override + public SerializableImage apply(SerializableImage src) { + return Filtering.glow(src, (int) blurX, (int) blurY, strength, glowColor.toColor(), innerGlow, knockout, passes); + } + + @Override + public double getDeltaX() { + return blurX; + } + + @Override + public double getDeltaY() { + return blurY; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java b/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java index cc278b960..1e93bfb98 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java +++ b/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java @@ -1,130 +1,130 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.util.ArrayList; -import java.util.List; - -/** - * Bevel filter with gradient instead of single color - * - * @author JPEXS - */ -public class GRADIENTBEVELFILTER extends FILTER { - - /** - * Gradient colors - */ - @SWFType(countField = "numColors") - public RGBA[] gradientColors = new RGBA[0]; - /** - * Gradient ratios - */ - @SWFType(value = BasicType.UI8, countField = "numColors") - public int[] gradientRatio = new int[0]; - /** - * Horizontal blur amount - */ - @SWFType(BasicType.FIXED) - public double blurX; - /** - * Vertical blur amount - */ - @SWFType(BasicType.FIXED) - public double blurY; - /** - * Radian angle of the gradient bevel - */ - @SWFType(BasicType.FIXED) - public double angle; - /** - * Distance of the gradient bevel - */ - @SWFType(BasicType.FIXED) - public double distance; - /** - * Strength of the gradient bevel - */ - @SWFType(BasicType.FIXED8) - public float strength; - /** - * Inner bevel mode - */ - public boolean innerShadow; - /** - * Knockout mode - */ - public boolean knockout; - /** - * Composite source - */ - public boolean compositeSource; - /** - * OnTop mode - */ - public boolean onTop; - /** - * Number of blur passes - */ - @SWFType(value = BasicType.UB, count = 4) - public int passes; - - public GRADIENTBEVELFILTER() { - super(7); - } - - @Override - public SerializableImage apply(SerializableImage src) { - List colors = new ArrayList<>(); - List ratios = new ArrayList<>(); - for (int i = 0; i < gradientColors.length; i++) { - if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) { - continue; - } - colors.add(gradientColors[i].toColor()); - ratios.add(gradientRatio[i] / 255f); - } - float[] ratiosAr = new float[ratios.size()]; - for (int i = 0; i < ratios.size(); i++) { - ratiosAr[i] = ratios.get(i); - } - Color[] colorsArr = colors.toArray(new Color[colors.size()]); - int type = Filtering.INNER; - if (onTop && !innerShadow) { - type = Filtering.FULL; - } else if (!innerShadow) { - type = Filtering.OUTER; - } - - return Filtering.gradientBevel(src, colorsArr, ratiosAr, (int) blurX, (int) blurY, strength, type, (int) (angle * 180 / Math.PI), (float) distance, knockout, passes); - } - - @Override - public double getDeltaX() { - return blurX + (distance * Math.cos(angle)); - } - - @Override - public double getDeltaY() { - return blurY +(distance * Math.sin(angle)); - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +/** + * Bevel filter with gradient instead of single color + * + * @author JPEXS + */ +public class GRADIENTBEVELFILTER extends FILTER { + + /** + * Gradient colors + */ + @SWFType(countField = "numColors") + public RGBA[] gradientColors = new RGBA[0]; + /** + * Gradient ratios + */ + @SWFType(value = BasicType.UI8, countField = "numColors") + public int[] gradientRatio = new int[0]; + /** + * Horizontal blur amount + */ + @SWFType(BasicType.FIXED) + public double blurX; + /** + * Vertical blur amount + */ + @SWFType(BasicType.FIXED) + public double blurY; + /** + * Radian angle of the gradient bevel + */ + @SWFType(BasicType.FIXED) + public double angle; + /** + * Distance of the gradient bevel + */ + @SWFType(BasicType.FIXED) + public double distance; + /** + * Strength of the gradient bevel + */ + @SWFType(BasicType.FIXED8) + public float strength; + /** + * Inner bevel mode + */ + public boolean innerShadow; + /** + * Knockout mode + */ + public boolean knockout; + /** + * Composite source + */ + public boolean compositeSource; + /** + * OnTop mode + */ + public boolean onTop; + /** + * Number of blur passes + */ + @SWFType(value = BasicType.UB, count = 4) + public int passes; + + public GRADIENTBEVELFILTER() { + super(7); + } + + @Override + public SerializableImage apply(SerializableImage src) { + List colors = new ArrayList<>(); + List ratios = new ArrayList<>(); + for (int i = 0; i < gradientColors.length; i++) { + if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) { + continue; + } + colors.add(gradientColors[i].toColor()); + ratios.add(gradientRatio[i] / 255f); + } + float[] ratiosAr = new float[ratios.size()]; + for (int i = 0; i < ratios.size(); i++) { + ratiosAr[i] = ratios.get(i); + } + Color[] colorsArr = colors.toArray(new Color[colors.size()]); + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + + return Filtering.gradientBevel(src, colorsArr, ratiosAr, (int) blurX, (int) blurY, strength, type, (int) (angle * 180 / Math.PI), (float) distance, knockout, passes); + } + + @Override + public double getDeltaX() { + return blurX + (distance * Math.cos(angle)); + } + + @Override + public double getDeltaY() { + return blurY + (distance * Math.sin(angle)); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java b/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java index a9c3e3a2a..b3346a694 100644 --- a/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java +++ b/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java @@ -1,132 +1,132 @@ -/* - * Copyright (C) 2010-2014 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.types.filters; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.util.ArrayList; -import java.util.List; - -/** - * Glow filter with gradient instead of single color - * - * @author JPEXS - */ -public class GRADIENTGLOWFILTER extends FILTER { - - /** - * Gradient colors - */ - @SWFType(countField = "numColors") - public RGBA[] gradientColors = new RGBA[0]; - /** - * Gradient ratios - */ - @SWFType(value = BasicType.UI8, countField = "numColors") - public int[] gradientRatio; - /** - * Horizontal blur amount - */ - @SWFType(BasicType.FIXED) - public double blurX; - /** - * Vertical blur amount - */ - @SWFType(BasicType.FIXED) - public double blurY; - /** - * Radian angle of the gradient glow - */ - @SWFType(BasicType.FIXED) - public double angle; - /** - * Distance of the gradient glow - */ - @SWFType(BasicType.FIXED) - public double distance; - /** - * Strength of the gradient glow - */ - @SWFType(BasicType.FIXED8) - public float strength; - /** - * Inner glow mode - */ - public boolean innerShadow; - /** - * Knockout mode - */ - public boolean knockout; - /** - * Composite source - */ - public boolean compositeSource; - /** - * OnTop mode - */ - public boolean onTop; - /** - * Number of blur passes - */ - @SWFType(value = BasicType.UB, count = 4) - public int passes; - - /** - * Constructor - */ - public GRADIENTGLOWFILTER() { - super(4); - } - - @Override - public SerializableImage apply(SerializableImage src) { - List colors = new ArrayList<>(); - List ratios = new ArrayList<>(); - for (int i = 0; i < gradientColors.length; i++) { - if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) { - continue; - } - colors.add(gradientColors[i].toColor()); - ratios.add(gradientRatio[i] / 255f); - } - int type = Filtering.INNER; - if (onTop && !innerShadow) { - type = Filtering.FULL; - } else if (!innerShadow) { - type = Filtering.OUTER; - } - - float[] ratiosAr = new float[ratios.size()]; - for (int i = 0; i < ratios.size(); i++) { - ratiosAr[i] = ratios.get(i); - } - return Filtering.gradientGlow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout); - } - - @Override - public double getDeltaX() { - return blurX + (distance * Math.cos(angle)); - } - - @Override - public double getDeltaY() { - return blurY +(distance * Math.sin(angle)); - } -} +/* + * Copyright (C) 2010-2014 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.types.filters; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +/** + * Glow filter with gradient instead of single color + * + * @author JPEXS + */ +public class GRADIENTGLOWFILTER extends FILTER { + + /** + * Gradient colors + */ + @SWFType(countField = "numColors") + public RGBA[] gradientColors = new RGBA[0]; + /** + * Gradient ratios + */ + @SWFType(value = BasicType.UI8, countField = "numColors") + public int[] gradientRatio; + /** + * Horizontal blur amount + */ + @SWFType(BasicType.FIXED) + public double blurX; + /** + * Vertical blur amount + */ + @SWFType(BasicType.FIXED) + public double blurY; + /** + * Radian angle of the gradient glow + */ + @SWFType(BasicType.FIXED) + public double angle; + /** + * Distance of the gradient glow + */ + @SWFType(BasicType.FIXED) + public double distance; + /** + * Strength of the gradient glow + */ + @SWFType(BasicType.FIXED8) + public float strength; + /** + * Inner glow mode + */ + public boolean innerShadow; + /** + * Knockout mode + */ + public boolean knockout; + /** + * Composite source + */ + public boolean compositeSource; + /** + * OnTop mode + */ + public boolean onTop; + /** + * Number of blur passes + */ + @SWFType(value = BasicType.UB, count = 4) + public int passes; + + /** + * Constructor + */ + public GRADIENTGLOWFILTER() { + super(4); + } + + @Override + public SerializableImage apply(SerializableImage src) { + List colors = new ArrayList<>(); + List ratios = new ArrayList<>(); + for (int i = 0; i < gradientColors.length; i++) { + if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) { + continue; + } + colors.add(gradientColors[i].toColor()); + ratios.add(gradientRatio[i] / 255f); + } + int type = Filtering.INNER; + if (onTop && !innerShadow) { + type = Filtering.FULL; + } else if (!innerShadow) { + type = Filtering.OUTER; + } + + float[] ratiosAr = new float[ratios.size()]; + for (int i = 0; i < ratios.size(); i++) { + ratiosAr[i] = ratios.get(i); + } + return Filtering.gradientGlow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout); + } + + @Override + public double getDeltaX() { + return blurX + (distance * Math.cos(angle)); + } + + @Override + public double getDeltaY() { + return blurY + (distance * Math.sin(angle)); + } +} diff --git a/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java index b439ad3a5..e9bbc1f49 100644 --- a/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java +++ b/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java @@ -1,463 +1,465 @@ -/* - * Copyright (C) 2010-2014 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.types.shaperecords; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.awt.Font; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.font.GlyphVector; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import javax.swing.JPanel; - -/** - * - * @author JPEXS - */ -public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Serializable { - - public static final int MAX_CHARACTERS_IN_FONT_PREVIEW = 400; - private static final boolean DRAW_BOUNDING_BOX = false; - - public abstract void calculateBits(); - - @Override - public Set getNeededCharacters() { - HashSet ret = new HashSet<>(); - return ret; - } - - public abstract int changeX(int x); - - public abstract int changeY(int y); - - public abstract void flip(); - - public static RECT getBounds(List records) { - int x = 0; - int y = 0; - int max_x = 0; - int max_y = 0; - int min_x = Integer.MAX_VALUE; - int min_y = Integer.MAX_VALUE; - boolean started = false; - for (SHAPERECORD r : records) { - x = r.changeX(x); - y = r.changeY(y); - if (x > max_x) { - max_x = x; - } - if (y > max_y) { - max_y = y; - } - if (r.isMove()) { - started = true; - } - if (started) { - if (y < min_y) { - min_y = y; - } - if (x < min_x) { - min_x = x; - } - } - if (r instanceof CurvedEdgeRecord) { - CurvedEdgeRecord curverEdge = (CurvedEdgeRecord) r; - int x2 = x + curverEdge.controlDeltaX; - int y2 = y + curverEdge.controlDeltaY; - if (x2 > max_x) { - max_x = x2; - } - if (y2 > max_y) { - max_y = y2; - } - if (started) { - if (y2 < min_y) { - min_y = y2; - } - if (x2 < min_x) { - min_x = x2; - } - } - } - } - return new RECT(min_x, max_x, min_y, max_y); - } - - public static CurvedEdgeRecord straightToCurve(StraightEdgeRecord ser) { - CurvedEdgeRecord ret = new CurvedEdgeRecord(); - ret.controlDeltaX = ser.deltaX / 2; - ret.controlDeltaY = ser.deltaY / 2; - ret.anchorDeltaX = ser.deltaX - ret.controlDeltaX; - ret.anchorDeltaY = ser.deltaY - ret.controlDeltaY; - return ret; - } - - public static void shapeListToImage(SWF swf, List shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) { - if (shapes.isEmpty()) { - return; - } - - int prevWidth = image.getWidth(); - int prevHeight = image.getHeight(); - - int maxw = 0; - int maxh = 0; - int minXMin = 0; - int minYMin = 0; - for (SHAPE s : shapes) { - RECT r = SHAPERECORD.getBounds(s.shapeRecords); - if (r.Xmax < r.Xmin || r.Ymax < r.Ymin) { - continue; - } - if (r.getWidth() > maxw) { - maxw = r.getWidth(); - } - if (r.getHeight() > maxh) { - maxh = r.getHeight(); - } - if (r.Xmin < minXMin) { - minXMin = r.Xmin; - } - if (r.Ymin < minYMin) { - minYMin = r.Ymin; - } - } - - int shapeCount = Math.min(MAX_CHARACTERS_IN_FONT_PREVIEW, shapes.size()); - int frameCount = (shapes.size() - 1) / MAX_CHARACTERS_IN_FONT_PREVIEW + 1; - if (frameCount < 1) { - frameCount = 1; - } - if (frame >= frameCount) { - frame = frameCount - 1; - } - int cols = (int) Math.ceil(Math.sqrt(shapeCount)); - int pos = frame * MAX_CHARACTERS_IN_FONT_PREVIEW; - int w2 = (int) (prevWidth * SWF.unitDivisor / cols); - int h2 = (int) (prevHeight * SWF.unitDivisor / cols); - - int mh = maxh * w2 / maxw; - int mw; - if (mh > h2) { - mw = maxw * h2 / maxh; - mh = h2; - } else { - mw = w2; - } - - float ratio = (float) mw / (float) maxw; - - loopy: - for (int y = 0; y < cols; y++) { - for (int x = 0; x < cols; x++) { - if (pos >= shapes.size()) { - break loopy; - } - - // shapeNum: 1 - SHAPE shape = shapes.get(pos); - List records = shape.shapeRecords; - RECT bounds = SHAPERECORD.getBounds(records); - - int w1 = bounds.getWidth(); - int h1 = bounds.getHeight(); - - double w = ratio * w1; - double h = ratio * h1; - double px = x * w2 + w2 / 2 - w / 2 - minXMin * ratio; - double py = y * h2 - minYMin * ratio; - - Matrix transformation = new Matrix(); - transformation.translate(px, py); - transformation = transformation.concatenate(Matrix.getScaleInstance(ratio)); - BitmapExporter.export(swf, shape, color, image, transformation, colorTransform); - - // draw bounding boxes - if (DRAW_BOUNDING_BOX) { - RGB borderColor = new RGBA(Color.black); - RGB fillColor = new RGBA(new Color(255, 255, 255, 0)); - transformation = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(transformation); - TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), transformation, colorTransform); - } - pos++; - } - } - } - - public abstract boolean isMove(); - - public static List systemFontCharactersToSHAPES(String fontName, int fontStyle, int fontSize, String characters) { - List ret = new ArrayList<>(); - for (int i = 0; i < characters.length(); i++) { - ret.add(systemFontCharacterToSHAPE(fontName, fontStyle, fontSize, characters.charAt(i))); - } - return ret; - } - - public static SHAPE systemFontCharacterToSHAPE(final String fontName, final int fontStyle, int fontSize, char character) { - int multiplier = 1; - if (fontSize > 1024) { - multiplier = fontSize / 1024; - fontSize = 1024; - } - List retList = new ArrayList<>(); - Font f = new Font(FontTag.getFontNameWithFallback(fontName), fontStyle, fontSize); - GlyphVector v = f.createGlyphVector((new JPanel()).getFontMetrics(f).getFontRenderContext(), "" + character); - Shape shp = v.getOutline(); - double[] points = new double[6]; - int lastX = 0; - int lastY = 0; - int startX = 0; - int startY = 0; - for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) { - int type = it.currentSegment(points); - switch (type) { - case PathIterator.SEG_MOVETO: - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateMoveTo = true; - scr.moveDeltaX = multiplier * (int) Math.round(points[0]); - scr.moveDeltaY = multiplier * (int) Math.round(points[1]); - scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); - retList.add(scr); - lastX = (int) Math.round(points[0]); - lastY = (int) Math.round(points[1]); - startX = lastX; - startY = lastY; - break; - case PathIterator.SEG_LINETO: - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = multiplier * (((int) Math.round(points[0])) - lastX); - ser.deltaY = multiplier * (((int) Math.round(points[1])) - lastY); - - ser.generalLineFlag = ser.deltaX != 0 && ser.deltaY != 0; - if (ser.deltaX == 0) { - ser.vertLineFlag = true; - } - ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2; - if (ser.numBits < 0) { - ser.numBits = 0; - } - retList.add(ser); - lastX = (int) Math.round(points[0]); - lastY = (int) Math.round(points[1]); - break; - case PathIterator.SEG_CUBICTO: - double[] cubicCoords = new double[]{ - lastX, lastY, - Math.round(points[0]), Math.round(points[1]), - Math.round(points[2]), Math.round(points[3]), - Math.round(points[4]), Math.round(points[5]) - }; - double[][] quadCoords = approximateCubic(cubicCoords); - for (int i = 0; i < quadCoords.length; i++) { - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = multiplier * (((int) Math.round(quadCoords[i][0])) - lastX); - cer.controlDeltaY = multiplier * (((int) Math.round(quadCoords[i][1])) - lastY); - cer.anchorDeltaX = multiplier * (((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0]))); - cer.anchorDeltaY = multiplier * (((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1]))); - cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; - if (cer.numBits < 0) { - cer.numBits = 0; - } - lastX = (int) Math.round(quadCoords[i][2]); - lastY = (int) Math.round(quadCoords[i][3]); - retList.add(cer); - } - break; - case PathIterator.SEG_QUADTO: - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = multiplier * (((int) Math.round(points[0])) - lastX); - cer.controlDeltaY = multiplier * (((int) Math.round(points[1])) - lastY); - cer.anchorDeltaX = multiplier * (((int) Math.round(points[2])) - (int) Math.round(points[0])); - cer.anchorDeltaY = multiplier * (((int) Math.round(points[3])) - (int) Math.round(points[1])); - cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; - if (cer.numBits < 0) { - cer.numBits = 0; - } - retList.add(cer); - lastX = (int) Math.round(points[2]); - lastY = (int) Math.round(points[3]); - break; - case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO - if ((startX == lastX) && (startY == lastY)) { - break; - } - StraightEdgeRecord closeSer = new StraightEdgeRecord(); - closeSer.generalLineFlag = true; - closeSer.deltaX = multiplier * ((int) Math.round((startX - lastX))); - closeSer.deltaY = multiplier * ((int) Math.round((startY - lastY))); - closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2; - if (closeSer.numBits < 0) { - closeSer.numBits = 0; - } - retList.add(closeSer); - lastX = startX; - lastY = startY; - break; - } - } - SHAPE shape = new SHAPE(); - StyleChangeRecord init; - if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) { - init = (StyleChangeRecord) retList.get(0); - } else { - init = new StyleChangeRecord(); - retList.add(0, init); - } - - retList.add(new EndShapeRecord()); - init.stateFillStyle0 = true; - init.fillStyle0 = 1; - shape.shapeRecords = retList; - shape.numFillBits = 1; - shape.numLineBits = 0; - return shape; - } - - // Taken from org.apache.fop.afp.util - public static double[][] approximateCubic(double[] cubicControlPointCoords) { - if (cubicControlPointCoords.length < 8) { - throw new IllegalArgumentException("Must have at least 8 coordinates"); - } - - //extract point objects from source array - Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]); - Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]); - Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]); - Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]); - - //calculates the useful base points - Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0); - Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0); - - //get 1/16 of the [P3, P0] segment - double dx = (p3.getX() - p0.getX()) / 16.0; - double dy = (p3.getY() - p0.getY()) / 16.0; - - //calculates control point 1 - Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0); - - //calculates control point 2 - Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0); - pc2 = movePoint(pc2, -dx, -dy); - - //calculates control point 3 - Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0); - pc3 = movePoint(pc3, dx, dy); - - //calculates control point 4 - Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0); - - //calculates the 3 anchor points - Point2D pa1 = getMidPoint(pc1, pc2); - Point2D pa2 = getMidPoint(pa, pb); - Point2D pa3 = getMidPoint(pc3, pc4); - - //return the points for the four quadratic curves - return new double[][]{ - {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()}, - {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()}, - {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()}, - {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}}; - } - - private static Point2D.Double movePoint(Point2D point, double dx, double dy) { - return new Point2D.Double(point.getX() + dx, point.getY() + dy); - } - - private static Point2D getMidPoint(Point2D p0, Point2D p1) { - return getPointOnSegment(p0, p1, 0.5); - } - - private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) { - double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio); - double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio); - return new Point2D.Double(x, y); - } - - public static SHAPE resizeSHAPE(SHAPE shp, double multiplier) { - SHAPE ret = new SHAPE(); - ret.numFillBits = shp.numFillBits; - ret.numLineBits = shp.numLineBits; - List recs = new ArrayList<>(); - for (SHAPERECORD r : shp.shapeRecords) { - SHAPERECORD c = Helper.deepCopy(r); - if (c instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) c; - scr.moveDeltaX = (int) (multiplier * scr.moveDeltaX); - scr.moveDeltaY = (int) (multiplier * scr.moveDeltaY); - scr.calculateBits(); - } - if (c instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer = (CurvedEdgeRecord) c; - cer.controlDeltaX = (int) (multiplier * cer.controlDeltaX); - cer.controlDeltaY = (int) (multiplier * cer.controlDeltaY); - cer.anchorDeltaX = (int) (multiplier * cer.anchorDeltaX); - cer.anchorDeltaY = (int) (multiplier * cer.anchorDeltaY); - cer.calculateBits(); - } - if (c instanceof StraightEdgeRecord) { - StraightEdgeRecord ser = (StraightEdgeRecord) c; - ser.deltaX = (int) (multiplier * ser.deltaX); - ser.deltaY = (int) (multiplier * ser.deltaY); - ser.calculateBits(); - } - recs.add(c); - } - ret.shapeRecords = recs; - return ret; - } - - public static Shape moveShapeToStart(Shape s) { - Rectangle bds = s.getBounds(); - s = AffineTransform.getTranslateInstance(-bds.x, -bds.y).createTransformedShape(s); - return s; - } - - public static Shape twipToPixelShape(Shape s) { - Rectangle bds = s.getBounds(); - int dx = -bds.x - bds.width / 2; - int dy = -bds.y - bds.height / 2; - s = AffineTransform.getTranslateInstance(dx, dy).createTransformedShape(s); - s = AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor).createTransformedShape(s); - s = AffineTransform.getTranslateInstance(-dx / SWF.unitDivisor, -dy / SWF.unitDivisor).createTransformedShape(s); - return s; - } -} +/* + * Copyright (C) 2010-2014 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.types.shaperecords; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.NeedsCharacters; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.swing.JPanel; + +/** + * + * @author JPEXS + */ +public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Serializable { + + public static final int MAX_CHARACTERS_IN_FONT_PREVIEW = 400; + private static final boolean DRAW_BOUNDING_BOX = false; + + public abstract void calculateBits(); + + @Override + public void getNeededCharacters(Set needed) { + } + + @Override + public boolean removeCharacter(int characterId) { + return false; + } + + public abstract int changeX(int x); + + public abstract int changeY(int y); + + public abstract void flip(); + + public static RECT getBounds(List records) { + int x = 0; + int y = 0; + int max_x = 0; + int max_y = 0; + int min_x = Integer.MAX_VALUE; + int min_y = Integer.MAX_VALUE; + boolean started = false; + for (SHAPERECORD r : records) { + x = r.changeX(x); + y = r.changeY(y); + if (x > max_x) { + max_x = x; + } + if (y > max_y) { + max_y = y; + } + if (r.isMove()) { + started = true; + } + if (started) { + if (y < min_y) { + min_y = y; + } + if (x < min_x) { + min_x = x; + } + } + if (r instanceof CurvedEdgeRecord) { + CurvedEdgeRecord curverEdge = (CurvedEdgeRecord) r; + int x2 = x + curverEdge.controlDeltaX; + int y2 = y + curverEdge.controlDeltaY; + if (x2 > max_x) { + max_x = x2; + } + if (y2 > max_y) { + max_y = y2; + } + if (started) { + if (y2 < min_y) { + min_y = y2; + } + if (x2 < min_x) { + min_x = x2; + } + } + } + } + return new RECT(min_x, max_x, min_y, max_y); + } + + public static CurvedEdgeRecord straightToCurve(StraightEdgeRecord ser) { + CurvedEdgeRecord ret = new CurvedEdgeRecord(); + ret.controlDeltaX = ser.deltaX / 2; + ret.controlDeltaY = ser.deltaY / 2; + ret.anchorDeltaX = ser.deltaX - ret.controlDeltaX; + ret.anchorDeltaY = ser.deltaY - ret.controlDeltaY; + return ret; + } + + public static void shapeListToImage(SWF swf, List shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) { + if (shapes.isEmpty()) { + return; + } + + int prevWidth = image.getWidth(); + int prevHeight = image.getHeight(); + + int maxw = 0; + int maxh = 0; + int minXMin = 0; + int minYMin = 0; + for (SHAPE s : shapes) { + RECT r = SHAPERECORD.getBounds(s.shapeRecords); + if (r.Xmax < r.Xmin || r.Ymax < r.Ymin) { + continue; + } + if (r.getWidth() > maxw) { + maxw = r.getWidth(); + } + if (r.getHeight() > maxh) { + maxh = r.getHeight(); + } + if (r.Xmin < minXMin) { + minXMin = r.Xmin; + } + if (r.Ymin < minYMin) { + minYMin = r.Ymin; + } + } + + int shapeCount = Math.min(MAX_CHARACTERS_IN_FONT_PREVIEW, shapes.size()); + int frameCount = (shapes.size() - 1) / MAX_CHARACTERS_IN_FONT_PREVIEW + 1; + if (frameCount < 1) { + frameCount = 1; + } + if (frame >= frameCount) { + frame = frameCount - 1; + } + int cols = (int) Math.ceil(Math.sqrt(shapeCount)); + int pos = frame * MAX_CHARACTERS_IN_FONT_PREVIEW; + int w2 = (int) (prevWidth * SWF.unitDivisor / cols); + int h2 = (int) (prevHeight * SWF.unitDivisor / cols); + + int mh = maxh * w2 / maxw; + int mw; + if (mh > h2) { + mw = maxw * h2 / maxh; + mh = h2; + } else { + mw = w2; + } + + float ratio = (float) mw / (float) maxw; + + loopy: + for (int y = 0; y < cols; y++) { + for (int x = 0; x < cols; x++) { + if (pos >= shapes.size()) { + break loopy; + } + + // shapeNum: 1 + SHAPE shape = shapes.get(pos); + List records = shape.shapeRecords; + RECT bounds = SHAPERECORD.getBounds(records); + + int w1 = bounds.getWidth(); + int h1 = bounds.getHeight(); + + double w = ratio * w1; + double h = ratio * h1; + double px = x * w2 + w2 / 2 - w / 2 - minXMin * ratio; + double py = y * h2 - minYMin * ratio; + + Matrix transformation = new Matrix(); + transformation.translate(px, py); + transformation = transformation.concatenate(Matrix.getScaleInstance(ratio)); + BitmapExporter.export(swf, shape, color, image, transformation, colorTransform); + + // draw bounding boxes + if (DRAW_BOUNDING_BOX) { + RGB borderColor = new RGBA(Color.black); + RGB fillColor = new RGBA(new Color(255, 255, 255, 0)); + transformation = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(transformation); + TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), transformation, colorTransform); + } + pos++; + } + } + } + + public abstract boolean isMove(); + + public static List systemFontCharactersToSHAPES(String fontName, int fontStyle, int fontSize, String characters) { + List ret = new ArrayList<>(); + for (int i = 0; i < characters.length(); i++) { + ret.add(systemFontCharacterToSHAPE(fontName, fontStyle, fontSize, characters.charAt(i))); + } + return ret; + } + + public static SHAPE systemFontCharacterToSHAPE(final String fontName, final int fontStyle, int fontSize, char character) { + int multiplier = 1; + if (fontSize > 1024) { + multiplier = fontSize / 1024; + fontSize = 1024; + } + List retList = new ArrayList<>(); + Font f = new Font(FontTag.getFontNameWithFallback(fontName), fontStyle, fontSize); + GlyphVector v = f.createGlyphVector((new JPanel()).getFontMetrics(f).getFontRenderContext(), "" + character); + Shape shp = v.getOutline(); + double[] points = new double[6]; + int lastX = 0; + int lastY = 0; + int startX = 0; + int startY = 0; + for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) { + int type = it.currentSegment(points); + switch (type) { + case PathIterator.SEG_MOVETO: + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateMoveTo = true; + scr.moveDeltaX = multiplier * (int) Math.round(points[0]); + scr.moveDeltaY = multiplier * (int) Math.round(points[1]); + scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY); + retList.add(scr); + lastX = (int) Math.round(points[0]); + lastY = (int) Math.round(points[1]); + startX = lastX; + startY = lastY; + break; + case PathIterator.SEG_LINETO: + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.deltaX = multiplier * (((int) Math.round(points[0])) - lastX); + ser.deltaY = multiplier * (((int) Math.round(points[1])) - lastY); + + ser.generalLineFlag = ser.deltaX != 0 && ser.deltaY != 0; + if (ser.deltaX == 0) { + ser.vertLineFlag = true; + } + ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2; + if (ser.numBits < 0) { + ser.numBits = 0; + } + retList.add(ser); + lastX = (int) Math.round(points[0]); + lastY = (int) Math.round(points[1]); + break; + case PathIterator.SEG_CUBICTO: + double[] cubicCoords = new double[]{ + lastX, lastY, + Math.round(points[0]), Math.round(points[1]), + Math.round(points[2]), Math.round(points[3]), + Math.round(points[4]), Math.round(points[5]) + }; + double[][] quadCoords = approximateCubic(cubicCoords); + for (int i = 0; i < quadCoords.length; i++) { + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = multiplier * (((int) Math.round(quadCoords[i][0])) - lastX); + cer.controlDeltaY = multiplier * (((int) Math.round(quadCoords[i][1])) - lastY); + cer.anchorDeltaX = multiplier * (((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0]))); + cer.anchorDeltaY = multiplier * (((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1]))); + cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; + if (cer.numBits < 0) { + cer.numBits = 0; + } + lastX = (int) Math.round(quadCoords[i][2]); + lastY = (int) Math.round(quadCoords[i][3]); + retList.add(cer); + } + break; + case PathIterator.SEG_QUADTO: + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = multiplier * (((int) Math.round(points[0])) - lastX); + cer.controlDeltaY = multiplier * (((int) Math.round(points[1])) - lastY); + cer.anchorDeltaX = multiplier * (((int) Math.round(points[2])) - (int) Math.round(points[0])); + cer.anchorDeltaY = multiplier * (((int) Math.round(points[3])) - (int) Math.round(points[1])); + cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2; + if (cer.numBits < 0) { + cer.numBits = 0; + } + retList.add(cer); + lastX = (int) Math.round(points[2]); + lastY = (int) Math.round(points[3]); + break; + case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO + if ((startX == lastX) && (startY == lastY)) { + break; + } + StraightEdgeRecord closeSer = new StraightEdgeRecord(); + closeSer.generalLineFlag = true; + closeSer.deltaX = multiplier * ((int) Math.round((startX - lastX))); + closeSer.deltaY = multiplier * ((int) Math.round((startY - lastY))); + closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2; + if (closeSer.numBits < 0) { + closeSer.numBits = 0; + } + retList.add(closeSer); + lastX = startX; + lastY = startY; + break; + } + } + SHAPE shape = new SHAPE(); + StyleChangeRecord init; + if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) { + init = (StyleChangeRecord) retList.get(0); + } else { + init = new StyleChangeRecord(); + retList.add(0, init); + } + + retList.add(new EndShapeRecord()); + init.stateFillStyle0 = true; + init.fillStyle0 = 1; + shape.shapeRecords = retList; + shape.numFillBits = 1; + shape.numLineBits = 0; + return shape; + } + + // Taken from org.apache.fop.afp.util + public static double[][] approximateCubic(double[] cubicControlPointCoords) { + if (cubicControlPointCoords.length < 8) { + throw new IllegalArgumentException("Must have at least 8 coordinates"); + } + + //extract point objects from source array + Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]); + Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]); + Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]); + Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]); + + //calculates the useful base points + Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0); + Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0); + + //get 1/16 of the [P3, P0] segment + double dx = (p3.getX() - p0.getX()) / 16.0; + double dy = (p3.getY() - p0.getY()) / 16.0; + + //calculates control point 1 + Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0); + + //calculates control point 2 + Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0); + pc2 = movePoint(pc2, -dx, -dy); + + //calculates control point 3 + Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0); + pc3 = movePoint(pc3, dx, dy); + + //calculates control point 4 + Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0); + + //calculates the 3 anchor points + Point2D pa1 = getMidPoint(pc1, pc2); + Point2D pa2 = getMidPoint(pa, pb); + Point2D pa3 = getMidPoint(pc3, pc4); + + //return the points for the four quadratic curves + return new double[][]{ + {pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()}, + {pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()}, + {pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()}, + {pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}}; + } + + private static Point2D.Double movePoint(Point2D point, double dx, double dy) { + return new Point2D.Double(point.getX() + dx, point.getY() + dy); + } + + private static Point2D getMidPoint(Point2D p0, Point2D p1) { + return getPointOnSegment(p0, p1, 0.5); + } + + private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) { + double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio); + double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio); + return new Point2D.Double(x, y); + } + + public static SHAPE resizeSHAPE(SHAPE shp, double multiplier) { + SHAPE ret = new SHAPE(); + ret.numFillBits = shp.numFillBits; + ret.numLineBits = shp.numLineBits; + List recs = new ArrayList<>(); + for (SHAPERECORD r : shp.shapeRecords) { + SHAPERECORD c = Helper.deepCopy(r); + if (c instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) c; + scr.moveDeltaX = (int) (multiplier * scr.moveDeltaX); + scr.moveDeltaY = (int) (multiplier * scr.moveDeltaY); + scr.calculateBits(); + } + if (c instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) c; + cer.controlDeltaX = (int) (multiplier * cer.controlDeltaX); + cer.controlDeltaY = (int) (multiplier * cer.controlDeltaY); + cer.anchorDeltaX = (int) (multiplier * cer.anchorDeltaX); + cer.anchorDeltaY = (int) (multiplier * cer.anchorDeltaY); + cer.calculateBits(); + } + if (c instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) c; + ser.deltaX = (int) (multiplier * ser.deltaX); + ser.deltaY = (int) (multiplier * ser.deltaY); + ser.calculateBits(); + } + recs.add(c); + } + ret.shapeRecords = recs; + return ret; + } + + public static Shape moveShapeToStart(Shape s) { + Rectangle bds = s.getBounds(); + s = AffineTransform.getTranslateInstance(-bds.x, -bds.y).createTransformedShape(s); + return s; + } + + public static Shape twipToPixelShape(Shape s) { + Rectangle bds = s.getBounds(); + int dx = -bds.x - bds.width / 2; + int dy = -bds.y - bds.height / 2; + s = AffineTransform.getTranslateInstance(dx, dy).createTransformedShape(s); + s = AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor).createTransformedShape(s); + s = AffineTransform.getTranslateInstance(-dx / SWF.unitDivisor, -dy / SWF.unitDivisor).createTransformedShape(s); + return s; + } +} diff --git a/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java b/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java index eddd28c90..fac590918 100644 --- a/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java +++ b/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java @@ -1,138 +1,141 @@ -/* - * Copyright (C) 2010-2014 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.types.shaperecords; - -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.annotations.Calculated; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class StyleChangeRecord extends SHAPERECORD implements Cloneable { - - public boolean typeFlag = false; - public boolean stateNewStyles; - public boolean stateLineStyle; - public boolean stateFillStyle1; - public boolean stateFillStyle0; - public boolean stateMoveTo; - - @Calculated - @SWFType(value = BasicType.UB, count = 5) - @Conditional("stateMoveTo") - public int moveBits; - - @SWFType(value = BasicType.SB, countField = "moveBits") - @Conditional("stateMoveTo") - public int moveDeltaX; - - @SWFType(value = BasicType.SB, countField = "moveBits") - @Conditional("stateMoveTo") - public int moveDeltaY; - - @SWFType(value = BasicType.UB, countField = "fillBits") //last defined fillBits - @Conditional("stateFillStyle0") - public int fillStyle0; - - @SWFType(value = BasicType.UB, countField = "fillBits") //last defined fillBits - @Conditional("stateFillStyle1") - public int fillStyle1; - - @SWFType(value = BasicType.UB, countField = "lineBits") //last defined lineBits - @Conditional("stateLineStyle") - public int lineStyle; - - @Conditional("stateNewStyles") - public FILLSTYLEARRAY fillStyles; - - @Conditional("stateNewStyles") - public LINESTYLEARRAY lineStyles; - - @Calculated - @Conditional("stateNewStyles") - public int numFillBits; - - @Calculated - @Conditional("stateNewStyles") - public int numLineBits; - - @Override - public Set getNeededCharacters() { - Set ret = super.getNeededCharacters(); - if (stateNewStyles) { - ret.addAll(fillStyles.getNeededCharacters()); - } - return ret; - } - - @Override - public String toString() { - return "[StyleChangeRecord stateNewStyles=" + stateNewStyles + ", stateLineStyle=" + stateLineStyle + ",stateFillStyle1=" + stateFillStyle1 + "," - + " stateFillStyle0=" + stateFillStyle0 + ", stateMoveTo=" + stateMoveTo + ", moveBits=" + moveBits + ", moveDeltaX=" + moveDeltaX + ", moveDeltaY=" + moveDeltaY + "," - + " fillStyle0=" + fillStyle0 + ", fillStyle1=" + fillStyle1 + ", lineStyle=" + lineStyle + ", fillStyles=" + fillStyles + ", lineStyles=" + lineStyles + ", numFillBits=" + numFillBits + ", numLineBits=" + numLineBits + "]"; - } - - @Override - public int changeX(int x) { - if (stateMoveTo) { - return moveDeltaX; - } - return x; - } - - @Override - public int changeY(int y) { - if (stateMoveTo) { - return moveDeltaY; - } - return y; - } - - @Override - public void flip() { - } - - @Override - public boolean isMove() { - return stateMoveTo; - - } - - @Override - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException ex) { - Logger.getLogger(StyleChangeRecord.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - public void calculateBits() { - moveBits = SWFOutputStream.getNeededBitsS(moveDeltaX, moveDeltaY); - } -} +/* + * Copyright (C) 2010-2014 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.types.shaperecords; + +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.annotations.Calculated; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class StyleChangeRecord extends SHAPERECORD implements Cloneable { + + public boolean typeFlag = false; + public boolean stateNewStyles; + public boolean stateLineStyle; + public boolean stateFillStyle1; + public boolean stateFillStyle0; + public boolean stateMoveTo; + + @Calculated + @SWFType(value = BasicType.UB, count = 5) + @Conditional("stateMoveTo") + public int moveBits; + + @SWFType(value = BasicType.SB, countField = "moveBits") + @Conditional("stateMoveTo") + public int moveDeltaX; + + @SWFType(value = BasicType.SB, countField = "moveBits") + @Conditional("stateMoveTo") + public int moveDeltaY; + + @SWFType(value = BasicType.UB, countField = "fillBits") //last defined fillBits + @Conditional("stateFillStyle0") + public int fillStyle0; + + @SWFType(value = BasicType.UB, countField = "fillBits") //last defined fillBits + @Conditional("stateFillStyle1") + public int fillStyle1; + + @SWFType(value = BasicType.UB, countField = "lineBits") //last defined lineBits + @Conditional("stateLineStyle") + public int lineStyle; + + @Conditional("stateNewStyles") + public FILLSTYLEARRAY fillStyles; + + @Conditional("stateNewStyles") + public LINESTYLEARRAY lineStyles; + + @Calculated + @Conditional("stateNewStyles") + public int numFillBits; + + @Calculated + @Conditional("stateNewStyles") + public int numLineBits; + + @Override + public void getNeededCharacters(Set needed) { + if (stateNewStyles) { + fillStyles.getNeededCharacters(needed); + } + } + + @Override + public boolean removeCharacter(int characterId) { + return fillStyles.removeCharacter(characterId); + } + + @Override + public String toString() { + return "[StyleChangeRecord stateNewStyles=" + stateNewStyles + ", stateLineStyle=" + stateLineStyle + ",stateFillStyle1=" + stateFillStyle1 + "," + + " stateFillStyle0=" + stateFillStyle0 + ", stateMoveTo=" + stateMoveTo + ", moveBits=" + moveBits + ", moveDeltaX=" + moveDeltaX + ", moveDeltaY=" + moveDeltaY + "," + + " fillStyle0=" + fillStyle0 + ", fillStyle1=" + fillStyle1 + ", lineStyle=" + lineStyle + ", fillStyles=" + fillStyles + ", lineStyles=" + lineStyles + ", numFillBits=" + numFillBits + ", numLineBits=" + numLineBits + "]"; + } + + @Override + public int changeX(int x) { + if (stateMoveTo) { + return moveDeltaX; + } + return x; + } + + @Override + public int changeY(int y) { + if (stateMoveTo) { + return moveDeltaY; + } + return y; + } + + @Override + public void flip() { + } + + @Override + public boolean isMove() { + return stateMoveTo; + + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + Logger.getLogger(StyleChangeRecord.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + public void calculateBits() { + moveBits = SWFOutputStream.getNeededBitsS(moveDeltaX, moveDeltaY); + } +} diff --git a/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java b/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java index fdb2e40a5..69e616175 100644 --- a/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java +++ b/src/com/jpexs/decompiler/flash/xfl/XFLConverter.java @@ -1,3419 +1,3419 @@ -/* - * Copyright (C) 2010-2014 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.xfl; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.RetryTask; -import com.jpexs.decompiler.flash.RunnableIOEx; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.MovieExporter; -import com.jpexs.decompiler.flash.exporters.SoundExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFontNameTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.FrameLabelTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.StartSoundTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -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.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.font.CharacterRanges; -import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; -import com.jpexs.decompiler.flash.types.BUTTONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; -import com.jpexs.decompiler.flash.types.CLIPACTIONS; -import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.FOCALGRADIENT; -import com.jpexs.decompiler.flash.types.GRADIENT; -import com.jpexs.decompiler.flash.types.GRADRECORD; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLE2; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.BLURFILTER; -import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; -import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; -import com.jpexs.decompiler.flash.types.filters.FILTER; -import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; -import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.decompiler.flash.types.sound.MP3FRAME; -import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.Font; -import java.awt.Point; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; -import javax.imageio.ImageIO; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.helpers.XMLReaderFactory; - -/** - * - * @author JPEXS - */ -public class XFLConverter { - - public static final int KEY_MODE_NORMAL = 9728; - public static final int KEY_MODE_CLASSIC_TWEEN = 22017; - public static final int KEY_MODE_SHAPE_TWEEN = 17922; - public static final int KEY_MODE_MOTION_TWEEN = 8195; - public static final int KEY_MODE_SHAPE_LAYERS = 8192; - - private XFLConverter() { - } - - public static String convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y) { - String ret = ""; - if (record instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) record; - Point p = new Point(scr.moveDeltaX, scr.moveDeltaY); - //p = mat.apply(p); - if (scr.stateMoveTo) { - return "! " + p.x + " " + p.y; - } - return ""; - } - if (record instanceof StraightEdgeRecord) { - StraightEdgeRecord ser = (StraightEdgeRecord) record; - if (ser.generalLineFlag || ser.vertLineFlag) { - y += ser.deltaY; - } - if (ser.generalLineFlag || (!ser.vertLineFlag)) { - x += ser.deltaX; - } - Point p = new Point(x, y); - //p = mat.apply(p); - return "| " + p.x + " " + p.y; - } - if (record instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer = (CurvedEdgeRecord) record; - int controlX = cer.controlDeltaX + x; - int controlY = cer.controlDeltaY + y; - int anchorX = cer.anchorDeltaX + controlX; - int anchorY = cer.anchorDeltaY + controlY; - Point control = new Point(controlX, controlY); - //control = mat.apply(control); - Point anchor = new Point(anchorX, anchorY); - //anchor = mat.apply(anchor); - return "[ " + control.x + " " + control.y + " " + anchor.x + " " + anchor.y; - } - return ret; - } - - public static String convertShapeEdges(int startX, int startY, MATRIX mat, List records) { - String ret = ""; - int x = startX; - int y = startY; - ret += "!" + startX + " " + startY; - for (SHAPERECORD rec : records) { - ret += convertShapeEdge(mat, rec, x, y); - x = rec.changeX(x); - y = rec.changeY(y); - } - return ret; - } - - public static String convertLineStyle(LINESTYLE ls, int shapeNum) { - return "" - + "" - + "" - + "" - + ""; - } - - public static String convertLineStyle(HashMap characters, LINESTYLE2 ls, int shapeNum) { - String ret = ""; - String params = ""; - if (ls.pixelHintingFlag) { - params += " pixelHinting=\"true\""; - } - if (ls.width == 1) { - params += " solidStyle=\"hairline\""; - } - if ((!ls.noHScaleFlag) && (!ls.noVScaleFlag)) { - params += " scaleMode=\"normal\""; - } else if ((!ls.noHScaleFlag) && ls.noVScaleFlag) { - params += " scaleMode=\"horizontal\""; - } else if (ls.noHScaleFlag && (!ls.noVScaleFlag)) { - params += " scaleMode=\"vertical\""; - } - - switch (ls.endCapStyle) { //What about endCapStyle? - case LINESTYLE2.NO_CAP: - params += " caps=\"none\""; - break; - case LINESTYLE2.SQUARE_CAP: - params += " caps=\"square\""; - break; - } - switch (ls.joinStyle) { - case LINESTYLE2.BEVEL_JOIN: - params += " joints=\"bevel\""; - break; - case LINESTYLE2.MITER_JOIN: - params += " joints=\"miter\""; - float miterLimitFactor = toFloat(ls.miterLimitFactor); - if (miterLimitFactor != 3.0f) { - params += " miterLimit=\"" + miterLimitFactor + "\""; - } - break; - } - - ret += ""; - } else { - ret += convertFillStyle(null/* FIXME */, characters, ls.fillType, shapeNum); - } - ret += ""; - ret += ""; - return ret; - } - - private static float toFloat(int i) { - return ((float) i) / (1 << 16); - } - - public static String convertFillStyle(MATRIX mat, HashMap characters, FILLSTYLE fs, int shapeNum) { - if (mat == null) { - mat = new MATRIX(); - } - String ret = ""; - //ret += ""; - switch (fs.fillStyleType) { - case FILLSTYLE.SOLID: - ret += "= 3) { - ret += " alpha=\"" + ((RGBA) fs.color).getAlphaFloat() + "\""; - } - ret += " />"; - break; - case FILLSTYLE.REPEATING_BITMAP: - case FILLSTYLE.CLIPPED_BITMAP: - case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP: - case FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP: - ret += ""; - } - ret += "\""; - - if ((fs.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) || (fs.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { - ret += " bitmapIsClipped=\"true\""; - } - - ret += ">"; - ret += "" + convertMatrix(fs.bitmapMatrix) + ""; - ret += ""; - break; - case FILLSTYLE.LINEAR_GRADIENT: - case FILLSTYLE.RADIAL_GRADIENT: - case FILLSTYLE.FOCAL_RADIAL_GRADIENT: - - if (fs.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) { - ret += ""; - GRADRECORD[] records; - if (fs.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { - records = fs.gradient.gradientRecords; - } else { - records = fs.gradient.gradientRecords; - } - for (GRADRECORD rec : records) { - ret += "= 3 ? rec.color.toHexRGB() : rec.color.toHexRGB()) + "\""; - if (shapeNum >= 3) { - ret += " alpha=\"" + ((RGBA) rec.color).getAlphaFloat() + "\""; - } - ret += " ratio=\"" + rec.getRatioFloat() + "\""; - ret += " />"; - } - if (fs.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) { - ret += ""; - } else { - ret += ""; - } - break; - } - //ret += ""; - return ret; - - } - - public static String convertMatrix(MATRIX m) { - return convertMatrix(new Matrix(m)); - } - - public static String convertMatrix(Matrix m) { - String ret = ""; - if (m == null) { - m = new Matrix(); - } - ret += " characters, MATRIX mat, int shapeNum, SHAPE shape) { - return convertShape(characters, mat, shapeNum, shape.shapeRecords); - } - - public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, SHAPEWITHSTYLE shape,boolean morphShape) { - return convertShape(characters, mat, shapeNum, shape.shapeRecords, shape.fillStyles, shape.lineStyles, morphShape); - } - - public static String convertShape(HashMap characters, MATRIX mat, ShapeTag shape, boolean morphShape) { - return convertShape(characters, mat, shape.getShapeNum(), shape.getShapes(),morphShape); - } - - public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords,boolean useLayers) { - return convertShape(characters, mat, shapeNum, shapeRecords, null, null, false,true); - }*/ - - private static boolean shapeHasMultiLayers(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) { - List layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, false); - return layers.size() > 1; - } - - public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers) { - String ret = ""; - List layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape); - if (layers.size() == 1 && !useLayers) { - ret += layers.get(0); - } else { - int layer = 1; - for (int l = layers.size() - 1; l >= 0; l--) { - ret += ""; //color=\"#4FFF4F\" - ret += ""; - ret += ""; - ret += ""; - ret += layers.get(l); - ret += ""; - ret += ""; - ret += ""; - ret += ""; - } - } - return ret; - } - - /** - * Remove bugs in shape: - * - * ... straightrecord straightrecord stylechange straightrecord (-2,0) <-- - * merge this with previous stylegchange - * - * @param shapeRecords - * @return - */ - private static List smoothShape(List shapeRecords) { - List ret = new ArrayList<>(shapeRecords); - - for (int i = 1; i < ret.size() - 1; i++) { - if (ret.get(i) instanceof StraightEdgeRecord && (ret.get(i - 1) instanceof StyleChangeRecord) && (ret.get(i + 1) instanceof StyleChangeRecord)) { - StraightEdgeRecord ser = (StraightEdgeRecord) ret.get(i); - StyleChangeRecord scr = (StyleChangeRecord) ret.get(i - 1); - StyleChangeRecord scr2 = (StyleChangeRecord) ret.get(i + 1); - if ((!scr.stateMoveTo && !scr.stateNewStyles) && Math.abs(ser.deltaX) < 5 && Math.abs(ser.deltaY) < 5) { - if (i >= 2) { - SHAPERECORD rbef = ret.get(i - 2); - if (rbef instanceof StraightEdgeRecord) { - StraightEdgeRecord ser_b = (StraightEdgeRecord) rbef; - ser_b.generalLineFlag = true; - ser_b.deltaX = ser.changeX(ser_b.deltaX); - ser_b.deltaY = ser.changeY(ser_b.deltaY); - } else if (rbef instanceof CurvedEdgeRecord) { - CurvedEdgeRecord cer_b = (CurvedEdgeRecord) rbef; - cer_b.anchorDeltaX = ser.changeX(cer_b.anchorDeltaX); - cer_b.anchorDeltaY = ser.changeY(cer_b.anchorDeltaY); - } else { - //??? - } - if (i >= 2) { - ret.remove(i - 1); - ret.remove(i - 1); - if (scr.stateFillStyle0 && !scr2.stateFillStyle0) { - scr2.stateFillStyle0 = true; - scr2.fillStyle0 = scr.fillStyle0; - } - if (scr.stateFillStyle1 && !scr2.stateFillStyle1) { - scr2.stateFillStyle1 = true; - scr2.fillStyle1 = scr.fillStyle1; - } - if (scr.stateLineStyle && !scr2.stateLineStyle) { - scr2.stateLineStyle = true; - scr2.lineStyle = scr.lineStyle; - } - i -= 2; - } - - } - } - } - - } - return ret; - } - - public static List getShapeLayers(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape) { - if (mat == null) { - mat = new MATRIX(); - } - shapeRecords = smoothShape(shapeRecords); - List edges = new ArrayList<>(); - int lineStyleCount = 0; - int fillStyle0 = -1; - int fillStyle1 = -1; - int strokeStyle = -1; - String fillsStr = ""; - String strokesStr = ""; - fillsStr += ""; - strokesStr += ""; - List layers = new ArrayList<>(); - String currentLayer = ""; - - int fillStyleCount = 0; - if (fillStyles != null) { - for (FILLSTYLE fs : fillStyles.fillStyles) { - fillsStr += ""; - fillsStr += convertFillStyle(mat, characters, fs, shapeNum); - fillsStr += ""; - fillStyleCount++; - } - } - if (lineStyles != null) { - if ((shapeNum == 4) && (lineStyles.lineStyles != null)) { //(shapeNum == 4) { - for (int l = 0; l < lineStyles.lineStyles.length; l++) { - strokesStr += ""; - strokesStr += convertLineStyle(characters, (LINESTYLE2) lineStyles.lineStyles[l], shapeNum); - strokesStr += ""; - lineStyleCount++; - } - } else if (lineStyles.lineStyles != null) { - for (int l = 0; l < lineStyles.lineStyles.length; l++) { - strokesStr += ""; - strokesStr += convertLineStyle(lineStyles.lineStyles[l], shapeNum); - strokesStr += ""; - lineStyleCount++; - } - } - } - - fillsStr += ""; - strokesStr += ""; - - int layer = 1; - - if ((fillStyleCount > 0) || (lineStyleCount > 0)) { - currentLayer += ""; - currentLayer += fillsStr; - currentLayer += strokesStr; - currentLayer += ""; - } - int x = 0; - int y = 0; - int startEdgeX = 0; - int startEdgeY = 0; - - LINESTYLEARRAY actualLinestyles = lineStyles; - int strokeStyleOrig = 0; - fillStyleCount = fillStyles.fillStyles.length; - for (SHAPERECORD edge : shapeRecords) { - if (edge instanceof StyleChangeRecord) { - StyleChangeRecord scr = (StyleChangeRecord) edge; - boolean styleChange = false; - int lastFillStyle1 = fillStyle1; - int lastFillStyle0 = fillStyle0; - int lastStrokeStyle = strokeStyle; - if (scr.stateNewStyles) { - fillsStr = ""; - strokesStr = ""; - if (fillStyleCount > 0 || lineStyleCount > 0) { - - if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { - - boolean empty = false; - if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { - if (shapeNum == 4) { - if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { - RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; - if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { - empty = true; - } - } - } - } - } - if (!empty) { - currentLayer += " -1) { - currentLayer += " fillStyle0=\"" + fillStyle0 + "\""; - } - if (fillStyle1 > -1) { - currentLayer += " fillStyle1=\"" + fillStyle1 + "\""; - } - if (strokeStyle > -1) { - currentLayer += " strokeStyle=\"" + strokeStyle + "\""; - } - currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; - } - } - - currentLayer += ""; - currentLayer += ""; - if (!currentLayer.contains("")) { //no empty layers, TODO:handle this better - layers.add(currentLayer); - } - currentLayer = ""; - } - - currentLayer += ""; - //ret += convertShape(characters, null, shape); - for (int f = 0; f < scr.fillStyles.fillStyles.length; f++) { - fillsStr += ""; - fillsStr += convertFillStyle(mat, characters, scr.fillStyles.fillStyles[f], shapeNum); - fillsStr += ""; - fillStyleCount++; - } - - lineStyleCount = 0; - if (shapeNum == 4) { - for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) { - strokesStr += ""; - strokesStr += convertLineStyle(characters, (LINESTYLE2) scr.lineStyles.lineStyles[l], shapeNum); - strokesStr += ""; - lineStyleCount++; - } - } else { - for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) { - strokesStr += ""; - strokesStr += convertLineStyle(scr.lineStyles.lineStyles[l], shapeNum); - strokesStr += ""; - lineStyleCount++; - } - } - fillsStr += ""; - strokesStr += ""; - currentLayer += fillsStr; - currentLayer += strokesStr; - currentLayer += ""; - actualLinestyles = scr.lineStyles; - } - if (scr.stateFillStyle0) { - int fillStyle0_new = scr.fillStyle0;// == 0 ? 0 : fillStylesMap.get(fillStyleCount - lastFillStyleCount + scr.fillStyle0 - 1) + 1; - if (morphshape) { //??? - fillStyle1 = fillStyle0_new; - } else { - fillStyle0 = fillStyle0_new; - } - styleChange = true; - } - if (scr.stateFillStyle1) { - int fillStyle1_new = scr.fillStyle1;// == 0 ? 0 : fillStylesMap.get(fillStyleCount - lastFillStyleCount + scr.fillStyle1 - 1) + 1; - if (morphshape) { - fillStyle0 = fillStyle1_new; - } else { - fillStyle1 = fillStyle1_new; - } - styleChange = true; - } - if (scr.stateLineStyle) { - strokeStyle = scr.lineStyle;// == 0 ? 0 : lineStyleCount - lastLineStyleCount + scr.lineStyle; - strokeStyleOrig = scr.lineStyle - 1; - styleChange = true; - } - if (!edges.isEmpty()) { - if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { - boolean empty = false; - if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { - if (shapeNum == 4) { - if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { - RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; - if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { - empty = true; - } - } - } - } - } - if (!empty) { - currentLayer += " -1) { - currentLayer += " fillStyle0=\"" + lastFillStyle0 + "\""; - } - if (fillStyle1 > -1) { - currentLayer += " fillStyle1=\"" + lastFillStyle1 + "\""; - } - if (strokeStyle > -1) { - currentLayer += " strokeStyle=\"" + lastStrokeStyle + "\""; - } - currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; - } - - startEdgeX = x; - startEdgeY = y; - } - edges.clear(); - } - } - edges.add(edge); - x = edge.changeX(x); - y = edge.changeY(y); - } - if (!edges.isEmpty()) { - if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { - - boolean empty = false; - if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { - if (shapeNum == 4) { - if (strokeStyleOrig > 0) { - if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { - RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; - if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { - empty = true; - } - } - } - } - } - if (!empty) { - currentLayer += " -1) { - currentLayer += " fillStyle0=\"" + fillStyle0 + "\""; - } - if (fillStyle1 > -1) { - currentLayer += " fillStyle1=\"" + fillStyle1 + "\""; - } - if (strokeStyle > -1) { - currentLayer += " strokeStyle=\"" + strokeStyle + "\""; - } - currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; - } - } - } - edges.clear(); - fillsStr += ""; - strokesStr += ""; - if (!currentLayer.isEmpty()) { - currentLayer += ""; - currentLayer += ""; - - if (!currentLayer.contains("")) { //no empty layers, TODO:handle this better - layers.add(currentLayer); - } - } - return layers; - } - - private static int getLayerCount(List tags) { - int maxDepth = 0; - for (Tag t : tags) { - if (t instanceof PlaceObjectTypeTag) { - int d = ((PlaceObjectTypeTag) t).getDepth(); - if (d > maxDepth) { - maxDepth = d; - } - int cd = ((PlaceObjectTypeTag) t).getClipDepth(); - if (cd > maxDepth) { - maxDepth = cd; - } - } - } - return maxDepth; - } - - private static void walkShapeUsages(List timeLineTags, HashMap characters, HashMap usages) { - for (Tag t : timeLineTags) { - if (t instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) t; - walkShapeUsages(sprite.subTags, characters, usages); - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - int ch = po.getCharacterId(); - if (ch > -1) { - if (!usages.containsKey(ch)) { - usages.put(ch, 0); - } - int usageCount = usages.get(ch); - if (po.getInstanceName() != null) { - usageCount++; - } - if (po.getColorTransform() != null) { - usageCount++; - } - if (po.cacheAsBitmap()) { - usageCount++; - } - MATRIX mat = po.getMatrix(); - if (mat != null) { - if (!mat.isEmpty()) { - usageCount++; - } - } - usages.put(ch, usageCount + 1); - } - } - } - } - - private static List getNonLibraryShapes(List tags, HashMap characters) { - HashMap usages = new HashMap<>(); - walkShapeUsages(tags, characters, usages); - List ret = new ArrayList<>(); - for (int ch : usages.keySet()) { - if (usages.get(ch) < 2) { - if (characters.get(ch) instanceof ShapeTag) { - ShapeTag shp = (ShapeTag) characters.get(ch); - if (!shapeHasMultiLayers(characters, null, shp.getShapeNum(), shp.getShapes().shapeRecords, shp.getShapes().fillStyles, shp.getShapes().lineStyles)) { - ret.add(ch); - } - } - - } - } - return ret; - } - - private static HashMap getCharacters(List tags) { - HashMap ret = new HashMap<>(); - int maxId = 0; - for (Tag t : tags) { - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - if (ct.getCharacterId() > maxId) { - maxId = ct.getCharacterId(); - } - } - } - for (Tag t : tags) { - if (t instanceof SoundStreamHeadTypeTag) { - SoundStreamHeadTypeTag ssh = (SoundStreamHeadTypeTag) t; - ssh.setVirtualCharacterId(++maxId); - } - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - ret.put(ct.getCharacterId(), ct); - } - } - return ret; - } - private static final String[] BLENDMODES = { - null, - null, - "layer", - "multiply", - "screen", - "lighten", - "darken", - "difference", - "add", - "subtract", - "invert", - "alpha", - "erase", - "overlay", - "hardligh" - }; - - private static double radToDeg(double rad) { - return rad * 180 / Math.PI; - } - - private static String doubleToString(double d, int precision) { - double m = Math.pow(10, precision); - d = Math.round(d * m) / m; - return doubleToString(d); - } - - private static String doubleToString(double d) { - String ds = "" + d; - if (ds.endsWith(".0")) { - ds = ds.substring(0, ds.length() - 2); - } - return ds; - } - - public static String convertFilter(FILTER filter) { - String ret = ""; - if (filter instanceof DROPSHADOWFILTER) { - DROPSHADOWFILTER dsf = (DROPSHADOWFILTER) filter; - ret += " filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, CharacterTag tag, HashMap characters, List tags, FLAVersion flaVersion) { - String ret = ""; - if (matrix == null) { - matrix = new MATRIX(); - } - if (tag instanceof DefineButtonTag) { - DefineButtonTag bt = (DefineButtonTag) tag; - for (Tag t : tags) { - if (t instanceof DefineButtonCxformTag) { - DefineButtonCxformTag bcx = (DefineButtonCxformTag) t; - if (bcx.buttonId == bt.buttonId) { - colorTransform = bcx.buttonColorTransform; - } - } - } - } - - ret += "= FLAVersion.CS5_5.ordinal()) { - ret += " isVisible=\"false\""; - } - ret += ">"; - ret += ""; - ret += convertMatrix(matrix); - ret += ""; - ret += ""; - - if (backgroundColor != null) { - ret += " characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, List tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion) { - - //TODO: Imported assets - //linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf" - String ret = ""; - List media = new ArrayList<>(); - List symbols = new ArrayList<>(); - for (int ch : characters.keySet()) { - CharacterTag symbol = characters.get(ch); - if ((symbol instanceof ShapeTag) && nonLibraryShapes.contains(symbol.getCharacterId())) { - continue; //shapes with 1 ocurrence and single layer are not added to library - } - if ((symbol instanceof ShapeTag) || (symbol instanceof DefineSpriteTag) || (symbol instanceof ButtonTag)) { - String symbolStr = ""; - - symbolStr += ""; - symbolStr += ""; - - ButtonTag button = (ButtonTag) symbol; - List records = button.getRecords(); - String[] frames = {"", "", "", ""}; - - int maxDepth = 0; - for (BUTTONRECORD rec : records) { - if (rec.placeDepth > maxDepth) { - maxDepth = rec.placeDepth; - } - } - for (int i = maxDepth; i >= 1; i--) { - symbolStr += ""; - symbolStr += ""; - int lastFrame = 0; - loopframes: - for (int frame = 1; frame <= 4; frame++) { - for (BUTTONRECORD rec : records) { - if (rec.placeDepth == i) { - boolean ok = false; - switch (frame) { - case 1: - ok = rec.buttonStateUp; - break; - case 2: - ok = rec.buttonStateOver; - break; - case 3: - ok = rec.buttonStateDown; - break; - case 4: - ok = rec.buttonStateHitTest; - break; - } - if (!ok) { - continue; - } - CXFORMWITHALPHA colorTransformAlpha = null; - int blendMode = 0; - List filters = new ArrayList<>(); - if (button instanceof DefineButton2Tag) { - colorTransformAlpha = rec.colorTransform; - if (rec.buttonHasBlendMode) { - blendMode = rec.blendMode; - } - if (rec.buttonHasFilterList) { - filters = rec.filterList; - } - } - CharacterTag character = characters.get(rec.characterId); - MATRIX matrix = rec.placeMatrix; - String recCharStr = ""; - if (character instanceof TextTag) { - recCharStr = convertText(null, tags, (TextTag) character, matrix, filters, null); - } else if (character instanceof DefineVideoStreamTag) { - recCharStr = convertVideoInstance(null, matrix, (DefineVideoStreamTag) character, null); - } else { - recCharStr = convertSymbolInstance(null, matrix, colorTransformAlpha, false, blendMode, filters, true, null, null, characters.get(rec.characterId), characters, tags, flaVersion); - } - int duration = frame - lastFrame; - lastFrame = frame; - if (duration > 0) { - if (duration > 1) { - symbolStr += ""; - symbolStr += ""; - symbolStr += ""; - symbolStr += ""; - } - symbolStr += ""; - symbolStr += ""; - symbolStr += recCharStr; - symbolStr += ""; - symbolStr += ""; - } - } - } - } - symbolStr += ""; - symbolStr += ""; - } - symbolStr += ""; - symbolStr += ""; - } else if (symbol instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) symbol; - if (sprite.subTags.isEmpty()) { //probably AS2 class - continue; - } - symbolStr += convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getSubTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files); - } else if (symbol instanceof ShapeTag) { - itemIcon = "1"; - ShapeTag shape = (ShapeTag) symbol; - symbolStr += ""; - symbolStr += ""; - symbolStr += convertShape(characters, null, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, true); - symbolStr += ""; - symbolStr += ""; - } - symbolStr += ""; - symbolStr += ""; - symbolStr = prettyFormatXML(symbolStr); - String symbolFile = "Symbol " + symbol.getCharacterId() + ".xml"; - files.put(symbolFile, Utf8Helper.getBytes(symbolStr)); - String symbLinkStr = ""; - symbLinkStr += "= FLAVersion.CS5_5.ordinal()) { - symbLinkStr += " lastModified=\"" + getTimestamp() + "\""; - //TODO: itemID=\"518de416-00000341\" - } - symbLinkStr += "/>"; - symbols.add(symbLinkStr); - } else if (symbol instanceof ImageTag) { - ImageTag imageTag = (ImageTag) symbol; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SerializableImage image = imageTag.getImage(); - String format = imageTag.getImageFormat(); - try { - ImageIO.write(image.getBufferedImage(), format.toUpperCase(), baos); - } catch (IOException ex) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); - } - String symbolFile = "bitmap" + symbol.getCharacterId() + "." + imageTag.getImageFormat(); - files.put(symbolFile, baos.toByteArray()); - String mediaLinkStr = "\n"; - media.add(mediaLinkStr); - - } else if ((symbol instanceof SoundStreamHeadTypeTag) || (symbol instanceof DefineSoundTag)) { - int soundFormat = 0; - int soundRate = 0; - boolean soundType = false; - boolean soundSize = false; - long soundSampleCount = 0; - byte[] soundData = new byte[0]; - int[] rateMap = {5, 11, 22, 44}; - String exportFormat = "flv"; - if (symbol instanceof SoundStreamHeadTypeTag) { - SoundStreamHeadTypeTag sstream = (SoundStreamHeadTypeTag) symbol; - soundFormat = sstream.getSoundFormatId(); - soundRate = sstream.getSoundRate(); - soundType = sstream.getSoundType(); - soundSize = sstream.getSoundSize(); - soundSampleCount = sstream.getSoundSampleCount(); - boolean found = false; - for (Tag t : tags) { - if (found && (t instanceof SoundStreamBlockTag)) { - SoundStreamBlockTag bl = (SoundStreamBlockTag) t; - soundData = bl.getData(); - break; - } - if (t == symbol) { - found = true; - } - } - } else if (symbol instanceof DefineSoundTag) { - DefineSoundTag sound = (DefineSoundTag) symbol; - soundFormat = sound.soundFormat; - soundRate = sound.soundRate; - soundType = sound.soundType; - soundData = sound.soundData; - soundSize = sound.soundSize; - soundSampleCount = sound.soundSampleCount; - } - int format = 0; - int bits = 0; - if ((soundFormat == SoundFormat.FORMAT_ADPCM) - || (soundFormat == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) - || (soundFormat == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN)) { - exportFormat = "wav"; - if (soundType) { //stereo - format += 1; - } - switch (soundRate) { - case 0: - format += 2; - break; - case 1: - format += 6; - break; - case 2: - format += 10; - break; - case 3: - format += 14; - break; - } - } - if (soundFormat == SoundFormat.FORMAT_SPEEX) { - bits = 18; - } - if (soundFormat == SoundFormat.FORMAT_ADPCM) { - SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(soundData), swf.version); - exportFormat = "wav"; - try { - int adpcmCodeSize = (int) sis.readUB(2); - bits = 2 + adpcmCodeSize; - } catch (IOException ex) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); - } - } - if (soundFormat == SoundFormat.FORMAT_MP3) { - exportFormat = "mp3"; - if (!soundType) { //mono - format += 1; - } - format += 4; //quality best - try { - MP3SOUNDDATA s = new MP3SOUNDDATA(new ByteArrayInputStream(soundData), false); - //sis.readSI16(); - //MP3FRAME frame = new MP3FRAME(sis); - MP3FRAME frame = s.frames.get(0); - int bitRate = frame.getBitRate(); - - switch (bitRate) { - case 8: - bits = 6; - break; - case 16: - bits = 7; - break; - case 20: - bits = 8; - break; - case 24: - bits = 9; - break; - case 32: - bits = 10; - break; - case 48: - bits = 11; - break; - case 56: - bits = 12; - break; - case 64: - bits = 13; - break; - case 80: - bits = 14; - break; - case 112: - bits = 15; - break; - case 128: - bits = 16; - break; - case 160: - bits = 17; - break; - - } - } catch (IOException ex) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); - } - } - SoundTag st = (SoundTag) symbol; - SoundFormat fmt = st.getSoundFormat(); - byte[] data = new byte[0]; - try { - data = new SoundExporter().exportSound(st, SoundExportMode.MP3_WAV); - } catch (IOException ex) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); - } - - String symbolFile = "sound" + symbol.getCharacterId() + "." + exportFormat; - files.put(symbolFile, data); - String mediaLinkStr = ""; - //Use the dat file, otherwise it does not work - datfiles.put(datFileName, new byte[]{ //Magic numbers, if anybody knows why, please tell me - (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xA0, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x78, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x59, (byte) 0x40, (byte) 0x18, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }); - } else { - files.put(symbolFile, data); - mediaLinkStr = " characters, List tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files) { - String ret = ""; - DefineSoundTag sound = null; - if (startSound != null) { - for (Tag t : tags) { - if (t instanceof DefineSoundTag) { - DefineSoundTag s = (DefineSoundTag) t; - if (s.soundId == startSound.soundId) { - sound = s; - break; - } - } - } - } - - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - if (shapeTween) { - ret += " tweenType=\"shape\" keyMode=\"" + KEY_MODE_SHAPE_TWEEN + "\""; - } else { - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\""; - } - String soundEnvelopeStr = ""; - if (soundStreamHead != null && startSound == null) { - String soundName = "sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat(); - ret += " soundName=\"" + soundName + "\""; - ret += " soundSync=\"stream\""; - soundEnvelopeStr += ""; - soundEnvelopeStr += ""; - soundEnvelopeStr += ""; - } - if (startSound != null && sound != null) { - String soundName = "sound" + sound.soundId + "." + sound.getExportFormat(); - ret += " soundName=\"" + soundName + "\""; - if (startSound.soundInfo.hasInPoint) { - ret += " inPoint44=\"" + startSound.soundInfo.inPoint + "\""; - } - if (startSound.soundInfo.hasOutPoint) { - ret += " outPoint44=\"" + startSound.soundInfo.outPoint + "\""; - } - if (startSound.soundInfo.hasLoops) { - if (startSound.soundInfo.loopCount == 32767) { - ret += " soundLoopMode=\"loop\""; - } - ret += " soundLoop=\"" + startSound.soundInfo.loopCount + "\""; - } - - if (startSound.soundInfo.syncStop) { - ret += " soundSync=\"stop\""; - } else if (startSound.soundInfo.syncNoMultiple) { - ret += " soundSync=\"start\""; - } - soundEnvelopeStr += ""; - if (startSound.soundInfo.hasEnvelope) { - for (SOUNDENVELOPE env : startSound.soundInfo.envelopeRecords) { - soundEnvelopeStr += ""; - } - - if (startSound.soundInfo.envelopeRecords.length == 1 - && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 - && startSound.soundInfo.envelopeRecords[0].pos44 == 0 - && startSound.soundInfo.envelopeRecords[0].rightLevel == 0) { - ret += " soundEffect=\"left channel\""; - } else if (startSound.soundInfo.envelopeRecords.length == 1 - && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 - && startSound.soundInfo.envelopeRecords[0].pos44 == 0 - && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768) { - ret += " soundEffect=\"right channel\""; - } else if (startSound.soundInfo.envelopeRecords.length == 2 - && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 - && startSound.soundInfo.envelopeRecords[0].pos44 == 0 - && startSound.soundInfo.envelopeRecords[0].rightLevel == 0 - && startSound.soundInfo.envelopeRecords[1].leftLevel == 0 - && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount - && startSound.soundInfo.envelopeRecords[1].rightLevel == 32768) { - ret += " soundEffect=\"fade left to right\""; - } else if (startSound.soundInfo.envelopeRecords.length == 2 - && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 - && startSound.soundInfo.envelopeRecords[0].pos44 == 0 - && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768 - && startSound.soundInfo.envelopeRecords[1].leftLevel == 32768 - && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount - && startSound.soundInfo.envelopeRecords[1].rightLevel == 0) { - ret += " soundEffect=\"fade right to left\""; - } else { - ret += " soundEffect=\"custom\""; - } - //TODO: fade in, fade out - - } else { - soundEnvelopeStr += ""; - } - soundEnvelopeStr += ""; - } - ret += ">"; - - ret += soundEnvelopeStr; - if (!actionScript.isEmpty()) { - ret += ""; - } - ret += ""; - ret += elements; - ret += ""; - ret += ""; - return ret; - } - - private static String convertVideoInstance(String instanceName, MATRIX matrix, DefineVideoStreamTag video, CLIPACTIONS clipActions) { - String ret = " nonLibraryShapes, List tags, List timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files) { - String ret = ""; - prevStr += ""; - int frame = -1; - String elements = ""; - String lastElements = ""; - - int duration = 1; - - CharacterTag character = null; - MATRIX matrix = null; - String instanceName = null; - ColorTransform colorTransForm = null; - boolean cacheAsBitmap = false; - int blendMode = 0; - List filters = new ArrayList<>(); - boolean isVisible = true; - RGBA backGroundColor = null; - CLIPACTIONS clipActions = null; - int characterId = -1; - int ratio = -1; - boolean shapeTween = false; - boolean lastShapeTween = false; - MorphShapeTag shapeTweener = null; - - for (Tag t : timelineTags) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getDepth() == depth) { - int newCharId = po.getCharacterId(); - if (newCharId == -1) { - newCharId = characterId; - } - characterId = newCharId; - if (characters.containsKey(characterId)) { - character = characters.get(characterId); - if (po.flagMove()) { - MATRIX matrix2 = po.getMatrix(); - if (matrix2 != null) { - matrix = matrix2; - } - String instanceName2 = po.getInstanceName(); - if (instanceName2 != null) { - instanceName = instanceName2; - } - ColorTransform colorTransForm2 = po.getColorTransform(); - if (colorTransForm2 != null) { - colorTransForm = colorTransForm2; - } - - CLIPACTIONS clipActions2 = po.getClipActions(); - if (clipActions2 != null) { - clipActions = clipActions2; - } - if (po.cacheAsBitmap()) { - cacheAsBitmap = true; - } - int blendMode2 = po.getBlendMode(); - if (blendMode2 > 0) { - blendMode = blendMode2; - } - List filters2 = po.getFilters(); - if (filters2 != null) { - filters = filters2; - } - int ratio2 = po.getRatio(); - if (ratio2 > -1) { - ratio = ratio2; - } - } else { - matrix = po.getMatrix(); - instanceName = po.getInstanceName(); - colorTransForm = po.getColorTransform(); - cacheAsBitmap = po.cacheAsBitmap(); - blendMode = po.getBlendMode(); - filters = po.getFilters(); - ratio = po.getRatio(); - clipActions = po.getClipActions(); - } - - } - } - } - - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - if (rt.getDepth() == depth) { - if (shapeTween && character != null) { - MorphShapeTag m = (MorphShapeTag) character; - shapeTweener = m; - shapeTween = false; - } - character = null; - matrix = null; - instanceName = null; - colorTransForm = null; - cacheAsBitmap = false; - blendMode = 0; - filters = new ArrayList<>(); - isVisible = true; - backGroundColor = null; - characterId = -1; - clipActions = null; - } - } - - if (t instanceof ShowFrameTag) { - elements = ""; - - if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId) || shapeTweener != null)) { - ShapeTag shape = (ShapeTag) character; - elements += convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false); - shapeTween = false; - shapeTweener = null; - } else if (character != null) { - if (character instanceof MorphShapeTag) { - MorphShapeTag m = (MorphShapeTag) character; - elements += convertShape(characters, matrix, 3, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false); - shapeTween = true; - } else { - shapeTween = false; - if (character instanceof TextTag) { - elements += convertText(instanceName, tags, (TextTag) character, matrix, filters, clipActions); - } else if (character instanceof DefineVideoStreamTag) { - elements += convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions); - } else { - elements += convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, character, characters, tags, flaVersion); - } - } - } - - frame++; - if (!elements.equals(lastElements) && frame > 0) { - ret += convertFrame(lastShapeTween, characters, tags, null, null, frame - duration, duration, "", lastElements, files); - duration = 1; - } else if (frame == 0) { - duration = 1; - } else { - duration++; - } - - lastShapeTween = shapeTween; - lastElements = elements; - } - } - if (!lastElements.isEmpty()) { - frame++; - ret += convertFrame(lastShapeTween, characters, tags, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files); - } - afterStr = "" + afterStr; - if (!ret.isEmpty()) { - ret = prevStr + ret + afterStr; - } - return ret; - } - - public static String convertFonts(List tags) { - String ret = ""; - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag font = (FontTag) t; - int fontId = font.getFontId(); - String fontName = null; - for (Tag t2 : tags) { - if (t2 instanceof DefineFontNameTag) { - if (((DefineFontNameTag) t2).fontId == fontId) { - fontName = ((DefineFontNameTag) t2).fontName; - } - } - } - if (fontName == null) { - fontName = font.getFontName(); - } - int fontStyle = font.getFontStyle(); - String installedFont; - if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { - fontName = new Font(installedFont, fontStyle, 10).getPSName(); - } - String embedRanges = ""; - - String fontChars = font.getCharacters(tags); - if ("".equals(fontChars)) { - continue; - } - String embeddedCharacters = fontChars; - embeddedCharacters = embeddedCharacters.replace("\u00A0", ""); //nonbreak space - embeddedCharacters = embeddedCharacters.replace(".", ""); - boolean hasAllRanges = false; - for (int r = 0; r < CharacterRanges.rangeCount(); r++) { - int codes[] = CharacterRanges.rangeCodes(r); - boolean hasAllInRange = true; - for (int i = 0; i < codes.length; i++) { - if (!fontChars.contains("" + (char) codes[i])) { - hasAllInRange = false; - break; - } - } - if (hasAllInRange) { - //remove all found characters - for (int i = 0; i < codes.length; i++) { - embeddedCharacters = embeddedCharacters.replace("" + (char) codes[i], ""); - } - if (!"".equals(embedRanges)) { - embedRanges += "|"; - } - embedRanges += (r + 1); - } else { - hasAllRanges = false; - } - } - if (hasAllRanges) { - embedRanges = "9999"; - } - ret += ""; - } - - } - - if (!"".equals(ret)) { - ret = "" + ret + ""; - } - - return ret; - } - - public static String convertActionScriptLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { - String ret = ""; - - String script = ""; - int duration = 0; - int frame = 0; - for (Tag t : tags) { - if (t instanceof DoInitActionTag) { - DoInitActionTag dia = (DoInitActionTag) t; - if (dia.spriteId == spriteId) { - script += convertActionScript(dia); - } - } - } - if (!script.isEmpty()) { - script = "#initclip\r\n" + script + "#endinitclip\r\n"; - } - for (Tag t : timeLineTags) { - if (t instanceof DoActionTag) { - DoActionTag da = (DoActionTag) t; - script += convertActionScript(da); - } - if (t instanceof ShowFrameTag) { - - if (script.isEmpty()) { - duration++; - } else { - if (duration > 0) { - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; - ret += ""; - ret += ""; - ret += ""; - } - ret += ""; - ret += ""; - ret += ""; - ret += ""; - ret += ""; - script = ""; - duration = 0; - } - frame++; - } - } - if (!ret.isEmpty()) { - ret = "" - + "" - + ret - + "" - + ""; - } - return ret; - } - - public static String convertLabelsLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { - String ret = ""; - int duration = 0; - int frame = 0; - String frameLabel = ""; - boolean isAnchor = false; - for (Tag t : timeLineTags) { - if (t instanceof FrameLabelTag) { - FrameLabelTag fl = (FrameLabelTag) t; - frameLabel = fl.getLabelName(); - isAnchor = fl.isNamedAnchor(); - } - if (t instanceof ShowFrameTag) { - - if (frameLabel.isEmpty()) { - duration++; - } else { - if (duration > 0) { - ret += " 1) { - ret += " duration=\"" + duration + "\""; - } - ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; - ret += ""; - ret += ""; - ret += ""; - } - ret += "" - + "" - + ret - + "" - + ""; - } - return ret; - } - - public static String convertSoundLayer(int layerIndex, String backgroundColor, HashMap characters, List tags, List timeLineTags, HashMap files) { - String ret = ""; - StartSoundTag lastStartSound = null; - SoundStreamHeadTypeTag lastSoundStreamHead = null; - StartSoundTag startSound = null; - SoundStreamHeadTypeTag soundStreamHead = null; - int duration = 1; - int frame = 0; - for (Tag t : timeLineTags) { - if (t instanceof StartSoundTag) { - startSound = (StartSoundTag) t; - - for (Tag ta : tags) { - if (ta instanceof DefineSoundTag) { - DefineSoundTag s = (DefineSoundTag) ta; - if (s.soundId == startSound.soundId) { - if (!files.containsKey("sound" + s.soundId + "." + s.getExportFormat())) { //Sound was not exported - startSound = null; //ignore - } - break; - } - } - } - - } - if (t instanceof SoundStreamHeadTypeTag) { - soundStreamHead = (SoundStreamHeadTypeTag) t; - if (!files.containsKey("sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat())) { //Sound was not exported - soundStreamHead = null; //ignore - } - } - if (t instanceof ShowFrameTag) { - if (soundStreamHead != null || startSound != null) { - if (lastSoundStreamHead != null || lastStartSound != null) { - ret += convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files); - } - frame += duration; - duration = 1; - lastSoundStreamHead = soundStreamHead; - lastStartSound = startSound; - soundStreamHead = null; - startSound = null; - } else { - duration++; - } - } - } - if (lastSoundStreamHead != null || lastStartSound != null) { - if (frame < 0) { - frame = 0; - duration = 1; - } - ret += convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files); - } - if (!ret.isEmpty()) { - ret = "" - + "" + ret + "" - + ""; - } - return ret; - } - - private static String randomOutlineColor() { - RGB outlineColor = new RGB(); - Random rnd = new Random(); - do { - outlineColor.red = rnd.nextInt(256); - outlineColor.green = rnd.nextInt(256); - outlineColor.blue = rnd.nextInt(256); - } while ((outlineColor.red + outlineColor.green + outlineColor.blue) / 3 < 128); - return outlineColor.toHexRGB(); - } - - public static String convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, List tags, List timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files) { - String ret = ""; - ret += ""; - ret += ""; - - ret += convertLabelsLayer(spriteId, tags, timelineTags, backgroundColor); - ret += convertActionScriptLayer(spriteId, tags, timelineTags, backgroundColor); - - int layerCount = getLayerCount(timelineTags); - Stack parentLayers = new Stack<>(); - int index = 0; - for (int d = layerCount; d >= 1; d--, index++) { - for (Tag t : timelineTags) { - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - if (po.getClipDepth() == d) { - for (int m = po.getDepth(); m < po.getClipDepth(); m++) { - parentLayers.push(index); - } - - ret += " getCharacterClasses(List tags) { - Map ret = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t; - for (int i = 0; i < sc.tags.length; i++) { - if (!ret.containsKey(sc.tags[i]) && !ret.containsValue(sc.names[i])) { - ret.put(sc.tags[i], sc.names[i]); - } - } - } - } - return ret; - } - - private static Map getCharacterVariables(List tags) { - Map ret = new HashMap<>(); - for (Tag t : tags) { - if (t instanceof ExportAssetsTag) { - ExportAssetsTag ea = (ExportAssetsTag) t; - for (int i = 0; i < ea.tags.size(); i++) { - if (!ret.containsKey(ea.tags.get(i))) { - ret.put(ea.tags.get(i), ea.names.get(i)); - } - } - } - } - return ret; - } - - public static String convertText(String instanceName, List tags, TextTag tag, MATRIX m, List filters, CLIPACTIONS clipActions) { - String ret = ""; - - if (m == null) { - m = new MATRIX(); - } - Matrix matrix=new Matrix(m); - CSMTextSettingsTag csmts = null; - String filterStr = ""; - if (filters != null) { - filterStr += ""; - for (FILTER f : filters) { - filterStr += convertFilter(f); - } - filterStr += ""; - } - - for (Tag t : tags) { - if (t instanceof CSMTextSettingsTag) { - CSMTextSettingsTag c = (CSMTextSettingsTag) t; - if (c.textID == tag.getCharacterId()) { - csmts = c; - break; - } - } - } - String fontRenderingMode = "standard"; - String antiAlias = ""; - if (csmts != null) { - if (csmts.thickness == 0 & csmts.sharpness == 0) { - fontRenderingMode = null; - } else { - fontRenderingMode = "customThicknessSharpness"; - } - antiAlias = " antiAliasSharpness=\"" + doubleToString(csmts.sharpness) + "\" antiAliasThickness=\"" + doubleToString(csmts.thickness) + "\""; - } - String matStr = ""; - matStr += ""; - String left = ""; - RECT bounds = tag.getBounds(); - if ((tag instanceof DefineTextTag) || (tag instanceof DefineText2Tag)) { - MATRIX textMatrix = tag.getTextMatrix(); - left = " left=\""+doubleToString((textMatrix.translateX)/SWF.unitDivisor)+"\""; - } - matStr += convertMatrix(matrix); - matStr += ""; - if ((tag instanceof DefineTextTag) || (tag instanceof DefineText2Tag)) { - List textRecords = new ArrayList<>(); - if (tag instanceof DefineTextTag) { - textRecords = ((DefineTextTag) tag).textRecords; - } else if (tag instanceof DefineText2Tag) { - textRecords = ((DefineText2Tag) tag).textRecords; - } - - looprec: - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasFont) { - for (Tag t : tags) { - if (t instanceof FontTag) { - FontTag ft = (FontTag) t; - if (ft.getFontId() == rec.fontId) { - if (ft.isSmall()) { - fontRenderingMode = "bitmap"; - break looprec; - } - } - } - } - } - } - - ret += " attrs = TextTag.getTextRecordsAttributes(textRecords, tags); - - ret += " width=\"" + tag.getBounds().getWidth() / 2 + "\" height=\"" + tag.getBounds().getHeight() + "\" autoExpand=\"true\" isSelectable=\"false\">"; - ret += matStr; - - ret += ""; - int fontId = -1; - FontTag font = null; - String fontName = null; - String psFontName = null; - int textHeight = -1; - RGB textColor = null; - RGBA textColorA = null; - boolean newline = false; - boolean firstRun = true; - @SuppressWarnings("unchecked") - List leftMargins = (List) attrs.get("allLeftMargins"); - @SuppressWarnings("unchecked") - List letterSpacings = (List) attrs.get("allLetterSpacings"); - for (int r = 0; r < textRecords.size(); r++) { - TEXTRECORD rec = textRecords.get(r); - if (rec.styleFlagsHasColor) { - if (tag instanceof DefineTextTag) { - textColor = rec.textColor; - } else { - textColorA = rec.textColorA; - } - } - if (rec.styleFlagsHasFont) { - fontId = rec.fontId; - fontName = null; - textHeight = rec.textHeight; - font = null; - for (Tag t : tags) { - if (t instanceof FontTag) { - if (((FontTag) t).getFontId() == fontId) { - font = (FontTag) t; - } - } - if (t instanceof DefineFontNameTag) { - if (((DefineFontNameTag) t).fontId == fontId) { - fontName = ((DefineFontNameTag) t).fontName; - } - } - } - if ((fontName == null) && (font != null)) { - fontName = font.getFontName(); - } - int fontStyle = 0; - if (font != null) { - fontStyle = font.getFontStyle(); - } - String installedFont; - if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { - psFontName = new Font(installedFont, fontStyle, 10).getPSName(); - } else { - psFontName = fontName; - } - } - newline = false; - if (!firstRun && rec.styleFlagsHasYOffset) { - newline = true; - } - firstRun = false; - if (font != null) { - ret += ""; - ret += "" + xmlString((newline ? "\r" : "") + rec.getText(font)) + ""; - ret += ""; - - ret += ""; - int leftMargin = -1; - int rightMargin = -1; - int indent = -1; - int lineSpacing = -1; - String alignment = null; - boolean italic = false; - boolean bold = false; - String fontFace = null; - int size = -1; - RGBA textColor = null; - if (det.hasTextColor) { - textColor = det.textColor; - } - if (det.hasFont) { - String fontName = null; - FontTag ft = null; - for (Tag u : tags) { - if (u instanceof DefineFontNameTag) { - if (((DefineFontNameTag) u).fontId == det.fontId) { - fontName = ((DefineFontNameTag) u).fontName; - } - } - if (u instanceof FontTag) { - if (((FontTag) u).getFontId() == det.fontId) { - ft = (FontTag) u; - } - } - if (fontName != null && ft != null) { - break; - } - } - if (ft != null) { - if (fontName == null) { - fontName = ft.getFontName(); - } - italic = ft.isItalic(); - bold = ft.isBold(); - size = det.fontHeight; - fontFace = fontName; - String installedFont = null; - if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { - fontName = installedFont; - fontFace = new Font(installedFont, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); - } - - } - } - if (det.hasLayout) { - leftMargin = det.leftMargin; - rightMargin = det.rightMargin; - indent = det.indent; - lineSpacing = det.leading; - String[] alignNames = {"left", "right", "center", "justify"}; - alignment = alignNames[det.align]; - } - ret += ""; - ret += " -1) { - ret += " indent=\"" + twipToPixel(indent) + "\""; - } - if (leftMargin > -1) { - ret += " leftMargin=\"" + twipToPixel(leftMargin) + "\""; - } - if (lineSpacing > -1) { - ret += " lineSpacing=\"" + twipToPixel(lineSpacing) + "\""; - } - if (rightMargin > -1) { - ret += " rightMargin=\"" + twipToPixel(rightMargin) + "\""; - } - if (size > -1) { - ret += " size=\"" + twipToPixel(size) + "\""; - ret += " bitmapSize=\"" + size + "\""; - } - if (fontFace != null) { - ret += " face=\"" + fontFace + "\""; - } - if (textColor != null) { - ret += " fillColor=\"" + textColor.toHexRGB() + "\" alpha=\"" + textColor.getAlphaFloat() + "\""; - } - ret += "/>"; - ret += ""; - ret += ""; - } - ret += ""; - ret += filterStr; - ret += ""; - } - return ret; - } - - public static void convertSWF(AbortRetryIgnoreHandler handler, SWF swf, String swfFileName, String outfile, boolean compressed, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion flaVersion) throws IOException { - - FileAttributesTag fa = null; - for (Tag t : swf.tags) { - if (t instanceof FileAttributesTag) { - fa = (FileAttributesTag) t; - } - } - - boolean useAS3 = false; - boolean useNetwork = false; - if (fa != null) { - useAS3 = fa.actionScript3; - useNetwork = fa.useNetwork; - } - - if (!useAS3 && flaVersion.minASVersion() > 2) { - throw new IllegalArgumentException("FLA version " + flaVersion + " does not support AS1/2"); - } - File file = new File(outfile); - File outDir = file.getParentFile(); - if (!outDir.exists()) { - if (!outDir.mkdirs()) { - if (!outDir.exists()) { - throw new IOException("cannot create directory " + outDir); - } - } - } - String domDocument = ""; - String baseName = swfFileName; - File f = new File(baseName); - baseName = f.getName(); - if (baseName.contains(".")) { - baseName = baseName.substring(0, baseName.lastIndexOf('.')); - } - final HashMap files = new HashMap<>(); - final HashMap datfiles = new HashMap<>(); - HashMap characters = getCharacters(swf.tags); - List nonLibraryShapes = getNonLibraryShapes(swf.tags, characters); - Map characterClasses = getCharacterClasses(swf.tags); - Map characterVariables = getCharacterVariables(swf.tags); - - String backgroundColor = "#ffffff"; - for (Tag t : swf.tags) { - if (t instanceof SetBackgroundColorTag) { - SetBackgroundColorTag sbc = (SetBackgroundColorTag) t; - backgroundColor = sbc.backgroundColor.toHexRGB(); - } - } - domDocument += " flaVersion.maxSwfVersion() ? flaVersion.maxSwfVersion() : swf.version; - String publishSettings = "\n" - + "\n" - + " \n" - + " 1\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 0\n" - + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 0\n" : " 0\n") - + " 0\n" - + " 0\n" - + " 1\n" - + " 1\n" - + " 1\n" - + " 1\n" - + " 1\n" - + " 1\n" - + " 1\n" - + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 1\n" : " 1\n") - + " 1\n" - + " 1\n" - + " " + baseName + ".swf\n" - + " " + baseName + ".exe\n" - + " " + baseName + ".app\n" - + " " + baseName + ".html\n" - + " " + baseName + ".gif\n" - + " " + baseName + ".jpg\n" - + " " + baseName + ".png\n" - + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 1\n" : " 1\n") - + " " + baseName + ".smil\n" - + " " + baseName + ".swc\n" - + " \n" - + " \n" - + " 0\n" - + " 12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1;\n" - + " 1\n" - + " 1\n" - + " " + baseName + "_content.html\n" - + " " + baseName + "_alternate.html\n" - + " 0\n" - + " \n" - + " " + width + "\n" - + " " + height + "\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " 1\n" - + " 4\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " \n" - + " 1\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 80\n" - + " 0\n" - + " 0\n" - + " 7\n" - + " 0\n" - + " 7\n" - + " 0\n" - + " " + flaSwfVersion + "\n" - + " " + FLAVersion.swfVersionToPlayer(flaSwfVersion) + "\n" - + " " + (useAS3 ? "3" : "2") + "\n" - + " 1\n" - + " \n" - + " .\n" - + " CONFIG::FLASH_AUTHORING="true";\n" - + " 0\n" - + " \n" - + " " + (swf.compressed ? "1" : "0") + "\n" - + " " + (swf.lzma ? "1" : "0") + "\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " " + (useNetwork ? 1 : 0) + "\n" - + " " + xmlString(characterClasses.containsKey(0) ? characterClasses.get(0) : "") + "\n" - + " 2\n" - + " 4\n" - + " 4096\n" - + " AS3\n" - + " 1\n" - + " 1\n" - + " 0\n" - + " 15\n" - + " 1\n" - + " 0\n" - + " 4102\n" - + " rsl\n" - + " wrap\n" - + " $(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf\n" - + ((flaVersion.ordinal() >= FLAVersion.CC.ordinal()) ? (" \n" - + " \n" - + " $(AppConfig)/ActionScript 3.0/libs\n" - + " merge\n" - + " \n" - + " \n" - + " $(FlexSDK)/frameworks/libs/flex.swc\n" - + " merge\n" - + " textLayout_2.0.0.232.swz\n" - + " \n" - + " \n" - + " $(FlexSDK)/frameworks/libs/core.swc\n" - + " merge\n" - + " textLayout_2.0.0.232.swz\n" - + " \n" - + " \n" - + " \n" - + " ") - : " \n" - + " \n" - + " $(AppConfig)/ActionScript 3.0/libs\n" - + " merge\n" - + " \n" - + " \n" - + " $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc\n" - + " rsl\n" - + " http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz\n" - + " http://fpdownload.adobe.com/pub/swz/crossdomain.xml\n" - + " textLayout_2.0.0.232.swz\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc\n" - + " \n" - + " http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz\n" - + " http://fpdownload.adobe.com/pub/swz/crossdomain.xml\n" - + " textLayout_2.0.0.232.swz\n" - + " \n" - + " \n") - + " \n" - + " \n" - + " " + width + "\n" - + " " + height + "\n" - + " 0\n" - + " 4718592\n" - + " 0\n" - + " 80\n" - + " 1\n" - + " \n" - + " \n" - + " 1\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 100000\n" - + " 1\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " \n" - + " \n" - + " " + width + "\n" - + " " + height + "\n" - + " 0\n" - + " 1\n" - + " 1\n" - + " \n" - + " 1\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " \n" - + " 128\n" - + " \n" - + " \n" - + " 255\n" - + " \n" - + " \n" - + " \n" - + " " + width + "\n" - + " " + height + "\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " \n" - + " \n" - + " \n" - + " 24-bit with Alpha\n" - + " 255\n" - + " \n" - + " \n" - + ((flaVersion.ordinal() >= FLAVersion.CC.ordinal()) ? "" - : (" \n" - + " " + width + "\n" - + " " + height + "\n" - + " 1\n" - + " 0\n" - + " \n" - + " \n" - + " 00000000\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 0\n" - + " 1\n" - + " \n")) - + "\n" - + ""; - - if (compressed) { - final String domDocumentF = domDocument; - final String publishSettingsF = publishSettings; - final String outfileF = outfile; - new RetryTask(new RunnableIOEx() { - @Override - public void run() throws IOException { - try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outfileF))) { - out.putNextEntry(new ZipEntry("DOMDocument.xml")); - out.write(Utf8Helper.getBytes(domDocumentF)); - out.putNextEntry(new ZipEntry("PublishSettings.xml")); - out.write(Utf8Helper.getBytes(publishSettingsF)); - for (String fileName : files.keySet()) { - out.putNextEntry(new ZipEntry("LIBRARY/" + fileName)); - out.write(files.get(fileName)); - } - for (String fileName : datfiles.keySet()) { - out.putNextEntry(new ZipEntry("bin/" + fileName)); - out.write(datfiles.get(fileName)); - } - } - } - }, handler).run(); - - } else { - - if (!outDir.exists()) { - if (!outDir.mkdirs()) { - if (!outDir.exists()) { - throw new IOException("cannot create directory " + outDir); - } - } - } - writeFile(handler, Utf8Helper.getBytes(domDocument), outDir.getAbsolutePath() + File.separator + "DOMDocument.xml"); - writeFile(handler, Utf8Helper.getBytes(publishSettings), outDir.getAbsolutePath() + File.separator + "PublishSettings.xml"); - File libraryDir = new File(outDir.getAbsolutePath() + File.separator + "LIBRARY"); - libraryDir.mkdir(); - File binDir = new File(outDir.getAbsolutePath() + File.separator + "bin"); - binDir.mkdir(); - for (String fileName : files.keySet()) { - writeFile(handler, files.get(fileName), libraryDir.getAbsolutePath() + File.separator + fileName); - } - for (String fileName : datfiles.keySet()) { - writeFile(handler, datfiles.get(fileName), binDir.getAbsolutePath() + File.separator + fileName); - } - writeFile(handler, Utf8Helper.getBytes("PROXY-CS5"), outfile); - } - if (useAS3) { - try { - swf.exportActionScript(handler, outDir.getAbsolutePath(), ScriptExportMode.AS, parallel); - } catch (Exception ex) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error during ActionScript3 export", ex); - } - } - } - - private static int normHue(double h) { - if (Double.isNaN(h)) { - h = -Math.PI; - } - int ret = (int) Math.round(h * 180 / Math.PI); - while (ret > 180) { - ret -= 360; - } - while (ret < -180) { - ret += 360; - } - return ret; - } - - private static int normBrightness(double b) { - if (Double.isNaN(b)) { - b = -100; - } - return (int) Math.round(b); - } - - private static int normSaturation(double s) { - if (Double.isNaN(s)) { - return -100; - } else if (s == 1) { - return 0; - } else if (s - 1 < 0) { - return (int) Math.round((s - 1) * 100); - } else { - return (int) Math.round(((s - 1) * 100) / 3); - } - } - - private static int normContrast(double c) { - double[] ctrMap = { - // 0 1 2 3 4 5 6 7 8 9 - /*0*/0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, - /*1*/ 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, - /*2*/ 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, - /*3*/ 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, - /*4*/ 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, - /*5*/ 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, - /*6*/ 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, - /*7*/ 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, - /*8*/ 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, - /*9*/ 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, - /*10*/ 10.0}; - if (c == 127) { - return 0; - } else if (c - 127 < 0) { - return (int) Math.round((c - 127) * 100.0 / 127.0); - } else { - c = (c - 127) / 127; - for (int i = 0; i < ctrMap.length; i++) { - if (ctrMap[i] >= c) { - return i; - } - } - } - return ctrMap.length - 1; - } - - private static boolean sameDouble(double a, double b) { - final double EPSILON = 0.00001; - return a == b ? true : Math.abs(a - b) < EPSILON; - } - - public static String convertAdjustColorFilter(COLORMATRIXFILTER filter) { - float[][] matrix = new float[5][5]; - int index = 0; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 5; j++) { - matrix[j][i] = filter.matrix[index]; - index++; - } - } - double a11 = matrix[0][0], a12 = matrix[0][1], a13 = matrix[0][2], - a21 = matrix[1][0], a22 = matrix[1][1], a23 = matrix[1][2], - a31 = matrix[2][0], a32 = matrix[2][1], a33 = matrix[2][2], - a41 = matrix[4][0]; - - double b, c, h, s; - b = (24872168661075.0 * a11 * a11 - 151430415740925.0 * a12 + 341095051289483.0 * a12 * a12 - 15302094789450.0 * a13 + 82428663495404.0 * a12 * a13 - - 4592294873812.0 * a13 * a13 + 43556251470.0 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - - 930 * a11 * (287 * a12 + 178 * a13)) + 2384730956550.0 * a12 * a41 + 240977870700.0 * a13 * a41 - - 685925220 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)) - * a41 + 465 * a11 * (466201717582.0 * a12 + 55756962908.0 * a13 + 764132175 * (-127 + 2 * a41))) - / (391687695450.0 * a11 * a11 + 5371575610858.0 * a12 * a12 + 1298089188904.0 * a12 * a13 - 72319604312.0 * a13 * a13 - + 1860 * a11 * (1835439833 * a12 + 219515602 * a13)); - c = (127 * (495225 * a11 + 1661845 * a12 + 167930 * a13 - + 478 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)))) - / 717495; - h = 2 * (Math.atan((-465 * a11 + 287 * a12 + 178 * a13 + Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - - 930 * a11 * (287 * a12 + 178 * a13))) / (500. * (a12 - a13))) + Math.PI/*+ Pi*C(1)*/); - s = (1543 * (-103355550 * a11 * a11 - 158872382 * a12 * a12 + 190161784 * a12 * a13 - 134644952 * a13 * a13 - + 1661845 * a12 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - - 930 * a11 * (287 * a12 + 178 * a13)) + 167930 * a13 - * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)) - + 465 * a11 * (274372 * a12 + 170168 * a13 + 1065 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 - + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13))))) - / (195843847725.0 * a11 * a11 + 2685787805429.0 * a12 * a12 + 649044594452.0 * a12 * a13 - 36159802156.0 * a13 * a13 - + 930 * a11 * (1835439833 * a12 + 219515602 * a13)); - - if (sameDouble(410 * a12, 1543 * a31) && sameDouble(410 * a12, 1543 * a32) && sameDouble(3047 * a12, 1543 * a21) && sameDouble(3047 * a12, 1543 * a23) - && sameDouble(a22, a11 + (1504 * a12) / 1543.) && sameDouble((1133 * a12) / 1543. + a33, a11) - /*&& (b == (195961 * a11 + 439039 * a12 + 1543 * (-127 + 2 * a41)) / (3086 * a11 + 6914 * a12)) - && (c == 127 * a11 + (439039 * a12) / 1543.) && (s == (1543 * (a11 - a12)) / (1543 * a11 + 3457 * a12)) - */ && !sameDouble(a11, a12) && !sameDouble(1543 * a11 + 3457 * a12, 0)) { - h = 0; - } - - return ""; - } - - private static String convertHTMLText(List tags, DefineEditTextTag det, String html) { - HTMLTextParser tparser = new HTMLTextParser(tags, det); - XMLReader parser; - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - parser = XMLReaderFactory.createXMLReader(); - parser.setContentHandler(tparser); - parser.setErrorHandler(tparser); - html = "\n" - + " \n" - + "]>" + html + ""; - try { - parser.parse(new InputSource(new StringReader(html))); - } catch (SAXParseException spe) { - System.out.println(html); - System.err.println(tparser.result); - } - } catch (SAXException | IOException e) { - Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error while converting HTML", e); - } - return tparser.result; - } - - private static String xmlString(String s) { - return s.replace("<", "<").replace(">", ">").replace("\"", """).replace("&", "&").replace("\r\n", " ").replace("\r", " ").replace("\n", " "); - } - - private static double twipToPixel(double tw) { - return tw / 20.0; - } - - private static class HTMLTextParser extends DefaultHandler { - - public String result = ""; - private String fontFace = ""; - private String color = ""; - private int size = -1; - private int indent = -1; - private int leftMargin = -1; - private int rightMargin = -1; - private int lineSpacing = -1; - private double letterSpacing = -1; - private String alignment = null; - private final List tags; - private boolean bold = false; - private boolean italic = false; - private boolean underline = false; - private boolean li = false; - private String url = null; - private String target = null; - - @Override - public void error(SAXParseException e) throws SAXException { - } - - @Override - public void fatalError(SAXParseException e) throws SAXException { - } - - @Override - public void warning(SAXParseException e) throws SAXException { - } - - public HTMLTextParser(List tags, DefineEditTextTag det) { - if (det.hasFont) { - String fontName = null; - FontTag ft = null; - for (Tag u : tags) { - if (u instanceof DefineFontNameTag) { - if (((DefineFontNameTag) u).fontId == det.fontId) { - fontName = ((DefineFontNameTag) u).fontName; - } - } - if (u instanceof FontTag) { - if (((FontTag) u).getFontId() == det.fontId) { - ft = (FontTag) u; - } - } - if (fontName != null && ft != null) { - break; - } - } - if (ft != null) { - if (fontName == null) { - fontName = ft.getFontName(); - } - italic = ft.isItalic(); - bold = ft.isBold(); - size = det.fontHeight; - fontFace = new Font(fontName, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); - } - } - if (det.hasLayout) { - leftMargin = det.leftMargin; - rightMargin = det.rightMargin; - indent = det.indent; - lineSpacing = det.leading; - String[] alignNames = {"left", "right", "center", "justify"}; - alignment = alignNames[det.align]; - } - this.tags = tags; - } - - @Override - public void startDocument() throws SAXException { - } - - @Override - public void startElement(String uri, String localName, - String qName, Attributes attributes) throws SAXException { - switch (qName) { - case "a": - String href = attributes.getValue("href"); - if (href != null) { - url = href; - } - String t = attributes.getValue("target"); - if (t != null) { - target = t; - } - break; - case "b": - bold = true; - break; - case "i": - italic = true; - break; - case "u": - underline = true; - break; - case "li": - li = true; - break; - case "p": - String a = attributes.getValue("align"); - if (a != null) { - alignment = a; - } - if (!result.isEmpty()) { - putText("\r\n"); - } - break; - case "font": - //kerning ? - String ls = attributes.getValue("letterSpacing"); - if (ls != null) { - letterSpacing = Double.parseDouble(ls); - } - String s = attributes.getValue("size"); - if (s != null) { - size = Integer.parseInt(s); - } - String c = attributes.getValue("color"); - if (c != null) { - color = c; - } - String f = attributes.getValue("face"); - if (f != null) { - for (Tag tag : tags) { - if (tag instanceof FontTag) { - FontTag ft = (FontTag) tag; - String fontName = null; - if (f.equals(ft.getFontName())) { - for (Tag u : tags) { - if (u instanceof DefineFontNameTag) { - if (((DefineFontNameTag) u).fontId == ft.getFontId()) { - fontName = ((DefineFontNameTag) u).fontName; - } - } - } - if (fontName == null) { - fontName = ft.getFontName(); - } - String installedFont; - if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { - fontFace = new Font(installedFont, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); - } else { - fontFace = fontName; - } - break; - } - } - } - } - break; - } - } - - @Override - public void endElement(String uri, String localName, - String qName) throws SAXException { - if (qName.equals("a")) { - url = null; - target = null; - } - if (qName.equals("b")) { - bold = false; - } - if (qName.equals("i")) { - italic = false; - } - if (qName.equals("u")) { - underline = false; - } - if (qName.equals("li")) { - li = false; - } - } - - private void putText(String txt) { - - result += ""; - result += "" + xmlString(txt) + ""; - result += ""; - result += " -1) { - result += " indent=\"" + twipToPixel(indent) + "\""; - } - if (leftMargin > -1) { - result += " leftMargin=\"" + twipToPixel(leftMargin) + "\""; - } - if (letterSpacing > -1) { - result += " letterSpacing=\"" + letterSpacing + "\""; - } - if (lineSpacing > -1) { - result += " lineSpacing=\"" + twipToPixel(lineSpacing) + "\""; - } - if (rightMargin > -1) { - result += " rightMargin=\"" + twipToPixel(rightMargin) + "\""; - } - if (size > -1) { - result += " size=\"" + size + "\""; - result += " bitmapSize=\"" + (size * 20) + "\""; - } - if (fontFace != null) { - result += " face=\"" + fontFace + "\""; - } - if (color != null) { - result += " fillColor=\"" + color + "\""; - } - if (url != null) { - result += " url=\"" + url + "\""; - } - if (target != null) { - result += " target=\"" + target + "\""; - } - result += "/>"; - result += ""; - result += ""; - } - - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { - putText(new String(ch, start, length)); - - } - - @Override - public void endDocument() { - if (this.result.isEmpty()) { - putText(""); - } - } - } -} +/* + * Copyright (C) 2010-2014 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.xfl; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.RetryTask; +import com.jpexs.decompiler.flash.RunnableIOEx; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.MovieExporter; +import com.jpexs.decompiler.flash.exporters.SoundExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFontNameTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.FrameLabelTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.StartSoundTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +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.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.font.CharacterRanges; +import com.jpexs.decompiler.flash.types.BUTTONCONDACTION; +import com.jpexs.decompiler.flash.types.BUTTONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONRECORD; +import com.jpexs.decompiler.flash.types.CLIPACTIONS; +import com.jpexs.decompiler.flash.types.CXFORMWITHALPHA; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.FOCALGRADIENT; +import com.jpexs.decompiler.flash.types.GRADIENT; +import com.jpexs.decompiler.flash.types.GRADRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLE2; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SOUNDENVELOPE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.filters.BEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.BLURFILTER; +import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER; +import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER; +import com.jpexs.decompiler.flash.types.filters.FILTER; +import com.jpexs.decompiler.flash.types.filters.GLOWFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER; +import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.decompiler.flash.types.sound.MP3FRAME; +import com.jpexs.decompiler.flash.types.sound.MP3SOUNDDATA; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Font; +import java.awt.Point; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import javax.imageio.ImageIO; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * + * @author JPEXS + */ +public class XFLConverter { + + public static final int KEY_MODE_NORMAL = 9728; + public static final int KEY_MODE_CLASSIC_TWEEN = 22017; + public static final int KEY_MODE_SHAPE_TWEEN = 17922; + public static final int KEY_MODE_MOTION_TWEEN = 8195; + public static final int KEY_MODE_SHAPE_LAYERS = 8192; + + private XFLConverter() { + } + + public static String convertShapeEdge(MATRIX mat, SHAPERECORD record, int x, int y) { + String ret = ""; + if (record instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) record; + Point p = new Point(scr.moveDeltaX, scr.moveDeltaY); + //p = mat.apply(p); + if (scr.stateMoveTo) { + return "! " + p.x + " " + p.y; + } + return ""; + } + if (record instanceof StraightEdgeRecord) { + StraightEdgeRecord ser = (StraightEdgeRecord) record; + if (ser.generalLineFlag || ser.vertLineFlag) { + y += ser.deltaY; + } + if (ser.generalLineFlag || (!ser.vertLineFlag)) { + x += ser.deltaX; + } + Point p = new Point(x, y); + //p = mat.apply(p); + return "| " + p.x + " " + p.y; + } + if (record instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer = (CurvedEdgeRecord) record; + int controlX = cer.controlDeltaX + x; + int controlY = cer.controlDeltaY + y; + int anchorX = cer.anchorDeltaX + controlX; + int anchorY = cer.anchorDeltaY + controlY; + Point control = new Point(controlX, controlY); + //control = mat.apply(control); + Point anchor = new Point(anchorX, anchorY); + //anchor = mat.apply(anchor); + return "[ " + control.x + " " + control.y + " " + anchor.x + " " + anchor.y; + } + return ret; + } + + public static String convertShapeEdges(int startX, int startY, MATRIX mat, List records) { + String ret = ""; + int x = startX; + int y = startY; + ret += "!" + startX + " " + startY; + for (SHAPERECORD rec : records) { + ret += convertShapeEdge(mat, rec, x, y); + x = rec.changeX(x); + y = rec.changeY(y); + } + return ret; + } + + public static String convertLineStyle(LINESTYLE ls, int shapeNum) { + return "" + + "" + + "" + + "" + + ""; + } + + public static String convertLineStyle(HashMap characters, LINESTYLE2 ls, int shapeNum) { + String ret = ""; + String params = ""; + if (ls.pixelHintingFlag) { + params += " pixelHinting=\"true\""; + } + if (ls.width == 1) { + params += " solidStyle=\"hairline\""; + } + if ((!ls.noHScaleFlag) && (!ls.noVScaleFlag)) { + params += " scaleMode=\"normal\""; + } else if ((!ls.noHScaleFlag) && ls.noVScaleFlag) { + params += " scaleMode=\"horizontal\""; + } else if (ls.noHScaleFlag && (!ls.noVScaleFlag)) { + params += " scaleMode=\"vertical\""; + } + + switch (ls.endCapStyle) { //What about endCapStyle? + case LINESTYLE2.NO_CAP: + params += " caps=\"none\""; + break; + case LINESTYLE2.SQUARE_CAP: + params += " caps=\"square\""; + break; + } + switch (ls.joinStyle) { + case LINESTYLE2.BEVEL_JOIN: + params += " joints=\"bevel\""; + break; + case LINESTYLE2.MITER_JOIN: + params += " joints=\"miter\""; + float miterLimitFactor = toFloat(ls.miterLimitFactor); + if (miterLimitFactor != 3.0f) { + params += " miterLimit=\"" + miterLimitFactor + "\""; + } + break; + } + + ret += ""; + } else { + ret += convertFillStyle(null/* FIXME */, characters, ls.fillType, shapeNum); + } + ret += ""; + ret += ""; + return ret; + } + + private static float toFloat(int i) { + return ((float) i) / (1 << 16); + } + + public static String convertFillStyle(MATRIX mat, HashMap characters, FILLSTYLE fs, int shapeNum) { + if (mat == null) { + mat = new MATRIX(); + } + String ret = ""; + //ret += ""; + switch (fs.fillStyleType) { + case FILLSTYLE.SOLID: + ret += "= 3) { + ret += " alpha=\"" + ((RGBA) fs.color).getAlphaFloat() + "\""; + } + ret += " />"; + break; + case FILLSTYLE.REPEATING_BITMAP: + case FILLSTYLE.CLIPPED_BITMAP: + case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP: + case FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP: + ret += ""; + } + ret += "\""; + + if ((fs.fillStyleType == FILLSTYLE.CLIPPED_BITMAP) || (fs.fillStyleType == FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP)) { + ret += " bitmapIsClipped=\"true\""; + } + + ret += ">"; + ret += "" + convertMatrix(fs.bitmapMatrix) + ""; + ret += ""; + break; + case FILLSTYLE.LINEAR_GRADIENT: + case FILLSTYLE.RADIAL_GRADIENT: + case FILLSTYLE.FOCAL_RADIAL_GRADIENT: + + if (fs.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) { + ret += ""; + GRADRECORD[] records; + if (fs.fillStyleType == FILLSTYLE.FOCAL_RADIAL_GRADIENT) { + records = fs.gradient.gradientRecords; + } else { + records = fs.gradient.gradientRecords; + } + for (GRADRECORD rec : records) { + ret += "= 3 ? rec.color.toHexRGB() : rec.color.toHexRGB()) + "\""; + if (shapeNum >= 3) { + ret += " alpha=\"" + ((RGBA) rec.color).getAlphaFloat() + "\""; + } + ret += " ratio=\"" + rec.getRatioFloat() + "\""; + ret += " />"; + } + if (fs.fillStyleType == FILLSTYLE.LINEAR_GRADIENT) { + ret += ""; + } else { + ret += ""; + } + break; + } + //ret += ""; + return ret; + + } + + public static String convertMatrix(MATRIX m) { + return convertMatrix(new Matrix(m)); + } + + public static String convertMatrix(Matrix m) { + String ret = ""; + if (m == null) { + m = new Matrix(); + } + ret += " characters, MATRIX mat, int shapeNum, SHAPE shape) { + return convertShape(characters, mat, shapeNum, shape.shapeRecords); + } + + public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, SHAPEWITHSTYLE shape,boolean morphShape) { + return convertShape(characters, mat, shapeNum, shape.shapeRecords, shape.fillStyles, shape.lineStyles, morphShape); + } + + public static String convertShape(HashMap characters, MATRIX mat, ShapeTag shape, boolean morphShape) { + return convertShape(characters, mat, shape.getShapeNum(), shape.getShapes(),morphShape); + } + + public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords,boolean useLayers) { + return convertShape(characters, mat, shapeNum, shapeRecords, null, null, false,true); + }*/ + + private static boolean shapeHasMultiLayers(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles) { + List layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, false); + return layers.size() > 1; + } + + public static String convertShape(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers) { + String ret = ""; + List layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape); + if (layers.size() == 1 && !useLayers) { + ret += layers.get(0); + } else { + int layer = 1; + for (int l = layers.size() - 1; l >= 0; l--) { + ret += ""; //color=\"#4FFF4F\" + ret += ""; + ret += ""; + ret += ""; + ret += layers.get(l); + ret += ""; + ret += ""; + ret += ""; + ret += ""; + } + } + return ret; + } + + /** + * Remove bugs in shape: + * + * ... straightrecord straightrecord stylechange straightrecord (-2,0) <-- + * merge this with previous stylegchange + * + * @param shapeRecords + * @return + */ + private static List smoothShape(List shapeRecords) { + List ret = new ArrayList<>(shapeRecords); + + for (int i = 1; i < ret.size() - 1; i++) { + if (ret.get(i) instanceof StraightEdgeRecord && (ret.get(i - 1) instanceof StyleChangeRecord) && (ret.get(i + 1) instanceof StyleChangeRecord)) { + StraightEdgeRecord ser = (StraightEdgeRecord) ret.get(i); + StyleChangeRecord scr = (StyleChangeRecord) ret.get(i - 1); + StyleChangeRecord scr2 = (StyleChangeRecord) ret.get(i + 1); + if ((!scr.stateMoveTo && !scr.stateNewStyles) && Math.abs(ser.deltaX) < 5 && Math.abs(ser.deltaY) < 5) { + if (i >= 2) { + SHAPERECORD rbef = ret.get(i - 2); + if (rbef instanceof StraightEdgeRecord) { + StraightEdgeRecord ser_b = (StraightEdgeRecord) rbef; + ser_b.generalLineFlag = true; + ser_b.deltaX = ser.changeX(ser_b.deltaX); + ser_b.deltaY = ser.changeY(ser_b.deltaY); + } else if (rbef instanceof CurvedEdgeRecord) { + CurvedEdgeRecord cer_b = (CurvedEdgeRecord) rbef; + cer_b.anchorDeltaX = ser.changeX(cer_b.anchorDeltaX); + cer_b.anchorDeltaY = ser.changeY(cer_b.anchorDeltaY); + } else { + //??? + } + if (i >= 2) { + ret.remove(i - 1); + ret.remove(i - 1); + if (scr.stateFillStyle0 && !scr2.stateFillStyle0) { + scr2.stateFillStyle0 = true; + scr2.fillStyle0 = scr.fillStyle0; + } + if (scr.stateFillStyle1 && !scr2.stateFillStyle1) { + scr2.stateFillStyle1 = true; + scr2.fillStyle1 = scr.fillStyle1; + } + if (scr.stateLineStyle && !scr2.stateLineStyle) { + scr2.stateLineStyle = true; + scr2.lineStyle = scr.lineStyle; + } + i -= 2; + } + + } + } + } + + } + return ret; + } + + public static List getShapeLayers(HashMap characters, MATRIX mat, int shapeNum, List shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape) { + if (mat == null) { + mat = new MATRIX(); + } + shapeRecords = smoothShape(shapeRecords); + List edges = new ArrayList<>(); + int lineStyleCount = 0; + int fillStyle0 = -1; + int fillStyle1 = -1; + int strokeStyle = -1; + String fillsStr = ""; + String strokesStr = ""; + fillsStr += ""; + strokesStr += ""; + List layers = new ArrayList<>(); + String currentLayer = ""; + + int fillStyleCount = 0; + if (fillStyles != null) { + for (FILLSTYLE fs : fillStyles.fillStyles) { + fillsStr += ""; + fillsStr += convertFillStyle(mat, characters, fs, shapeNum); + fillsStr += ""; + fillStyleCount++; + } + } + if (lineStyles != null) { + if ((shapeNum == 4) && (lineStyles.lineStyles != null)) { //(shapeNum == 4) { + for (int l = 0; l < lineStyles.lineStyles.length; l++) { + strokesStr += ""; + strokesStr += convertLineStyle(characters, (LINESTYLE2) lineStyles.lineStyles[l], shapeNum); + strokesStr += ""; + lineStyleCount++; + } + } else if (lineStyles.lineStyles != null) { + for (int l = 0; l < lineStyles.lineStyles.length; l++) { + strokesStr += ""; + strokesStr += convertLineStyle(lineStyles.lineStyles[l], shapeNum); + strokesStr += ""; + lineStyleCount++; + } + } + } + + fillsStr += ""; + strokesStr += ""; + + int layer = 1; + + if ((fillStyleCount > 0) || (lineStyleCount > 0)) { + currentLayer += ""; + currentLayer += fillsStr; + currentLayer += strokesStr; + currentLayer += ""; + } + int x = 0; + int y = 0; + int startEdgeX = 0; + int startEdgeY = 0; + + LINESTYLEARRAY actualLinestyles = lineStyles; + int strokeStyleOrig = 0; + fillStyleCount = fillStyles.fillStyles.length; + for (SHAPERECORD edge : shapeRecords) { + if (edge instanceof StyleChangeRecord) { + StyleChangeRecord scr = (StyleChangeRecord) edge; + boolean styleChange = false; + int lastFillStyle1 = fillStyle1; + int lastFillStyle0 = fillStyle0; + int lastStrokeStyle = strokeStyle; + if (scr.stateNewStyles) { + fillsStr = ""; + strokesStr = ""; + if (fillStyleCount > 0 || lineStyleCount > 0) { + + if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { + + boolean empty = false; + if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { + if (shapeNum == 4) { + if (strokeStyleOrig > 0) { + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; + } + } + } + } + } + if (!empty) { + currentLayer += " -1) { + currentLayer += " fillStyle0=\"" + fillStyle0 + "\""; + } + if (fillStyle1 > -1) { + currentLayer += " fillStyle1=\"" + fillStyle1 + "\""; + } + if (strokeStyle > -1) { + currentLayer += " strokeStyle=\"" + strokeStyle + "\""; + } + currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; + } + } + + currentLayer += ""; + currentLayer += ""; + if (!currentLayer.contains("")) { //no empty layers, TODO:handle this better + layers.add(currentLayer); + } + currentLayer = ""; + } + + currentLayer += ""; + //ret += convertShape(characters, null, shape); + for (int f = 0; f < scr.fillStyles.fillStyles.length; f++) { + fillsStr += ""; + fillsStr += convertFillStyle(mat, characters, scr.fillStyles.fillStyles[f], shapeNum); + fillsStr += ""; + fillStyleCount++; + } + + lineStyleCount = 0; + if (shapeNum == 4) { + for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) { + strokesStr += ""; + strokesStr += convertLineStyle(characters, (LINESTYLE2) scr.lineStyles.lineStyles[l], shapeNum); + strokesStr += ""; + lineStyleCount++; + } + } else { + for (int l = 0; l < scr.lineStyles.lineStyles.length; l++) { + strokesStr += ""; + strokesStr += convertLineStyle(scr.lineStyles.lineStyles[l], shapeNum); + strokesStr += ""; + lineStyleCount++; + } + } + fillsStr += ""; + strokesStr += ""; + currentLayer += fillsStr; + currentLayer += strokesStr; + currentLayer += ""; + actualLinestyles = scr.lineStyles; + } + if (scr.stateFillStyle0) { + int fillStyle0_new = scr.fillStyle0;// == 0 ? 0 : fillStylesMap.get(fillStyleCount - lastFillStyleCount + scr.fillStyle0 - 1) + 1; + if (morphshape) { //??? + fillStyle1 = fillStyle0_new; + } else { + fillStyle0 = fillStyle0_new; + } + styleChange = true; + } + if (scr.stateFillStyle1) { + int fillStyle1_new = scr.fillStyle1;// == 0 ? 0 : fillStylesMap.get(fillStyleCount - lastFillStyleCount + scr.fillStyle1 - 1) + 1; + if (morphshape) { + fillStyle0 = fillStyle1_new; + } else { + fillStyle1 = fillStyle1_new; + } + styleChange = true; + } + if (scr.stateLineStyle) { + strokeStyle = scr.lineStyle;// == 0 ? 0 : lineStyleCount - lastLineStyleCount + scr.lineStyle; + strokeStyleOrig = scr.lineStyle - 1; + styleChange = true; + } + if (!edges.isEmpty()) { + if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { + boolean empty = false; + if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { + if (shapeNum == 4) { + if (strokeStyleOrig > 0) { + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; + } + } + } + } + } + if (!empty) { + currentLayer += " -1) { + currentLayer += " fillStyle0=\"" + lastFillStyle0 + "\""; + } + if (fillStyle1 > -1) { + currentLayer += " fillStyle1=\"" + lastFillStyle1 + "\""; + } + if (strokeStyle > -1) { + currentLayer += " strokeStyle=\"" + lastStrokeStyle + "\""; + } + currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; + } + + startEdgeX = x; + startEdgeY = y; + } + edges.clear(); + } + } + edges.add(edge); + x = edge.changeX(x); + y = edge.changeY(y); + } + if (!edges.isEmpty()) { + if ((fillStyle0 > 0) || (fillStyle1 > 0) || (strokeStyle > 0)) { + + boolean empty = false; + if ((fillStyle0 <= 0) && (fillStyle1 <= 0) && (strokeStyle > 0) && morphshape) { + if (shapeNum == 4) { + if (strokeStyleOrig > 0) { + if (!((LINESTYLE2) actualLinestyles.lineStyles[strokeStyleOrig]).hasFillFlag) { + RGBA color = (RGBA) actualLinestyles.lineStyles[strokeStyleOrig].color; + if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0) { + empty = true; + } + } + } + } + } + if (!empty) { + currentLayer += " -1) { + currentLayer += " fillStyle0=\"" + fillStyle0 + "\""; + } + if (fillStyle1 > -1) { + currentLayer += " fillStyle1=\"" + fillStyle1 + "\""; + } + if (strokeStyle > -1) { + currentLayer += " strokeStyle=\"" + strokeStyle + "\""; + } + currentLayer += " edges=\"" + convertShapeEdges(startEdgeX, startEdgeY, mat, edges) + "\" />"; + } + } + } + edges.clear(); + fillsStr += ""; + strokesStr += ""; + if (!currentLayer.isEmpty()) { + currentLayer += ""; + currentLayer += ""; + + if (!currentLayer.contains("")) { //no empty layers, TODO:handle this better + layers.add(currentLayer); + } + } + return layers; + } + + private static int getLayerCount(List tags) { + int maxDepth = 0; + for (Tag t : tags) { + if (t instanceof PlaceObjectTypeTag) { + int d = ((PlaceObjectTypeTag) t).getDepth(); + if (d > maxDepth) { + maxDepth = d; + } + int cd = ((PlaceObjectTypeTag) t).getClipDepth(); + if (cd > maxDepth) { + maxDepth = cd; + } + } + } + return maxDepth; + } + + private static void walkShapeUsages(List timeLineTags, HashMap characters, HashMap usages) { + for (Tag t : timeLineTags) { + if (t instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) t; + walkShapeUsages(sprite.subTags, characters, usages); + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int ch = po.getCharacterId(); + if (ch > -1) { + if (!usages.containsKey(ch)) { + usages.put(ch, 0); + } + int usageCount = usages.get(ch); + if (po.getInstanceName() != null) { + usageCount++; + } + if (po.getColorTransform() != null) { + usageCount++; + } + if (po.cacheAsBitmap()) { + usageCount++; + } + MATRIX mat = po.getMatrix(); + if (mat != null) { + if (!mat.isEmpty()) { + usageCount++; + } + } + usages.put(ch, usageCount + 1); + } + } + } + } + + private static List getNonLibraryShapes(List tags, HashMap characters) { + HashMap usages = new HashMap<>(); + walkShapeUsages(tags, characters, usages); + List ret = new ArrayList<>(); + for (int ch : usages.keySet()) { + if (usages.get(ch) < 2) { + if (characters.get(ch) instanceof ShapeTag) { + ShapeTag shp = (ShapeTag) characters.get(ch); + if (!shapeHasMultiLayers(characters, null, shp.getShapeNum(), shp.getShapes().shapeRecords, shp.getShapes().fillStyles, shp.getShapes().lineStyles)) { + ret.add(ch); + } + } + + } + } + return ret; + } + + private static HashMap getCharacters(List tags) { + HashMap ret = new HashMap<>(); + int maxId = 0; + for (Tag t : tags) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (ct.getCharacterId() > maxId) { + maxId = ct.getCharacterId(); + } + } + } + for (Tag t : tags) { + if (t instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag ssh = (SoundStreamHeadTypeTag) t; + ssh.setVirtualCharacterId(++maxId); + } + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + ret.put(ct.getCharacterId(), ct); + } + } + return ret; + } + private static final String[] BLENDMODES = { + null, + null, + "layer", + "multiply", + "screen", + "lighten", + "darken", + "difference", + "add", + "subtract", + "invert", + "alpha", + "erase", + "overlay", + "hardligh" + }; + + private static double radToDeg(double rad) { + return rad * 180 / Math.PI; + } + + private static String doubleToString(double d, int precision) { + double m = Math.pow(10, precision); + d = Math.round(d * m) / m; + return doubleToString(d); + } + + private static String doubleToString(double d) { + String ds = "" + d; + if (ds.endsWith(".0")) { + ds = ds.substring(0, ds.length() - 2); + } + return ds; + } + + public static String convertFilter(FILTER filter) { + String ret = ""; + if (filter instanceof DROPSHADOWFILTER) { + DROPSHADOWFILTER dsf = (DROPSHADOWFILTER) filter; + ret += " filters, boolean isVisible, RGBA backgroundColor, CLIPACTIONS clipActions, CharacterTag tag, HashMap characters, List tags, FLAVersion flaVersion) { + String ret = ""; + if (matrix == null) { + matrix = new MATRIX(); + } + if (tag instanceof DefineButtonTag) { + DefineButtonTag bt = (DefineButtonTag) tag; + for (Tag t : tags) { + if (t instanceof DefineButtonCxformTag) { + DefineButtonCxformTag bcx = (DefineButtonCxformTag) t; + if (bcx.buttonId == bt.buttonId) { + colorTransform = bcx.buttonColorTransform; + } + } + } + } + + ret += "= FLAVersion.CS5_5.ordinal()) { + ret += " isVisible=\"false\""; + } + ret += ">"; + ret += ""; + ret += convertMatrix(matrix); + ret += ""; + ret += ""; + + if (backgroundColor != null) { + ret += " characterVariables, Map characterClasses, List nonLibraryShapes, String backgroundColor, List tags, HashMap characters, HashMap files, HashMap datfiles, FLAVersion flaVersion) { + + //TODO: Imported assets + //linkageImportForRS="true" linkageIdentifier="xxx" linkageURL="yyy.swf" + String ret = ""; + List media = new ArrayList<>(); + List symbols = new ArrayList<>(); + for (int ch : characters.keySet()) { + CharacterTag symbol = characters.get(ch); + if ((symbol instanceof ShapeTag) && nonLibraryShapes.contains(symbol.getCharacterId())) { + continue; //shapes with 1 ocurrence and single layer are not added to library + } + if ((symbol instanceof ShapeTag) || (symbol instanceof DefineSpriteTag) || (symbol instanceof ButtonTag)) { + String symbolStr = ""; + + symbolStr += ""; + symbolStr += ""; + + ButtonTag button = (ButtonTag) symbol; + List records = button.getRecords(); + String[] frames = {"", "", "", ""}; + + int maxDepth = 0; + for (BUTTONRECORD rec : records) { + if (rec.placeDepth > maxDepth) { + maxDepth = rec.placeDepth; + } + } + for (int i = maxDepth; i >= 1; i--) { + symbolStr += ""; + symbolStr += ""; + int lastFrame = 0; + loopframes: + for (int frame = 1; frame <= 4; frame++) { + for (BUTTONRECORD rec : records) { + if (rec.placeDepth == i) { + boolean ok = false; + switch (frame) { + case 1: + ok = rec.buttonStateUp; + break; + case 2: + ok = rec.buttonStateOver; + break; + case 3: + ok = rec.buttonStateDown; + break; + case 4: + ok = rec.buttonStateHitTest; + break; + } + if (!ok) { + continue; + } + CXFORMWITHALPHA colorTransformAlpha = null; + int blendMode = 0; + List filters = new ArrayList<>(); + if (button instanceof DefineButton2Tag) { + colorTransformAlpha = rec.colorTransform; + if (rec.buttonHasBlendMode) { + blendMode = rec.blendMode; + } + if (rec.buttonHasFilterList) { + filters = rec.filterList; + } + } + CharacterTag character = characters.get(rec.characterId); + MATRIX matrix = rec.placeMatrix; + String recCharStr = ""; + if (character instanceof TextTag) { + recCharStr = convertText(null, tags, (TextTag) character, matrix, filters, null); + } else if (character instanceof DefineVideoStreamTag) { + recCharStr = convertVideoInstance(null, matrix, (DefineVideoStreamTag) character, null); + } else { + recCharStr = convertSymbolInstance(null, matrix, colorTransformAlpha, false, blendMode, filters, true, null, null, characters.get(rec.characterId), characters, tags, flaVersion); + } + int duration = frame - lastFrame; + lastFrame = frame; + if (duration > 0) { + if (duration > 1) { + symbolStr += ""; + symbolStr += ""; + symbolStr += ""; + symbolStr += ""; + } + symbolStr += ""; + symbolStr += ""; + symbolStr += recCharStr; + symbolStr += ""; + symbolStr += ""; + } + } + } + } + symbolStr += ""; + symbolStr += ""; + } + symbolStr += ""; + symbolStr += ""; + } else if (symbol instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) symbol; + if (sprite.subTags.isEmpty()) { //probably AS2 class + continue; + } + symbolStr += convertTimeline(sprite.spriteId, nonLibraryShapes, backgroundColor, tags, sprite.getSubTags(), characters, "Symbol " + symbol.getCharacterId(), flaVersion, files); + } else if (symbol instanceof ShapeTag) { + itemIcon = "1"; + ShapeTag shape = (ShapeTag) symbol; + symbolStr += ""; + symbolStr += ""; + symbolStr += convertShape(characters, null, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, true); + symbolStr += ""; + symbolStr += ""; + } + symbolStr += ""; + symbolStr += ""; + symbolStr = prettyFormatXML(symbolStr); + String symbolFile = "Symbol " + symbol.getCharacterId() + ".xml"; + files.put(symbolFile, Utf8Helper.getBytes(symbolStr)); + String symbLinkStr = ""; + symbLinkStr += "= FLAVersion.CS5_5.ordinal()) { + symbLinkStr += " lastModified=\"" + getTimestamp() + "\""; + //TODO: itemID=\"518de416-00000341\" + } + symbLinkStr += "/>"; + symbols.add(symbLinkStr); + } else if (symbol instanceof ImageTag) { + ImageTag imageTag = (ImageTag) symbol; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SerializableImage image = imageTag.getImage(); + String format = imageTag.getImageFormat(); + try { + ImageIO.write(image.getBufferedImage(), format.toUpperCase(), baos); + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + String symbolFile = "bitmap" + symbol.getCharacterId() + "." + imageTag.getImageFormat(); + files.put(symbolFile, baos.toByteArray()); + String mediaLinkStr = "\n"; + media.add(mediaLinkStr); + + } else if ((symbol instanceof SoundStreamHeadTypeTag) || (symbol instanceof DefineSoundTag)) { + int soundFormat = 0; + int soundRate = 0; + boolean soundType = false; + boolean soundSize = false; + long soundSampleCount = 0; + byte[] soundData = new byte[0]; + int[] rateMap = {5, 11, 22, 44}; + String exportFormat = "flv"; + if (symbol instanceof SoundStreamHeadTypeTag) { + SoundStreamHeadTypeTag sstream = (SoundStreamHeadTypeTag) symbol; + soundFormat = sstream.getSoundFormatId(); + soundRate = sstream.getSoundRate(); + soundType = sstream.getSoundType(); + soundSize = sstream.getSoundSize(); + soundSampleCount = sstream.getSoundSampleCount(); + boolean found = false; + for (Tag t : tags) { + if (found && (t instanceof SoundStreamBlockTag)) { + SoundStreamBlockTag bl = (SoundStreamBlockTag) t; + soundData = bl.getData(); + break; + } + if (t == symbol) { + found = true; + } + } + } else if (symbol instanceof DefineSoundTag) { + DefineSoundTag sound = (DefineSoundTag) symbol; + soundFormat = sound.soundFormat; + soundRate = sound.soundRate; + soundType = sound.soundType; + soundData = sound.soundData; + soundSize = sound.soundSize; + soundSampleCount = sound.soundSampleCount; + } + int format = 0; + int bits = 0; + if ((soundFormat == SoundFormat.FORMAT_ADPCM) + || (soundFormat == SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN) + || (soundFormat == SoundFormat.FORMAT_UNCOMPRESSED_NATIVE_ENDIAN)) { + exportFormat = "wav"; + if (soundType) { //stereo + format += 1; + } + switch (soundRate) { + case 0: + format += 2; + break; + case 1: + format += 6; + break; + case 2: + format += 10; + break; + case 3: + format += 14; + break; + } + } + if (soundFormat == SoundFormat.FORMAT_SPEEX) { + bits = 18; + } + if (soundFormat == SoundFormat.FORMAT_ADPCM) { + SWFInputStream sis = new SWFInputStream(new ByteArrayInputStream(soundData), swf.version); + exportFormat = "wav"; + try { + int adpcmCodeSize = (int) sis.readUB(2); + bits = 2 + adpcmCodeSize; + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + } + if (soundFormat == SoundFormat.FORMAT_MP3) { + exportFormat = "mp3"; + if (!soundType) { //mono + format += 1; + } + format += 4; //quality best + try { + MP3SOUNDDATA s = new MP3SOUNDDATA(new ByteArrayInputStream(soundData), false); + //sis.readSI16(); + //MP3FRAME frame = new MP3FRAME(sis); + MP3FRAME frame = s.frames.get(0); + int bitRate = frame.getBitRate(); + + switch (bitRate) { + case 8: + bits = 6; + break; + case 16: + bits = 7; + break; + case 20: + bits = 8; + break; + case 24: + bits = 9; + break; + case 32: + bits = 10; + break; + case 48: + bits = 11; + break; + case 56: + bits = 12; + break; + case 64: + bits = 13; + break; + case 80: + bits = 14; + break; + case 112: + bits = 15; + break; + case 128: + bits = 16; + break; + case 160: + bits = 17; + break; + + } + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + } + SoundTag st = (SoundTag) symbol; + SoundFormat fmt = st.getSoundFormat(); + byte[] data = new byte[0]; + try { + data = new SoundExporter().exportSound(st, SoundExportMode.MP3_WAV); + } catch (IOException ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, null, ex); + } + + String symbolFile = "sound" + symbol.getCharacterId() + "." + exportFormat; + files.put(symbolFile, data); + String mediaLinkStr = ""; + //Use the dat file, otherwise it does not work + datfiles.put(datFileName, new byte[]{ //Magic numbers, if anybody knows why, please tell me + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xA0, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x78, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x59, (byte) 0x40, (byte) 0x18, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }); + } else { + files.put(symbolFile, data); + mediaLinkStr = " characters, List tags, SoundStreamHeadTypeTag soundStreamHead, StartSoundTag startSound, int frame, int duration, String actionScript, String elements, HashMap files) { + String ret = ""; + DefineSoundTag sound = null; + if (startSound != null) { + for (Tag t : tags) { + if (t instanceof DefineSoundTag) { + DefineSoundTag s = (DefineSoundTag) t; + if (s.soundId == startSound.soundId) { + sound = s; + break; + } + } + } + } + + ret += " 1) { + ret += " duration=\"" + duration + "\""; + } + if (shapeTween) { + ret += " tweenType=\"shape\" keyMode=\"" + KEY_MODE_SHAPE_TWEEN + "\""; + } else { + ret += " keyMode=\"" + KEY_MODE_NORMAL + "\""; + } + String soundEnvelopeStr = ""; + if (soundStreamHead != null && startSound == null) { + String soundName = "sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat(); + ret += " soundName=\"" + soundName + "\""; + ret += " soundSync=\"stream\""; + soundEnvelopeStr += ""; + soundEnvelopeStr += ""; + soundEnvelopeStr += ""; + } + if (startSound != null && sound != null) { + String soundName = "sound" + sound.soundId + "." + sound.getExportFormat(); + ret += " soundName=\"" + soundName + "\""; + if (startSound.soundInfo.hasInPoint) { + ret += " inPoint44=\"" + startSound.soundInfo.inPoint + "\""; + } + if (startSound.soundInfo.hasOutPoint) { + ret += " outPoint44=\"" + startSound.soundInfo.outPoint + "\""; + } + if (startSound.soundInfo.hasLoops) { + if (startSound.soundInfo.loopCount == 32767) { + ret += " soundLoopMode=\"loop\""; + } + ret += " soundLoop=\"" + startSound.soundInfo.loopCount + "\""; + } + + if (startSound.soundInfo.syncStop) { + ret += " soundSync=\"stop\""; + } else if (startSound.soundInfo.syncNoMultiple) { + ret += " soundSync=\"start\""; + } + soundEnvelopeStr += ""; + if (startSound.soundInfo.hasEnvelope) { + for (SOUNDENVELOPE env : startSound.soundInfo.envelopeRecords) { + soundEnvelopeStr += ""; + } + + if (startSound.soundInfo.envelopeRecords.length == 1 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 0) { + ret += " soundEffect=\"left channel\""; + } else if (startSound.soundInfo.envelopeRecords.length == 1 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768) { + ret += " soundEffect=\"right channel\""; + } else if (startSound.soundInfo.envelopeRecords.length == 2 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 0 + && startSound.soundInfo.envelopeRecords[1].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount + && startSound.soundInfo.envelopeRecords[1].rightLevel == 32768) { + ret += " soundEffect=\"fade left to right\""; + } else if (startSound.soundInfo.envelopeRecords.length == 2 + && startSound.soundInfo.envelopeRecords[0].leftLevel == 0 + && startSound.soundInfo.envelopeRecords[0].pos44 == 0 + && startSound.soundInfo.envelopeRecords[0].rightLevel == 32768 + && startSound.soundInfo.envelopeRecords[1].leftLevel == 32768 + && startSound.soundInfo.envelopeRecords[1].pos44 == sound.soundSampleCount + && startSound.soundInfo.envelopeRecords[1].rightLevel == 0) { + ret += " soundEffect=\"fade right to left\""; + } else { + ret += " soundEffect=\"custom\""; + } + //TODO: fade in, fade out + + } else { + soundEnvelopeStr += ""; + } + soundEnvelopeStr += ""; + } + ret += ">"; + + ret += soundEnvelopeStr; + if (!actionScript.isEmpty()) { + ret += ""; + } + ret += ""; + ret += elements; + ret += ""; + ret += ""; + return ret; + } + + private static String convertVideoInstance(String instanceName, MATRIX matrix, DefineVideoStreamTag video, CLIPACTIONS clipActions) { + String ret = " nonLibraryShapes, List tags, List timelineTags, HashMap characters, int depth, FLAVersion flaVersion, HashMap files) { + String ret = ""; + prevStr += ""; + int frame = -1; + String elements = ""; + String lastElements = ""; + + int duration = 1; + + CharacterTag character = null; + MATRIX matrix = null; + String instanceName = null; + ColorTransform colorTransForm = null; + boolean cacheAsBitmap = false; + int blendMode = 0; + List filters = new ArrayList<>(); + boolean isVisible = true; + RGBA backGroundColor = null; + CLIPACTIONS clipActions = null; + int characterId = -1; + int ratio = -1; + boolean shapeTween = false; + boolean lastShapeTween = false; + MorphShapeTag shapeTweener = null; + + for (Tag t : timelineTags) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + if (po.getDepth() == depth) { + int newCharId = po.getCharacterId(); + if (newCharId == -1) { + newCharId = characterId; + } + characterId = newCharId; + if (characters.containsKey(characterId)) { + character = characters.get(characterId); + if (po.flagMove()) { + MATRIX matrix2 = po.getMatrix(); + if (matrix2 != null) { + matrix = matrix2; + } + String instanceName2 = po.getInstanceName(); + if (instanceName2 != null) { + instanceName = instanceName2; + } + ColorTransform colorTransForm2 = po.getColorTransform(); + if (colorTransForm2 != null) { + colorTransForm = colorTransForm2; + } + + CLIPACTIONS clipActions2 = po.getClipActions(); + if (clipActions2 != null) { + clipActions = clipActions2; + } + if (po.cacheAsBitmap()) { + cacheAsBitmap = true; + } + int blendMode2 = po.getBlendMode(); + if (blendMode2 > 0) { + blendMode = blendMode2; + } + List filters2 = po.getFilters(); + if (filters2 != null) { + filters = filters2; + } + int ratio2 = po.getRatio(); + if (ratio2 > -1) { + ratio = ratio2; + } + } else { + matrix = po.getMatrix(); + instanceName = po.getInstanceName(); + colorTransForm = po.getColorTransform(); + cacheAsBitmap = po.cacheAsBitmap(); + blendMode = po.getBlendMode(); + filters = po.getFilters(); + ratio = po.getRatio(); + clipActions = po.getClipActions(); + } + + } + } + } + + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + if (rt.getDepth() == depth) { + if (shapeTween && character != null) { + MorphShapeTag m = (MorphShapeTag) character; + shapeTweener = m; + shapeTween = false; + } + character = null; + matrix = null; + instanceName = null; + colorTransForm = null; + cacheAsBitmap = false; + blendMode = 0; + filters = new ArrayList<>(); + isVisible = true; + backGroundColor = null; + characterId = -1; + clipActions = null; + } + } + + if (t instanceof ShowFrameTag) { + elements = ""; + + if ((character instanceof ShapeTag) && (nonLibraryShapes.contains(characterId) || shapeTweener != null)) { + ShapeTag shape = (ShapeTag) character; + elements += convertShape(characters, matrix, shape.getShapeNum(), shape.getShapes().shapeRecords, shape.getShapes().fillStyles, shape.getShapes().lineStyles, false, false); + shapeTween = false; + shapeTweener = null; + } else if (character != null) { + if (character instanceof MorphShapeTag) { + MorphShapeTag m = (MorphShapeTag) character; + elements += convertShape(characters, matrix, 3, m.getStartEdges().shapeRecords, m.getFillStyles().getStartFillStyles(), m.getLineStyles().getStartLineStyles(m.getShapeNum()), true, false); + shapeTween = true; + } else { + shapeTween = false; + if (character instanceof TextTag) { + elements += convertText(instanceName, tags, (TextTag) character, matrix, filters, clipActions); + } else if (character instanceof DefineVideoStreamTag) { + elements += convertVideoInstance(instanceName, matrix, (DefineVideoStreamTag) character, clipActions); + } else { + elements += convertSymbolInstance(instanceName, matrix, colorTransForm, cacheAsBitmap, blendMode, filters, isVisible, backGroundColor, clipActions, character, characters, tags, flaVersion); + } + } + } + + frame++; + if (!elements.equals(lastElements) && frame > 0) { + ret += convertFrame(lastShapeTween, characters, tags, null, null, frame - duration, duration, "", lastElements, files); + duration = 1; + } else if (frame == 0) { + duration = 1; + } else { + duration++; + } + + lastShapeTween = shapeTween; + lastElements = elements; + } + } + if (!lastElements.isEmpty()) { + frame++; + ret += convertFrame(lastShapeTween, characters, tags, null, null, (frame - duration < 0 ? 0 : frame - duration), duration, "", lastElements, files); + } + afterStr = "" + afterStr; + if (!ret.isEmpty()) { + ret = prevStr + ret + afterStr; + } + return ret; + } + + public static String convertFonts(List tags) { + String ret = ""; + for (Tag t : tags) { + if (t instanceof FontTag) { + FontTag font = (FontTag) t; + int fontId = font.getFontId(); + String fontName = null; + for (Tag t2 : tags) { + if (t2 instanceof DefineFontNameTag) { + if (((DefineFontNameTag) t2).fontId == fontId) { + fontName = ((DefineFontNameTag) t2).fontName; + } + } + } + if (fontName == null) { + fontName = font.getFontName(); + } + int fontStyle = font.getFontStyle(); + String installedFont; + if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { + fontName = new Font(installedFont, fontStyle, 10).getPSName(); + } + String embedRanges = ""; + + String fontChars = font.getCharacters(tags); + if ("".equals(fontChars)) { + continue; + } + String embeddedCharacters = fontChars; + embeddedCharacters = embeddedCharacters.replace("\u00A0", ""); //nonbreak space + embeddedCharacters = embeddedCharacters.replace(".", ""); + boolean hasAllRanges = false; + for (int r = 0; r < CharacterRanges.rangeCount(); r++) { + int codes[] = CharacterRanges.rangeCodes(r); + boolean hasAllInRange = true; + for (int i = 0; i < codes.length; i++) { + if (!fontChars.contains("" + (char) codes[i])) { + hasAllInRange = false; + break; + } + } + if (hasAllInRange) { + //remove all found characters + for (int i = 0; i < codes.length; i++) { + embeddedCharacters = embeddedCharacters.replace("" + (char) codes[i], ""); + } + if (!"".equals(embedRanges)) { + embedRanges += "|"; + } + embedRanges += (r + 1); + } else { + hasAllRanges = false; + } + } + if (hasAllRanges) { + embedRanges = "9999"; + } + ret += ""; + } + + } + + if (!"".equals(ret)) { + ret = "" + ret + ""; + } + + return ret; + } + + public static String convertActionScriptLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { + String ret = ""; + + String script = ""; + int duration = 0; + int frame = 0; + for (Tag t : tags) { + if (t instanceof DoInitActionTag) { + DoInitActionTag dia = (DoInitActionTag) t; + if (dia.spriteId == spriteId) { + script += convertActionScript(dia); + } + } + } + if (!script.isEmpty()) { + script = "#initclip\r\n" + script + "#endinitclip\r\n"; + } + for (Tag t : timeLineTags) { + if (t instanceof DoActionTag) { + DoActionTag da = (DoActionTag) t; + script += convertActionScript(da); + } + if (t instanceof ShowFrameTag) { + + if (script.isEmpty()) { + duration++; + } else { + if (duration > 0) { + ret += " 1) { + ret += " duration=\"" + duration + "\""; + } + ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; + ret += ""; + ret += ""; + ret += ""; + } + ret += ""; + ret += ""; + ret += ""; + ret += ""; + ret += ""; + script = ""; + duration = 0; + } + frame++; + } + } + if (!ret.isEmpty()) { + ret = "" + + "" + + ret + + "" + + ""; + } + return ret; + } + + public static String convertLabelsLayer(int spriteId, List tags, List timeLineTags, String backgroundColor) { + String ret = ""; + int duration = 0; + int frame = 0; + String frameLabel = ""; + boolean isAnchor = false; + for (Tag t : timeLineTags) { + if (t instanceof FrameLabelTag) { + FrameLabelTag fl = (FrameLabelTag) t; + frameLabel = fl.getLabelName(); + isAnchor = fl.isNamedAnchor(); + } + if (t instanceof ShowFrameTag) { + + if (frameLabel.isEmpty()) { + duration++; + } else { + if (duration > 0) { + ret += " 1) { + ret += " duration=\"" + duration + "\""; + } + ret += " keyMode=\"" + KEY_MODE_NORMAL + "\">"; + ret += ""; + ret += ""; + ret += ""; + } + ret += "" + + "" + + ret + + "" + + ""; + } + return ret; + } + + public static String convertSoundLayer(int layerIndex, String backgroundColor, HashMap characters, List tags, List timeLineTags, HashMap files) { + String ret = ""; + StartSoundTag lastStartSound = null; + SoundStreamHeadTypeTag lastSoundStreamHead = null; + StartSoundTag startSound = null; + SoundStreamHeadTypeTag soundStreamHead = null; + int duration = 1; + int frame = 0; + for (Tag t : timeLineTags) { + if (t instanceof StartSoundTag) { + startSound = (StartSoundTag) t; + + for (Tag ta : tags) { + if (ta instanceof DefineSoundTag) { + DefineSoundTag s = (DefineSoundTag) ta; + if (s.soundId == startSound.soundId) { + if (!files.containsKey("sound" + s.soundId + "." + s.getExportFormat())) { //Sound was not exported + startSound = null; //ignore + } + break; + } + } + } + + } + if (t instanceof SoundStreamHeadTypeTag) { + soundStreamHead = (SoundStreamHeadTypeTag) t; + if (!files.containsKey("sound" + soundStreamHead.getCharacterId() + "." + soundStreamHead.getExportFormat())) { //Sound was not exported + soundStreamHead = null; //ignore + } + } + if (t instanceof ShowFrameTag) { + if (soundStreamHead != null || startSound != null) { + if (lastSoundStreamHead != null || lastStartSound != null) { + ret += convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files); + } + frame += duration; + duration = 1; + lastSoundStreamHead = soundStreamHead; + lastStartSound = startSound; + soundStreamHead = null; + startSound = null; + } else { + duration++; + } + } + } + if (lastSoundStreamHead != null || lastStartSound != null) { + if (frame < 0) { + frame = 0; + duration = 1; + } + ret += convertFrame(false, characters, tags, lastSoundStreamHead, lastStartSound, frame, duration, "", "", files); + } + if (!ret.isEmpty()) { + ret = "" + + "" + ret + "" + + ""; + } + return ret; + } + + private static String randomOutlineColor() { + RGB outlineColor = new RGB(); + Random rnd = new Random(); + do { + outlineColor.red = rnd.nextInt(256); + outlineColor.green = rnd.nextInt(256); + outlineColor.blue = rnd.nextInt(256); + } while ((outlineColor.red + outlineColor.green + outlineColor.blue) / 3 < 128); + return outlineColor.toHexRGB(); + } + + public static String convertTimeline(int spriteId, List nonLibraryShapes, String backgroundColor, List tags, List timelineTags, HashMap characters, String name, FLAVersion flaVersion, HashMap files) { + String ret = ""; + ret += ""; + ret += ""; + + ret += convertLabelsLayer(spriteId, tags, timelineTags, backgroundColor); + ret += convertActionScriptLayer(spriteId, tags, timelineTags, backgroundColor); + + int layerCount = getLayerCount(timelineTags); + Stack parentLayers = new Stack<>(); + int index = 0; + for (int d = layerCount; d >= 1; d--, index++) { + for (Tag t : timelineTags) { + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + if (po.getClipDepth() == d) { + for (int m = po.getDepth(); m < po.getClipDepth(); m++) { + parentLayers.push(index); + } + + ret += " getCharacterClasses(List tags) { + Map ret = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.length; i++) { + if (!ret.containsKey(sc.tags[i]) && !ret.containsValue(sc.names[i])) { + ret.put(sc.tags[i], sc.names[i]); + } + } + } + } + return ret; + } + + private static Map getCharacterVariables(List tags) { + Map ret = new HashMap<>(); + for (Tag t : tags) { + if (t instanceof ExportAssetsTag) { + ExportAssetsTag ea = (ExportAssetsTag) t; + for (int i = 0; i < ea.tags.size(); i++) { + if (!ret.containsKey(ea.tags.get(i))) { + ret.put(ea.tags.get(i), ea.names.get(i)); + } + } + } + } + return ret; + } + + public static String convertText(String instanceName, List tags, TextTag tag, MATRIX m, List filters, CLIPACTIONS clipActions) { + String ret = ""; + + if (m == null) { + m = new MATRIX(); + } + Matrix matrix = new Matrix(m); + CSMTextSettingsTag csmts = null; + String filterStr = ""; + if (filters != null) { + filterStr += ""; + for (FILTER f : filters) { + filterStr += convertFilter(f); + } + filterStr += ""; + } + + for (Tag t : tags) { + if (t instanceof CSMTextSettingsTag) { + CSMTextSettingsTag c = (CSMTextSettingsTag) t; + if (c.textID == tag.getCharacterId()) { + csmts = c; + break; + } + } + } + String fontRenderingMode = "standard"; + String antiAlias = ""; + if (csmts != null) { + if (csmts.thickness == 0 & csmts.sharpness == 0) { + fontRenderingMode = null; + } else { + fontRenderingMode = "customThicknessSharpness"; + } + antiAlias = " antiAliasSharpness=\"" + doubleToString(csmts.sharpness) + "\" antiAliasThickness=\"" + doubleToString(csmts.thickness) + "\""; + } + String matStr = ""; + matStr += ""; + String left = ""; + RECT bounds = tag.getBounds(); + if ((tag instanceof DefineTextTag) || (tag instanceof DefineText2Tag)) { + MATRIX textMatrix = tag.getTextMatrix(); + left = " left=\"" + doubleToString((textMatrix.translateX) / SWF.unitDivisor) + "\""; + } + matStr += convertMatrix(matrix); + matStr += ""; + if ((tag instanceof DefineTextTag) || (tag instanceof DefineText2Tag)) { + List textRecords = new ArrayList<>(); + if (tag instanceof DefineTextTag) { + textRecords = ((DefineTextTag) tag).textRecords; + } else if (tag instanceof DefineText2Tag) { + textRecords = ((DefineText2Tag) tag).textRecords; + } + + looprec: + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasFont) { + for (Tag t : tags) { + if (t instanceof FontTag) { + FontTag ft = (FontTag) t; + if (ft.getFontId() == rec.fontId) { + if (ft.isSmall()) { + fontRenderingMode = "bitmap"; + break looprec; + } + } + } + } + } + } + + ret += " attrs = TextTag.getTextRecordsAttributes(textRecords, tags); + + ret += " width=\"" + tag.getBounds().getWidth() / 2 + "\" height=\"" + tag.getBounds().getHeight() + "\" autoExpand=\"true\" isSelectable=\"false\">"; + ret += matStr; + + ret += ""; + int fontId = -1; + FontTag font = null; + String fontName = null; + String psFontName = null; + int textHeight = -1; + RGB textColor = null; + RGBA textColorA = null; + boolean newline = false; + boolean firstRun = true; + @SuppressWarnings("unchecked") + List leftMargins = (List) attrs.get("allLeftMargins"); + @SuppressWarnings("unchecked") + List letterSpacings = (List) attrs.get("allLetterSpacings"); + for (int r = 0; r < textRecords.size(); r++) { + TEXTRECORD rec = textRecords.get(r); + if (rec.styleFlagsHasColor) { + if (tag instanceof DefineTextTag) { + textColor = rec.textColor; + } else { + textColorA = rec.textColorA; + } + } + if (rec.styleFlagsHasFont) { + fontId = rec.fontId; + fontName = null; + textHeight = rec.textHeight; + font = null; + for (Tag t : tags) { + if (t instanceof FontTag) { + if (((FontTag) t).getFontId() == fontId) { + font = (FontTag) t; + } + } + if (t instanceof DefineFontNameTag) { + if (((DefineFontNameTag) t).fontId == fontId) { + fontName = ((DefineFontNameTag) t).fontName; + } + } + } + if ((fontName == null) && (font != null)) { + fontName = font.getFontName(); + } + int fontStyle = 0; + if (font != null) { + fontStyle = font.getFontStyle(); + } + String installedFont; + if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { + psFontName = new Font(installedFont, fontStyle, 10).getPSName(); + } else { + psFontName = fontName; + } + } + newline = false; + if (!firstRun && rec.styleFlagsHasYOffset) { + newline = true; + } + firstRun = false; + if (font != null) { + ret += ""; + ret += "" + xmlString((newline ? "\r" : "") + rec.getText(font)) + ""; + ret += ""; + + ret += ""; + int leftMargin = -1; + int rightMargin = -1; + int indent = -1; + int lineSpacing = -1; + String alignment = null; + boolean italic = false; + boolean bold = false; + String fontFace = null; + int size = -1; + RGBA textColor = null; + if (det.hasTextColor) { + textColor = det.textColor; + } + if (det.hasFont) { + String fontName = null; + FontTag ft = null; + for (Tag u : tags) { + if (u instanceof DefineFontNameTag) { + if (((DefineFontNameTag) u).fontId == det.fontId) { + fontName = ((DefineFontNameTag) u).fontName; + } + } + if (u instanceof FontTag) { + if (((FontTag) u).getFontId() == det.fontId) { + ft = (FontTag) u; + } + } + if (fontName != null && ft != null) { + break; + } + } + if (ft != null) { + if (fontName == null) { + fontName = ft.getFontName(); + } + italic = ft.isItalic(); + bold = ft.isBold(); + size = det.fontHeight; + fontFace = fontName; + String installedFont = null; + if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { + fontName = installedFont; + fontFace = new Font(installedFont, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); + } + + } + } + if (det.hasLayout) { + leftMargin = det.leftMargin; + rightMargin = det.rightMargin; + indent = det.indent; + lineSpacing = det.leading; + String[] alignNames = {"left", "right", "center", "justify"}; + alignment = alignNames[det.align]; + } + ret += ""; + ret += " -1) { + ret += " indent=\"" + twipToPixel(indent) + "\""; + } + if (leftMargin > -1) { + ret += " leftMargin=\"" + twipToPixel(leftMargin) + "\""; + } + if (lineSpacing > -1) { + ret += " lineSpacing=\"" + twipToPixel(lineSpacing) + "\""; + } + if (rightMargin > -1) { + ret += " rightMargin=\"" + twipToPixel(rightMargin) + "\""; + } + if (size > -1) { + ret += " size=\"" + twipToPixel(size) + "\""; + ret += " bitmapSize=\"" + size + "\""; + } + if (fontFace != null) { + ret += " face=\"" + fontFace + "\""; + } + if (textColor != null) { + ret += " fillColor=\"" + textColor.toHexRGB() + "\" alpha=\"" + textColor.getAlphaFloat() + "\""; + } + ret += "/>"; + ret += ""; + ret += ""; + } + ret += ""; + ret += filterStr; + ret += ""; + } + return ret; + } + + public static void convertSWF(AbortRetryIgnoreHandler handler, SWF swf, String swfFileName, String outfile, boolean compressed, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion flaVersion) throws IOException { + + FileAttributesTag fa = null; + for (Tag t : swf.tags) { + if (t instanceof FileAttributesTag) { + fa = (FileAttributesTag) t; + } + } + + boolean useAS3 = false; + boolean useNetwork = false; + if (fa != null) { + useAS3 = fa.actionScript3; + useNetwork = fa.useNetwork; + } + + if (!useAS3 && flaVersion.minASVersion() > 2) { + throw new IllegalArgumentException("FLA version " + flaVersion + " does not support AS1/2"); + } + File file = new File(outfile); + File outDir = file.getParentFile(); + if (!outDir.exists()) { + if (!outDir.mkdirs()) { + if (!outDir.exists()) { + throw new IOException("cannot create directory " + outDir); + } + } + } + String domDocument = ""; + String baseName = swfFileName; + File f = new File(baseName); + baseName = f.getName(); + if (baseName.contains(".")) { + baseName = baseName.substring(0, baseName.lastIndexOf('.')); + } + final HashMap files = new HashMap<>(); + final HashMap datfiles = new HashMap<>(); + HashMap characters = getCharacters(swf.tags); + List nonLibraryShapes = getNonLibraryShapes(swf.tags, characters); + Map characterClasses = getCharacterClasses(swf.tags); + Map characterVariables = getCharacterVariables(swf.tags); + + String backgroundColor = "#ffffff"; + for (Tag t : swf.tags) { + if (t instanceof SetBackgroundColorTag) { + SetBackgroundColorTag sbc = (SetBackgroundColorTag) t; + backgroundColor = sbc.backgroundColor.toHexRGB(); + } + } + domDocument += " flaVersion.maxSwfVersion() ? flaVersion.maxSwfVersion() : swf.version; + String publishSettings = "\n" + + "\n" + + " \n" + + " 1\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 0\n" + + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 0\n" : " 0\n") + + " 0\n" + + " 0\n" + + " 1\n" + + " 1\n" + + " 1\n" + + " 1\n" + + " 1\n" + + " 1\n" + + " 1\n" + + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 1\n" : " 1\n") + + " 1\n" + + " 1\n" + + " " + baseName + ".swf\n" + + " " + baseName + ".exe\n" + + " " + baseName + ".app\n" + + " " + baseName + ".html\n" + + " " + baseName + ".gif\n" + + " " + baseName + ".jpg\n" + + " " + baseName + ".png\n" + + (flaVersion.ordinal() >= FLAVersion.CC.ordinal() ? " 1\n" : " 1\n") + + " " + baseName + ".smil\n" + + " " + baseName + ".swc\n" + + " \n" + + " \n" + + " 0\n" + + " 12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1;\n" + + " 1\n" + + " 1\n" + + " " + baseName + "_content.html\n" + + " " + baseName + "_alternate.html\n" + + " 0\n" + + " \n" + + " " + width + "\n" + + " " + height + "\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " 1\n" + + " 4\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 80\n" + + " 0\n" + + " 0\n" + + " 7\n" + + " 0\n" + + " 7\n" + + " 0\n" + + " " + flaSwfVersion + "\n" + + " " + FLAVersion.swfVersionToPlayer(flaSwfVersion) + "\n" + + " " + (useAS3 ? "3" : "2") + "\n" + + " 1\n" + + " \n" + + " .\n" + + " CONFIG::FLASH_AUTHORING="true";\n" + + " 0\n" + + " \n" + + " " + (swf.compressed ? "1" : "0") + "\n" + + " " + (swf.lzma ? "1" : "0") + "\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " " + (useNetwork ? 1 : 0) + "\n" + + " " + xmlString(characterClasses.containsKey(0) ? characterClasses.get(0) : "") + "\n" + + " 2\n" + + " 4\n" + + " 4096\n" + + " AS3\n" + + " 1\n" + + " 1\n" + + " 0\n" + + " 15\n" + + " 1\n" + + " 0\n" + + " 4102\n" + + " rsl\n" + + " wrap\n" + + " $(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf\n" + + ((flaVersion.ordinal() >= FLAVersion.CC.ordinal()) ? (" \n" + + " \n" + + " $(AppConfig)/ActionScript 3.0/libs\n" + + " merge\n" + + " \n" + + " \n" + + " $(FlexSDK)/frameworks/libs/flex.swc\n" + + " merge\n" + + " textLayout_2.0.0.232.swz\n" + + " \n" + + " \n" + + " $(FlexSDK)/frameworks/libs/core.swc\n" + + " merge\n" + + " textLayout_2.0.0.232.swz\n" + + " \n" + + " \n" + + " \n" + + " ") + : " \n" + + " \n" + + " $(AppConfig)/ActionScript 3.0/libs\n" + + " merge\n" + + " \n" + + " \n" + + " $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc\n" + + " rsl\n" + + " http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz\n" + + " http://fpdownload.adobe.com/pub/swz/crossdomain.xml\n" + + " textLayout_2.0.0.232.swz\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " $(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc\n" + + " \n" + + " http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz\n" + + " http://fpdownload.adobe.com/pub/swz/crossdomain.xml\n" + + " textLayout_2.0.0.232.swz\n" + + " \n" + + " \n") + + " \n" + + " \n" + + " " + width + "\n" + + " " + height + "\n" + + " 0\n" + + " 4718592\n" + + " 0\n" + + " 80\n" + + " 1\n" + + " \n" + + " \n" + + " 1\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 100000\n" + + " 1\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " \n" + + " \n" + + " " + width + "\n" + + " " + height + "\n" + + " 0\n" + + " 1\n" + + " 1\n" + + " \n" + + " 1\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " \n" + + " 128\n" + + " \n" + + " \n" + + " 255\n" + + " \n" + + " \n" + + " \n" + + " " + width + "\n" + + " " + height + "\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " \n" + + " \n" + + " \n" + + " 24-bit with Alpha\n" + + " 255\n" + + " \n" + + " \n" + + ((flaVersion.ordinal() >= FLAVersion.CC.ordinal()) ? "" + : (" \n" + + " " + width + "\n" + + " " + height + "\n" + + " 1\n" + + " 0\n" + + " \n" + + " \n" + + " 00000000\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 0\n" + + " 1\n" + + " \n")) + + "\n" + + ""; + + if (compressed) { + final String domDocumentF = domDocument; + final String publishSettingsF = publishSettings; + final String outfileF = outfile; + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outfileF))) { + out.putNextEntry(new ZipEntry("DOMDocument.xml")); + out.write(Utf8Helper.getBytes(domDocumentF)); + out.putNextEntry(new ZipEntry("PublishSettings.xml")); + out.write(Utf8Helper.getBytes(publishSettingsF)); + for (String fileName : files.keySet()) { + out.putNextEntry(new ZipEntry("LIBRARY/" + fileName)); + out.write(files.get(fileName)); + } + for (String fileName : datfiles.keySet()) { + out.putNextEntry(new ZipEntry("bin/" + fileName)); + out.write(datfiles.get(fileName)); + } + } + } + }, handler).run(); + + } else { + + if (!outDir.exists()) { + if (!outDir.mkdirs()) { + if (!outDir.exists()) { + throw new IOException("cannot create directory " + outDir); + } + } + } + writeFile(handler, Utf8Helper.getBytes(domDocument), outDir.getAbsolutePath() + File.separator + "DOMDocument.xml"); + writeFile(handler, Utf8Helper.getBytes(publishSettings), outDir.getAbsolutePath() + File.separator + "PublishSettings.xml"); + File libraryDir = new File(outDir.getAbsolutePath() + File.separator + "LIBRARY"); + libraryDir.mkdir(); + File binDir = new File(outDir.getAbsolutePath() + File.separator + "bin"); + binDir.mkdir(); + for (String fileName : files.keySet()) { + writeFile(handler, files.get(fileName), libraryDir.getAbsolutePath() + File.separator + fileName); + } + for (String fileName : datfiles.keySet()) { + writeFile(handler, datfiles.get(fileName), binDir.getAbsolutePath() + File.separator + fileName); + } + writeFile(handler, Utf8Helper.getBytes("PROXY-CS5"), outfile); + } + if (useAS3) { + try { + swf.exportActionScript(handler, outDir.getAbsolutePath(), ScriptExportMode.AS, parallel); + } catch (Exception ex) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error during ActionScript3 export", ex); + } + } + } + + private static int normHue(double h) { + if (Double.isNaN(h)) { + h = -Math.PI; + } + int ret = (int) Math.round(h * 180 / Math.PI); + while (ret > 180) { + ret -= 360; + } + while (ret < -180) { + ret += 360; + } + return ret; + } + + private static int normBrightness(double b) { + if (Double.isNaN(b)) { + b = -100; + } + return (int) Math.round(b); + } + + private static int normSaturation(double s) { + if (Double.isNaN(s)) { + return -100; + } else if (s == 1) { + return 0; + } else if (s - 1 < 0) { + return (int) Math.round((s - 1) * 100); + } else { + return (int) Math.round(((s - 1) * 100) / 3); + } + } + + private static int normContrast(double c) { + double[] ctrMap = { + // 0 1 2 3 4 5 6 7 8 9 + /*0*/0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, + /*1*/ 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, + /*2*/ 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, + /*3*/ 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, + /*4*/ 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, + /*5*/ 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, + /*6*/ 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, + /*7*/ 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, + /*8*/ 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, + /*9*/ 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, + /*10*/ 10.0}; + if (c == 127) { + return 0; + } else if (c - 127 < 0) { + return (int) Math.round((c - 127) * 100.0 / 127.0); + } else { + c = (c - 127) / 127; + for (int i = 0; i < ctrMap.length; i++) { + if (ctrMap[i] >= c) { + return i; + } + } + } + return ctrMap.length - 1; + } + + private static boolean sameDouble(double a, double b) { + final double EPSILON = 0.00001; + return a == b ? true : Math.abs(a - b) < EPSILON; + } + + public static String convertAdjustColorFilter(COLORMATRIXFILTER filter) { + float[][] matrix = new float[5][5]; + int index = 0; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 5; j++) { + matrix[j][i] = filter.matrix[index]; + index++; + } + } + double a11 = matrix[0][0], a12 = matrix[0][1], a13 = matrix[0][2], + a21 = matrix[1][0], a22 = matrix[1][1], a23 = matrix[1][2], + a31 = matrix[2][0], a32 = matrix[2][1], a33 = matrix[2][2], + a41 = matrix[4][0]; + + double b, c, h, s; + b = (24872168661075.0 * a11 * a11 - 151430415740925.0 * a12 + 341095051289483.0 * a12 * a12 - 15302094789450.0 * a13 + 82428663495404.0 * a12 * a13 + - 4592294873812.0 * a13 * a13 + 43556251470.0 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 + - 930 * a11 * (287 * a12 + 178 * a13)) + 2384730956550.0 * a12 * a41 + 240977870700.0 * a13 * a41 + - 685925220 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)) + * a41 + 465 * a11 * (466201717582.0 * a12 + 55756962908.0 * a13 + 764132175 * (-127 + 2 * a41))) + / (391687695450.0 * a11 * a11 + 5371575610858.0 * a12 * a12 + 1298089188904.0 * a12 * a13 - 72319604312.0 * a13 * a13 + + 1860 * a11 * (1835439833 * a12 + 219515602 * a13)); + c = (127 * (495225 * a11 + 1661845 * a12 + 167930 * a13 + + 478 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)))) + / 717495; + h = 2 * (Math.atan((-465 * a11 + 287 * a12 + 178 * a13 + Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 + - 930 * a11 * (287 * a12 + 178 * a13))) / (500. * (a12 - a13))) + Math.PI/*+ Pi*C(1)*/); + s = (1543 * (-103355550 * a11 * a11 - 158872382 * a12 * a12 + 190161784 * a12 * a13 - 134644952 * a13 * a13 + + 1661845 * a12 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 + - 930 * a11 * (287 * a12 + 178 * a13)) + 167930 * a13 + * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13)) + + 465 * a11 * (274372 * a12 + 170168 * a13 + 1065 * Math.sqrt(216225 * a11 * a11 + 332369 * a12 * a12 - 397828 * a12 * a13 + + 281684 * a13 * a13 - 930 * a11 * (287 * a12 + 178 * a13))))) + / (195843847725.0 * a11 * a11 + 2685787805429.0 * a12 * a12 + 649044594452.0 * a12 * a13 - 36159802156.0 * a13 * a13 + + 930 * a11 * (1835439833 * a12 + 219515602 * a13)); + + if (sameDouble(410 * a12, 1543 * a31) && sameDouble(410 * a12, 1543 * a32) && sameDouble(3047 * a12, 1543 * a21) && sameDouble(3047 * a12, 1543 * a23) + && sameDouble(a22, a11 + (1504 * a12) / 1543.) && sameDouble((1133 * a12) / 1543. + a33, a11) + /*&& (b == (195961 * a11 + 439039 * a12 + 1543 * (-127 + 2 * a41)) / (3086 * a11 + 6914 * a12)) + && (c == 127 * a11 + (439039 * a12) / 1543.) && (s == (1543 * (a11 - a12)) / (1543 * a11 + 3457 * a12)) + */ && !sameDouble(a11, a12) && !sameDouble(1543 * a11 + 3457 * a12, 0)) { + h = 0; + } + + return ""; + } + + private static String convertHTMLText(List tags, DefineEditTextTag det, String html) { + HTMLTextParser tparser = new HTMLTextParser(tags, det); + XMLReader parser; + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + parser = XMLReaderFactory.createXMLReader(); + parser.setContentHandler(tparser); + parser.setErrorHandler(tparser); + html = "\n" + + " \n" + + "]>" + html + ""; + try { + parser.parse(new InputSource(new StringReader(html))); + } catch (SAXParseException spe) { + System.out.println(html); + System.err.println(tparser.result); + } + } catch (SAXException | IOException e) { + Logger.getLogger(XFLConverter.class.getName()).log(Level.SEVERE, "Error while converting HTML", e); + } + return tparser.result; + } + + private static String xmlString(String s) { + return s.replace("<", "<").replace(">", ">").replace("\"", """).replace("&", "&").replace("\r\n", " ").replace("\r", " ").replace("\n", " "); + } + + private static double twipToPixel(double tw) { + return tw / 20.0; + } + + private static class HTMLTextParser extends DefaultHandler { + + public String result = ""; + private String fontFace = ""; + private String color = ""; + private int size = -1; + private int indent = -1; + private int leftMargin = -1; + private int rightMargin = -1; + private int lineSpacing = -1; + private double letterSpacing = -1; + private String alignment = null; + private final List tags; + private boolean bold = false; + private boolean italic = false; + private boolean underline = false; + private boolean li = false; + private String url = null; + private String target = null; + + @Override + public void error(SAXParseException e) throws SAXException { + } + + @Override + public void fatalError(SAXParseException e) throws SAXException { + } + + @Override + public void warning(SAXParseException e) throws SAXException { + } + + public HTMLTextParser(List tags, DefineEditTextTag det) { + if (det.hasFont) { + String fontName = null; + FontTag ft = null; + for (Tag u : tags) { + if (u instanceof DefineFontNameTag) { + if (((DefineFontNameTag) u).fontId == det.fontId) { + fontName = ((DefineFontNameTag) u).fontName; + } + } + if (u instanceof FontTag) { + if (((FontTag) u).getFontId() == det.fontId) { + ft = (FontTag) u; + } + } + if (fontName != null && ft != null) { + break; + } + } + if (ft != null) { + if (fontName == null) { + fontName = ft.getFontName(); + } + italic = ft.isItalic(); + bold = ft.isBold(); + size = det.fontHeight; + fontFace = new Font(fontName, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); + } + } + if (det.hasLayout) { + leftMargin = det.leftMargin; + rightMargin = det.rightMargin; + indent = det.indent; + lineSpacing = det.leading; + String[] alignNames = {"left", "right", "center", "justify"}; + alignment = alignNames[det.align]; + } + this.tags = tags; + } + + @Override + public void startDocument() throws SAXException { + } + + @Override + public void startElement(String uri, String localName, + String qName, Attributes attributes) throws SAXException { + switch (qName) { + case "a": + String href = attributes.getValue("href"); + if (href != null) { + url = href; + } + String t = attributes.getValue("target"); + if (t != null) { + target = t; + } + break; + case "b": + bold = true; + break; + case "i": + italic = true; + break; + case "u": + underline = true; + break; + case "li": + li = true; + break; + case "p": + String a = attributes.getValue("align"); + if (a != null) { + alignment = a; + } + if (!result.isEmpty()) { + putText("\r\n"); + } + break; + case "font": + //kerning ? + String ls = attributes.getValue("letterSpacing"); + if (ls != null) { + letterSpacing = Double.parseDouble(ls); + } + String s = attributes.getValue("size"); + if (s != null) { + size = Integer.parseInt(s); + } + String c = attributes.getValue("color"); + if (c != null) { + color = c; + } + String f = attributes.getValue("face"); + if (f != null) { + for (Tag tag : tags) { + if (tag instanceof FontTag) { + FontTag ft = (FontTag) tag; + String fontName = null; + if (f.equals(ft.getFontName())) { + for (Tag u : tags) { + if (u instanceof DefineFontNameTag) { + if (((DefineFontNameTag) u).fontId == ft.getFontId()) { + fontName = ((DefineFontNameTag) u).fontName; + } + } + } + if (fontName == null) { + fontName = ft.getFontName(); + } + String installedFont; + if ((installedFont = FontTag.isFontInstalled(fontName)) != null) { + fontFace = new Font(installedFont, (italic ? Font.ITALIC : 0) | (bold ? Font.BOLD : 0) | (!italic && !bold ? Font.PLAIN : 0), size < 0 ? 10 : size).getPSName(); + } else { + fontFace = fontName; + } + break; + } + } + } + } + break; + } + } + + @Override + public void endElement(String uri, String localName, + String qName) throws SAXException { + if (qName.equals("a")) { + url = null; + target = null; + } + if (qName.equals("b")) { + bold = false; + } + if (qName.equals("i")) { + italic = false; + } + if (qName.equals("u")) { + underline = false; + } + if (qName.equals("li")) { + li = false; + } + } + + private void putText(String txt) { + + result += ""; + result += "" + xmlString(txt) + ""; + result += ""; + result += " -1) { + result += " indent=\"" + twipToPixel(indent) + "\""; + } + if (leftMargin > -1) { + result += " leftMargin=\"" + twipToPixel(leftMargin) + "\""; + } + if (letterSpacing > -1) { + result += " letterSpacing=\"" + letterSpacing + "\""; + } + if (lineSpacing > -1) { + result += " lineSpacing=\"" + twipToPixel(lineSpacing) + "\""; + } + if (rightMargin > -1) { + result += " rightMargin=\"" + twipToPixel(rightMargin) + "\""; + } + if (size > -1) { + result += " size=\"" + size + "\""; + result += " bitmapSize=\"" + (size * 20) + "\""; + } + if (fontFace != null) { + result += " face=\"" + fontFace + "\""; + } + if (color != null) { + result += " fillColor=\"" + color + "\""; + } + if (url != null) { + result += " url=\"" + url + "\""; + } + if (target != null) { + result += " target=\"" + target + "\""; + } + result += "/>"; + result += ""; + result += ""; + } + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + putText(new String(ch, start, length)); + + } + + @Override + public void endDocument() { + if (this.result.isEmpty()) { + putText(""); + } + } + } +} diff --git a/src/com/jpexs/decompiler/graph/TypeItem.java b/src/com/jpexs/decompiler/graph/TypeItem.java index 5aa4dfb26..8ae8fcfe1 100644 --- a/src/com/jpexs/decompiler/graph/TypeItem.java +++ b/src/com/jpexs/decompiler/graph/TypeItem.java @@ -1,106 +1,105 @@ -/* - * Copyright (C) 2014 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.graph; - -import com.jpexs.decompiler.flash.SourceGeneratorLocalData; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.decompiler.graph.model.UnboundedTypeItem; -import com.jpexs.helpers.Helper; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * - * @author JPEXS - */ -public class TypeItem extends GraphTargetItem { - - public static TypeItem BOOLEAN = new TypeItem("Boolean"); - public static TypeItem STRING = new TypeItem("String"); - public static TypeItem ARRAY = new TypeItem("Array"); - public static UnboundedTypeItem UNBOUNDED = new UnboundedTypeItem(); - - public String fullTypeName; - - public TypeItem(String fullTypeName) { - this(fullTypeName, new ArrayList()); - } - - public TypeItem(String fullTypeName, List subtypes) { - super(null, NOPRECEDENCE); - this.fullTypeName = fullTypeName; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 83 * hash + Objects.hashCode(this.fullTypeName); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final TypeItem other = (TypeItem) obj; - if (!Objects.equals(this.fullTypeName, other.fullTypeName)) { - return false; - } - return true; - } - - @Override - public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { - if(localData.fullyQualifiedNames.contains(fullTypeName)){ - writer.append(fullTypeName); - }else{ - String simpleName = fullTypeName; - if(simpleName.contains(".")){ - simpleName = simpleName.substring(simpleName.lastIndexOf(".")+1); - } - writer.append(simpleName); - } - - return writer; - } - - @Override - public GraphTargetItem returnType() { - return this; - } - - @Override - public boolean hasReturnValue() { - return true; - } - - @Override - public String toString() { - return fullTypeName; - } - - @Override - public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { - return generator.generate(localData, this); - } -} +/* + * Copyright (C) 2014 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.graph; + +import com.jpexs.decompiler.flash.SourceGeneratorLocalData; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.decompiler.graph.model.UnboundedTypeItem; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * + * @author JPEXS + */ +public class TypeItem extends GraphTargetItem { + + public static TypeItem BOOLEAN = new TypeItem("Boolean"); + public static TypeItem STRING = new TypeItem("String"); + public static TypeItem ARRAY = new TypeItem("Array"); + public static UnboundedTypeItem UNBOUNDED = new UnboundedTypeItem(); + + public String fullTypeName; + + public TypeItem(String fullTypeName) { + this(fullTypeName, new ArrayList()); + } + + public TypeItem(String fullTypeName, List subtypes) { + super(null, NOPRECEDENCE); + this.fullTypeName = fullTypeName; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 83 * hash + Objects.hashCode(this.fullTypeName); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TypeItem other = (TypeItem) obj; + if (!Objects.equals(this.fullTypeName, other.fullTypeName)) { + return false; + } + return true; + } + + @Override + public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) throws InterruptedException { + if (localData.fullyQualifiedNames.contains(fullTypeName)) { + writer.append(fullTypeName); + } else { + String simpleName = fullTypeName; + if (simpleName.contains(".")) { + simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1); + } + writer.append(simpleName); + } + + return writer; + } + + @Override + public GraphTargetItem returnType() { + return this; + } + + @Override + public boolean hasReturnValue() { + return true; + } + + @Override + public String toString() { + return fullTypeName; + } + + @Override + public List toSource(SourceGeneratorLocalData localData, SourceGenerator generator) throws CompilationException { + return generator.generate(localData, this); + } +} diff --git a/src/com/jpexs/helpers/Helper.java b/src/com/jpexs/helpers/Helper.java index 69b97b241..d0546c73a 100644 --- a/src/com/jpexs/helpers/Helper.java +++ b/src/com/jpexs/helpers/Helper.java @@ -1,867 +1,868 @@ -/* - * Copyright (C) 2010-2014 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.helpers; - -import com.jpexs.decompiler.flash.AppStrings; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.Component; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.Area; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Field; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.Collection; -import java.util.List; -import java.util.Scanner; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Class with helper method - * - * @author JPEXS, Paolo Cancedda - */ -public class Helper { - - public static String newLine = System.getProperty("line.separator"); - - /** - * Converts array of int values to string - * - * @param array Array of int values - * @return String representation of the array - */ - public static String intArrToString(int[] array) { - String s = "["; - for (int i = 0; i < array.length; i++) { - if (i > 0) { - s += ","; - } - s += array[i]; - } - s += "]"; - return s; - } - - /** - * Converts array of byte values to string - * - * @param array Array of byte values - * @return String representation of the array - */ - public static String byteArrToString(byte[] array) { - String s = "["; - for (int i = 0; i < array.length; i++) { - if (i > 0) { - s += " "; - } - s += padZeros(Integer.toHexString(array[i] & 0xff), 2); - } - s += "]"; - return s; - } - - /** - * Adds zeros to beginning of the number to fill specified length. Returns - * as string - * - * @param number Number as string - * @param length Length of new string - * @return Number with added zeros - */ - public static String padZeros(String number, int length) { - int count = length - number.length(); - for (int i = 0; i < count; i++) { - number = "0" + number; - } - return number; - } - - /** - * Formats specified address to four numbers xxxx - * - * @param number Address to format - * @return String representation of the address - */ - public static String formatAddress(long number) { - if (Configuration.decimalAddress.get()) { - return padZeros(Long.toString(number), 4); - } - return padZeros(Long.toHexString(number), 4); - } - - /** - * Adds space to text to fill specified width - * - * @param text Text to add spaces to - * @param width New width - * @return Text with appended spaces - */ - public static String padSpaceRight(String text, int width) { - int oldLen = text.length(); - for (int i = oldLen; i < width; i++) { - text += " "; - } - return text; - } - - /** - * Escapes string by adding backslashes - * - * @param s String to escape - * @return Escaped string - */ - public static String escapeString(String s) { - String ret = ""; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\n') { - ret += "\\n"; - } else if (c == '\r') { - ret += "\\r"; - } else if (c == '\t') { - ret += "\\t"; - } else if (c == '\b') { - ret += "\\b"; - } else if (c == '\t') { - ret += "\\t"; - } else if (c == '\f') { - ret += "\\f"; - } else if (c == '\\') { - ret += "\\\\"; - } else if (c == '"') { - ret += "\\\""; - } else if (c == '\'') { - ret += "\\'"; - } else { - ret += c; - } - - } - return ret; - } - - public static String getValidHtmlId(String text) { - // ID and NAME tokens must begin with a letter ([A-Za-z]) and - // may be followed by any number of letters, digits ([0-9]), - // hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < text.length(); i++) { - char ch = text.charAt(i); - if ((ch >= 'a' && ch <= 'z') - || (ch >= 'A' && ch <= 'Z') - || (i > 0 && ((ch >= '0' && ch <= '9') - || ch == '-' || ch == '_' || ch == ':' || ch == '.'))) { - sb.append(ch); - } else { - sb.append('_'); - } - } - return sb.toString(); - } - - private final static String SPACES12 = " "; - private final static String ZEROS8 = "00000000"; - - public static String formatHex(int value, int width) { - StringBuilder sb = new StringBuilder(); - sb.append(Integer.toHexString(value)); - if (width > sb.length()) { - sb.insert(0, ZEROS8, 0, width - sb.length()); - } - return sb.toString(); - } - - public static String formatInt(int value, int width) { - StringBuilder sb = new StringBuilder(); - sb.append(value); - if (width > sb.length()) { - sb.insert(0, SPACES12, 0, width - sb.length()); - } - return sb.toString(); - } - - public static String indent(int level, String ss, String indentStr) { - StringBuilder sb = new StringBuilder(); - for (int ii = 0; ii < level; ii++) { - sb.append(indentStr); - } - sb.append(ss); - return sb.toString(); - } - - public static String indentRows(int level, String ss, String indentStr) { - StringBuilder sb = new StringBuilder(); - for (int ii = 0; ii < level; ii++) { - sb.append(indentStr); - } - ss = ss.replaceAll("(\r\n|\r|\n)", "\r\n"); - ss = "\r\n" + ss; - String repl = "\r\n" + sb.toString(); - ss = ss.replace("\r\n", repl); - if (ss.endsWith(repl)) { - ss = ss.substring(0, ss.length() - sb.toString().length()); - } - ss = ss.substring(2); - return ss; - } - - public static String unindentRows(int prefixLineCount, int level, String text) { - StringBuilder sb = new StringBuilder(); - Scanner scanner = new Scanner(text); - String indentStr = ""; - for (int i = 0; i < level; i++) { - indentStr += Configuration.getCodeFormatting().indentString; - } - int indentLength = indentStr.length(); - for (int i = 0; i < prefixLineCount; i++) { - scanner.nextLine(); // ignore line - } - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith(indentStr)) { - sb.append(line.substring(indentLength)).append(Configuration.getCodeFormatting().newLineChars); - } else { - return sb.toString(); - } - } - return sb.toString(); - } - - public static int getLineCount(String s) { - if (s.endsWith("\r\n")) { - s = s.substring(0, s.length() - 2); - } else if (s.endsWith("\r")) { - s = s.substring(0, s.length() - 1); - } else if (s.endsWith("\n")) { - s = s.substring(0, s.length() - 1); - } - String[] parts = s.split("(\r\n|\r|\n)"); - return parts.length; - } - - public static String padZeros(long number, int length) { - String ret = "" + number; - while (ret.length() < length) { - ret = "0" + ret; - } - return ret; - } - - public static String bytesToHexString(byte[] bytes) { - return bytesToHexString(bytes, 0); - } - - public static String bytesToHexString(byte[] bytes, int start) { - StringBuilder sb = new StringBuilder(); - if (start < bytes.length) { - for (int ii = start; ii < bytes.length; ii++) { - sb.append(formatHex(bytes[ii] & 0xff, 2)); - sb.append(' '); - } - sb.setLength(sb.length() - 1); - } - return sb.toString(); - } - - public static String bytesToHexString(int maxByteCountInString, byte[] bytes, int start) { - if (bytes.length - start <= maxByteCountInString) { - return bytesToHexString(bytes, start); - } - byte[] trailingBytes = new byte[maxByteCountInString / 2]; - byte[] headingBytes = new byte[maxByteCountInString - trailingBytes.length]; - System.arraycopy(bytes, start, headingBytes, 0, headingBytes.length); - int startOfTrailingBytes = bytes.length - trailingBytes.length; - System.arraycopy(bytes, startOfTrailingBytes, trailingBytes, 0, trailingBytes.length); - StringBuilder sb = new StringBuilder(); - sb.append(bytesToHexString(headingBytes, 0)); - if (trailingBytes.length > 0) { - sb.append(" ... "); - sb.append(bytesToHexString(trailingBytes, 0)); - } - return sb.toString(); - } - - public static String format(String str, int len) { - if (len <= str.length()) { - return str; - } - StringBuilder sb = new StringBuilder(str); - for (int ii = str.length(); ii < len; ii++) { - sb.append(' '); - } - return sb.toString(); - } - - public static String joinStrings(List arr, String glue) { - String ret = ""; - boolean first = true; - for (String s : arr) { - if (!first) { - ret += glue; - } else { - first = false; - } - ret += s; - } - return ret; - } - - public static String joinStrings(String[] arr, String glue) { - String ret = ""; - boolean first = true; - for (String s : arr) { - if (!first) { - ret += glue; - } else { - first = false; - } - ret += s; - } - return ret; - } - - public static String joinStrings(List arr, String formatString, String glue) { - String ret = ""; - boolean first = true; - for (String s : arr) { - if (!first) { - ret += glue; - } else { - first = false; - } - ret += String.format(formatString, s); - } - return ret; - } - - public static String joinStrings(String[] arr, String formatString, String glue) { - String ret = ""; - boolean first = true; - for (String s : arr) { - if (!first) { - ret += glue; - } else { - first = false; - } - ret += String.format(formatString, s); - } - return ret; - } - - @SuppressWarnings("unchecked") - public static E deepCopy(E o) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { - oos.writeObject(o); - oos.flush(); - } - E copy; - try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { - copy = (E) ois.readObject(); - } - return copy; - } catch (IOException | ClassNotFoundException ex) { - Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, "Copy error", ex); - return null; - } - } - - public static List toList(Object... rest) { - List ret = new ArrayList<>(); - ret.addAll(Arrays.asList(rest)); - return ret; - } - - public static ByteArrayInputStream getInputStream(byte[] - ... data) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - try { - for (byte[] d : data) { - baos.write(d); - } - } catch (IOException iex) { - } - return new ByteArrayInputStream(baos.toByteArray()); - } - - public static byte[] readFile(String... file) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (String f : file) { - try (FileInputStream fis = new FileInputStream(f)) { - byte[] buf = new byte[4096]; - int cnt = 0; - while ((cnt = fis.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - } catch (IOException ex) { - Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); - } - } - return baos.toByteArray(); - } - - public static String readTextFile(String... file) { - return new String(readFile(file), Utf8Helper.charset); - } - - public static byte[] readStream(InputStream is) { - if (is instanceof MemoryInputStream) { - return ((MemoryInputStream) is).getAllRead(); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - byte[] buf = new byte[4096]; - int cnt = 0; - while ((cnt = is.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - } catch (IOException ex) { - Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); - } - return baos.toByteArray(); - } - - public static void writeFile(String file, byte[] - ... data) { - try (FileOutputStream fos = new FileOutputStream(file)) { - for (byte[] d : data) { - fos.write(d); - } - } catch (IOException ex) { - //ignore - } - } - - public static String stackToString(Stack stack, LocalData localData) throws InterruptedException { - String ret = "["; - for (int i = stack.size() - 1; i >= 0; i--) { - if (i < stack.size() - 1) { - ret += ", "; - } - ret += stack.get(i).toString(localData); - } - ret += "]"; - return ret; - } - - public static File fixDialogFile(File f) { - Pattern pat = Pattern.compile("\"([^\"]+)\""); - String name = f.getAbsolutePath(); - Matcher m = pat.matcher(name); - if (m.find()) { - f = new File(m.group(1)); - } - return f; - } - private static final BitSet fileNameInvalidChars; - private static final List invalidFilenamesParts; - - static { - BitSet toEncode = new BitSet(256); - - for (int i = 0; i < 32; i++) { - toEncode.set(i); - } - - toEncode.set('\\'); - toEncode.set('/'); - toEncode.set(':'); - toEncode.set('*'); - toEncode.set('?'); - toEncode.set('"'); - toEncode.set('<'); - toEncode.set('>'); - toEncode.set('|'); - - fileNameInvalidChars = toEncode; - - //windows reserved filenames: - invalidFilenamesParts = new ArrayList<>(); - invalidFilenamesParts.add("CON"); - invalidFilenamesParts.add("PRN"); - invalidFilenamesParts.add("AUX"); - invalidFilenamesParts.add("CLOCK$"); - invalidFilenamesParts.add("NUL"); - for (int i = 1; i <= 9; i++) { - invalidFilenamesParts.add("COM" + i); - invalidFilenamesParts.add("LPT" + i); - } - } - - public static String makeFileName(String str) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < str.length(); i++) { - int ch = (int) str.charAt(i); - if (ch < 256 && fileNameInvalidChars.get(ch)) { - sb.append("%").append(String.format("%02X", ch)); - } else { - sb.append((char) ch); - } - } - str = sb.toString(); - if (str.endsWith(" ")) { - str = str.substring(0, str.length() - 1) + "%20"; - } - if (str.endsWith(".")) { - str = str.substring(0, str.length() - 1) + "%2E"; - } - str = "." + str + "."; - for (String inv : invalidFilenamesParts) { - str = Pattern.compile("\\." + Pattern.quote(inv) + "\\.", Pattern.CASE_INSENSITIVE).matcher(str).replaceAll("._" + inv + "."); - } - str = str.substring(1, str.length() - 1); //remove dots - if (str.isEmpty()) { - str = "unnamed"; - } - return str; - } - - public static String strToHex(String s) { - byte[] bs = Utf8Helper.getBytes(s); - String sn = ""; - for (int i = 0; i < bs.length; i++) { - sn += "0x" + Integer.toHexString(bs[i] & 0xff) + " "; - } - return sn; - } - - public static void emptyObject(Object obj) { - Field[] fields = obj.getClass().getDeclaredFields(); - for (Field f : fields) { - try { - f.setAccessible(true); - Object v = f.get(obj); - if (v != null) { - if (v instanceof Collection) { - ((Collection) v).clear(); - } - if (v instanceof Component) { - if (((Component) v).getParent() != null) { - ((Component) v).getParent().remove((Component) v); - } - } - if (v instanceof Freed) { - Freed freed = ((Freed) v); - if (!freed.isFreeing()) { - ((Freed) v).free(); - } - } - f.set(obj, null); - } - } catch (UnsupportedOperationException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { - } - } - } - - public static String formatTimeSec(long timeMs) { - long timeS = timeMs / 1000; - timeMs %= 1000; - long timeM = timeS / 60; - timeS %= 60; - long timeH = timeM / 60; - timeM %= 60; - String timeStr = ""; - if (timeH > 0) { - timeStr += Helper.padZeros(timeH, 2) + ":"; - } - timeStr += Helper.padZeros(timeM, 2) + ":"; - timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3); - return timeStr; - } - - public static String formatFileSize(long fileSizeLong) { - double fileSize = fileSizeLong; - if (fileSize < 1024) { - return String.format("%d bytes", fileSizeLong); - } - fileSize /= 1024; - if (fileSize < 1024) { - return String.format("%.2f KB", fileSize); - } - fileSize /= 1024; - return String.format("%.2f MB", fileSize); - } - - public static void freeMem() { - Cache.clearAll(); - System.gc(); - } - - public static String formatTimeToText(int timeS) { - long timeM = timeS / 60; - timeS %= 60; - long timeH = timeM / 60; - timeM %= 60; - - String timeStr = ""; - String strAnd = AppStrings.translate("timeFormat.and"); - String strHour = AppStrings.translate("timeFormat.hour"); - String strHours = AppStrings.translate("timeFormat.hours"); - String strMinute = AppStrings.translate("timeFormat.minute"); - String strMinutes = AppStrings.translate("timeFormat.minutes"); - String strSecond = AppStrings.translate("timeFormat.second"); - String strSeconds = AppStrings.translate("timeFormat.seconds"); - - if (timeH > 0) { - timeStr += timeH + " " + (timeH > 1 ? strHours : strHour); - } - if (timeM > 0) { - if (timeStr.length() > 0) { - timeStr += " " + strAnd + " "; - } - timeStr += timeM + " " + (timeM > 1 ? strMinutes : strMinute); - } - if (timeS > 0) { - if (timeStr.length() > 0) { - timeStr += " " + strAnd + " "; - } - timeStr += timeS + " " + (timeS > 1 ? strSeconds : strSecond); - } - - // (currently) used only in log, so no localization is required - return timeStr; - } - - public static GraphTextWriter byteArrayToHexWithHeader(GraphTextWriter writer, byte[] data) { - writer.appendNoHilight("#hexdata").newLine().newLine(); - return byteArrayToHex(writer, data, 8, 8, -1, false, false); - } - - public static GraphTextWriter byteArrayToHex(GraphTextWriter writer, byte[] data, int bytesPerRow, int groupSize, int limit, boolean addChars, boolean showAddress) { - - /* // hex data from decompiled actions - Scanner scanner = new Scanner(srcWithHex); - while (scanner.hasNextLine()) { - String line = scanner.nextLine().trim(); - if (line.startsWith(";")) { - result.append(line.substring(1).trim()).append(nl); - } else { - result.append(";").append(line).append(nl); - } - }*/ - int length = data.length; - if (limit != -1 && length > limit) { - length = limit; - } - - int rowCount = length / bytesPerRow; - if (length % bytesPerRow > 0) { - rowCount++; - } - - long address = 0; - for (int row = 0; row < rowCount; row++) { - if (row > 0) { - writer.newLine(); - } - - if (showAddress) { - writer.appendNoHilight("0x" + String.format("%08x ", address)); - } - - for (int i = 0; i < bytesPerRow; i++) { - int idx = row * bytesPerRow + i; - if (length > idx) { - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - writer.appendNoHilight(String.format("%02x ", data[idx])); - } else { - if (addChars) { - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - writer.appendNoHilight(" "); - } - } - address += bytesPerRow; - } - - if (addChars) { - writer.appendNoHilight(" "); - for (int i = 0; i < bytesPerRow; i++) { - int idx = row * bytesPerRow + i; - if (length == idx) { - break; - } - if (i > 0 && i % groupSize == 0) { - writer.appendNoHilight(" "); - } - byte ch = data[idx]; - if (ch >= 0 && ch < 32) { - ch = '.'; - } - writer.appendNoHilight((char) ch + ""); - } - } - } - - writer.newLine(); - if (limit != -1 && data.length > limit) { - writer.appendNoHilight(AppStrings.translate("binaryData.truncateWarning").replace("%count%", Integer.toString(data.length - limit))); - } - - return writer; - } - - public static String byteArrayToHex(byte[] data, int bytesPerRow, int limit) { - HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); - byteArrayToHex(writer, data, bytesPerRow, 8, limit, true, true); - return writer.toString(); - } - - public static byte[] getBytesFromHexaText(String text) { - Scanner scanner = new Scanner(text); - scanner.nextLine(); // ignore first line - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while (scanner.hasNextLine()) { - String line = scanner.nextLine().trim(); - if (line.startsWith(";")) { - continue; - } - line = line.replace(" ", ""); - for (int i = 0; i < line.length() / 2; i++) { - String hexStr = line.substring(i * 2, (i + 1) * 2); - byte b = (byte) Integer.parseInt(hexStr, 16); - baos.write(b); - } - } - byte[] data = baos.toByteArray(); - return data; - } - - public static boolean contains(int[] array, int value) { - for (int i : array) { - if (i == value) { - return true; - } - } - return false; - } - - public static void saveStream(InputStream is, File output) throws IOException { - byte[] buf = new byte[1024]; - int cnt; - try (FileOutputStream fos = new FileOutputStream(output)) { - while ((cnt = is.read(buf)) > 0) { - fos.write(buf, 0, cnt); - fos.flush(); - } - } - } - - public static void appendTimeoutComment(GraphTextWriter writer, int timeout) { - writer.appendNoHilight("/*").newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError")).newLine(); - writer.appendNoHilight(" * ").appendNoHilight(MessageFormat.format(AppStrings.translate("decompilationError.timeout"), Helper.formatTimeToText(timeout))).newLine(); - writer.appendNoHilight(" */").newLine(); - writer.appendNoHilight("throw new IllegalOperationError(\""). - appendNoHilight(AppStrings.translate("decompilationError.timeout.description")). - appendNoHilight("\");").newLine(); - } - - public static void appendErrorComment(GraphTextWriter writer, Throwable ex) { - writer.appendNoHilight("/*").newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError")).newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError.obfuscated")).newLine(); - writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError.errorType")). - appendNoHilight(": " + ex.getClass().getSimpleName()).newLine(); - writer.appendNoHilight(" */").newLine(); - writer.appendNoHilight("throw new IllegalOperationError(\""). - appendNoHilight(AppStrings.translate("decompilationError.error.description")). - appendNoHilight("\");").newLine(); - } - - public static String escapeHTML(String text) { - String from[] = new String[]{"&", "<", ">", "\"", "'", "/"}; - String to[] = new String[]{"&", "<", ">", """, "'", "/"}; - for (int i = 0; i < from.length; i++) { - text = text.replace(from[i], to[i]); - } - return text; - } - - public static Shape imageToShape(BufferedImage image) { - Area area = new Area(); - Rectangle rectangle = new Rectangle(); - int y1, y2; - for (int x = 0; x < image.getWidth(); x++) { - y1 = Integer.MAX_VALUE; - y2 = -1; - for (int y = 0; y < image.getHeight(); y++) { - int rgb = image.getRGB(x, y); - rgb = rgb >>> 24; - if (rgb > 0) { - if (y1 == Integer.MAX_VALUE) { - y1 = y; - y2 = y; - } - if (y > (y2 + 1)) { - rectangle.setBounds(x, y1, 1, y2 - y1 + 1); - area.add(new Area(rectangle)); - y1 = y; - } - y2 = y; - } - } - if ((y2 - y1) >= 0) { - rectangle.setBounds(x, y1, 1, y2 - y1 + 1); - area.add(new Area(rectangle)); - } - } - return area; - } - - /** - * Formats double value (removes .0 from end) - * @param d - * @return String - */ - public static String doubleStr(double d){ - String ret= ""+d; - if(ret.endsWith(".0")){ - ret = ret.substring(0,ret.length()-2); - } - return ret; - } -} +/* + * Copyright (C) 2010-2014 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.helpers; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Area; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.List; +import java.util.Scanner; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Class with helper method + * + * @author JPEXS, Paolo Cancedda + */ +public class Helper { + + public static String newLine = System.getProperty("line.separator"); + + /** + * Converts array of int values to string + * + * @param array Array of int values + * @return String representation of the array + */ + public static String intArrToString(int[] array) { + String s = "["; + for (int i = 0; i < array.length; i++) { + if (i > 0) { + s += ","; + } + s += array[i]; + } + s += "]"; + return s; + } + + /** + * Converts array of byte values to string + * + * @param array Array of byte values + * @return String representation of the array + */ + public static String byteArrToString(byte[] array) { + String s = "["; + for (int i = 0; i < array.length; i++) { + if (i > 0) { + s += " "; + } + s += padZeros(Integer.toHexString(array[i] & 0xff), 2); + } + s += "]"; + return s; + } + + /** + * Adds zeros to beginning of the number to fill specified length. Returns + * as string + * + * @param number Number as string + * @param length Length of new string + * @return Number with added zeros + */ + public static String padZeros(String number, int length) { + int count = length - number.length(); + for (int i = 0; i < count; i++) { + number = "0" + number; + } + return number; + } + + /** + * Formats specified address to four numbers xxxx + * + * @param number Address to format + * @return String representation of the address + */ + public static String formatAddress(long number) { + if (Configuration.decimalAddress.get()) { + return padZeros(Long.toString(number), 4); + } + return padZeros(Long.toHexString(number), 4); + } + + /** + * Adds space to text to fill specified width + * + * @param text Text to add spaces to + * @param width New width + * @return Text with appended spaces + */ + public static String padSpaceRight(String text, int width) { + int oldLen = text.length(); + for (int i = oldLen; i < width; i++) { + text += " "; + } + return text; + } + + /** + * Escapes string by adding backslashes + * + * @param s String to escape + * @return Escaped string + */ + public static String escapeString(String s) { + String ret = ""; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\n') { + ret += "\\n"; + } else if (c == '\r') { + ret += "\\r"; + } else if (c == '\t') { + ret += "\\t"; + } else if (c == '\b') { + ret += "\\b"; + } else if (c == '\t') { + ret += "\\t"; + } else if (c == '\f') { + ret += "\\f"; + } else if (c == '\\') { + ret += "\\\\"; + } else if (c == '"') { + ret += "\\\""; + } else if (c == '\'') { + ret += "\\'"; + } else { + ret += c; + } + + } + return ret; + } + + public static String getValidHtmlId(String text) { + // ID and NAME tokens must begin with a letter ([A-Za-z]) and + // may be followed by any number of letters, digits ([0-9]), + // hyphens ("-"), underscores ("_"), colons (":"), and periods ("."). + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if ((ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (i > 0 && ((ch >= '0' && ch <= '9') + || ch == '-' || ch == '_' || ch == ':' || ch == '.'))) { + sb.append(ch); + } else { + sb.append('_'); + } + } + return sb.toString(); + } + + private final static String SPACES12 = " "; + private final static String ZEROS8 = "00000000"; + + public static String formatHex(int value, int width) { + StringBuilder sb = new StringBuilder(); + sb.append(Integer.toHexString(value)); + if (width > sb.length()) { + sb.insert(0, ZEROS8, 0, width - sb.length()); + } + return sb.toString(); + } + + public static String formatInt(int value, int width) { + StringBuilder sb = new StringBuilder(); + sb.append(value); + if (width > sb.length()) { + sb.insert(0, SPACES12, 0, width - sb.length()); + } + return sb.toString(); + } + + public static String indent(int level, String ss, String indentStr) { + StringBuilder sb = new StringBuilder(); + for (int ii = 0; ii < level; ii++) { + sb.append(indentStr); + } + sb.append(ss); + return sb.toString(); + } + + public static String indentRows(int level, String ss, String indentStr) { + StringBuilder sb = new StringBuilder(); + for (int ii = 0; ii < level; ii++) { + sb.append(indentStr); + } + ss = ss.replaceAll("(\r\n|\r|\n)", "\r\n"); + ss = "\r\n" + ss; + String repl = "\r\n" + sb.toString(); + ss = ss.replace("\r\n", repl); + if (ss.endsWith(repl)) { + ss = ss.substring(0, ss.length() - sb.toString().length()); + } + ss = ss.substring(2); + return ss; + } + + public static String unindentRows(int prefixLineCount, int level, String text) { + StringBuilder sb = new StringBuilder(); + Scanner scanner = new Scanner(text); + String indentStr = ""; + for (int i = 0; i < level; i++) { + indentStr += Configuration.getCodeFormatting().indentString; + } + int indentLength = indentStr.length(); + for (int i = 0; i < prefixLineCount; i++) { + scanner.nextLine(); // ignore line + } + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith(indentStr)) { + sb.append(line.substring(indentLength)).append(Configuration.getCodeFormatting().newLineChars); + } else { + return sb.toString(); + } + } + return sb.toString(); + } + + public static int getLineCount(String s) { + if (s.endsWith("\r\n")) { + s = s.substring(0, s.length() - 2); + } else if (s.endsWith("\r")) { + s = s.substring(0, s.length() - 1); + } else if (s.endsWith("\n")) { + s = s.substring(0, s.length() - 1); + } + String[] parts = s.split("(\r\n|\r|\n)"); + return parts.length; + } + + public static String padZeros(long number, int length) { + String ret = "" + number; + while (ret.length() < length) { + ret = "0" + ret; + } + return ret; + } + + public static String bytesToHexString(byte[] bytes) { + return bytesToHexString(bytes, 0); + } + + public static String bytesToHexString(byte[] bytes, int start) { + StringBuilder sb = new StringBuilder(); + if (start < bytes.length) { + for (int ii = start; ii < bytes.length; ii++) { + sb.append(formatHex(bytes[ii] & 0xff, 2)); + sb.append(' '); + } + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } + + public static String bytesToHexString(int maxByteCountInString, byte[] bytes, int start) { + if (bytes.length - start <= maxByteCountInString) { + return bytesToHexString(bytes, start); + } + byte[] trailingBytes = new byte[maxByteCountInString / 2]; + byte[] headingBytes = new byte[maxByteCountInString - trailingBytes.length]; + System.arraycopy(bytes, start, headingBytes, 0, headingBytes.length); + int startOfTrailingBytes = bytes.length - trailingBytes.length; + System.arraycopy(bytes, startOfTrailingBytes, trailingBytes, 0, trailingBytes.length); + StringBuilder sb = new StringBuilder(); + sb.append(bytesToHexString(headingBytes, 0)); + if (trailingBytes.length > 0) { + sb.append(" ... "); + sb.append(bytesToHexString(trailingBytes, 0)); + } + return sb.toString(); + } + + public static String format(String str, int len) { + if (len <= str.length()) { + return str; + } + StringBuilder sb = new StringBuilder(str); + for (int ii = str.length(); ii < len; ii++) { + sb.append(' '); + } + return sb.toString(); + } + + public static String joinStrings(List arr, String glue) { + String ret = ""; + boolean first = true; + for (String s : arr) { + if (!first) { + ret += glue; + } else { + first = false; + } + ret += s; + } + return ret; + } + + public static String joinStrings(String[] arr, String glue) { + String ret = ""; + boolean first = true; + for (String s : arr) { + if (!first) { + ret += glue; + } else { + first = false; + } + ret += s; + } + return ret; + } + + public static String joinStrings(List arr, String formatString, String glue) { + String ret = ""; + boolean first = true; + for (String s : arr) { + if (!first) { + ret += glue; + } else { + first = false; + } + ret += String.format(formatString, s); + } + return ret; + } + + public static String joinStrings(String[] arr, String formatString, String glue) { + String ret = ""; + boolean first = true; + for (String s : arr) { + if (!first) { + ret += glue; + } else { + first = false; + } + ret += String.format(formatString, s); + } + return ret; + } + + @SuppressWarnings("unchecked") + public static E deepCopy(E o) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(o); + oos.flush(); + } + E copy; + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + copy = (E) ois.readObject(); + } + return copy; + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, "Copy error", ex); + return null; + } + } + + public static List toList(Object... rest) { + List ret = new ArrayList<>(); + ret.addAll(Arrays.asList(rest)); + return ret; + } + + public static ByteArrayInputStream getInputStream(byte[] + ... data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + for (byte[] d : data) { + baos.write(d); + } + } catch (IOException iex) { + } + return new ByteArrayInputStream(baos.toByteArray()); + } + + public static byte[] readFile(String... file) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (String f : file) { + try (FileInputStream fis = new FileInputStream(f)) { + byte[] buf = new byte[4096]; + int cnt = 0; + while ((cnt = fis.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + } catch (IOException ex) { + Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); + } + } + return baos.toByteArray(); + } + + public static String readTextFile(String... file) { + return new String(readFile(file), Utf8Helper.charset); + } + + public static byte[] readStream(InputStream is) { + if (is instanceof MemoryInputStream) { + return ((MemoryInputStream) is).getAllRead(); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + byte[] buf = new byte[4096]; + int cnt = 0; + while ((cnt = is.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + } catch (IOException ex) { + Logger.getLogger(Helper.class.getName()).log(Level.SEVERE, null, ex); + } + return baos.toByteArray(); + } + + public static void writeFile(String file, byte[] + ... data) { + try (FileOutputStream fos = new FileOutputStream(file)) { + for (byte[] d : data) { + fos.write(d); + } + } catch (IOException ex) { + //ignore + } + } + + public static String stackToString(Stack stack, LocalData localData) throws InterruptedException { + String ret = "["; + for (int i = stack.size() - 1; i >= 0; i--) { + if (i < stack.size() - 1) { + ret += ", "; + } + ret += stack.get(i).toString(localData); + } + ret += "]"; + return ret; + } + + public static File fixDialogFile(File f) { + Pattern pat = Pattern.compile("\"([^\"]+)\""); + String name = f.getAbsolutePath(); + Matcher m = pat.matcher(name); + if (m.find()) { + f = new File(m.group(1)); + } + return f; + } + private static final BitSet fileNameInvalidChars; + private static final List invalidFilenamesParts; + + static { + BitSet toEncode = new BitSet(256); + + for (int i = 0; i < 32; i++) { + toEncode.set(i); + } + + toEncode.set('\\'); + toEncode.set('/'); + toEncode.set(':'); + toEncode.set('*'); + toEncode.set('?'); + toEncode.set('"'); + toEncode.set('<'); + toEncode.set('>'); + toEncode.set('|'); + + fileNameInvalidChars = toEncode; + + //windows reserved filenames: + invalidFilenamesParts = new ArrayList<>(); + invalidFilenamesParts.add("CON"); + invalidFilenamesParts.add("PRN"); + invalidFilenamesParts.add("AUX"); + invalidFilenamesParts.add("CLOCK$"); + invalidFilenamesParts.add("NUL"); + for (int i = 1; i <= 9; i++) { + invalidFilenamesParts.add("COM" + i); + invalidFilenamesParts.add("LPT" + i); + } + } + + public static String makeFileName(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + int ch = (int) str.charAt(i); + if (ch < 256 && fileNameInvalidChars.get(ch)) { + sb.append("%").append(String.format("%02X", ch)); + } else { + sb.append((char) ch); + } + } + str = sb.toString(); + if (str.endsWith(" ")) { + str = str.substring(0, str.length() - 1) + "%20"; + } + if (str.endsWith(".")) { + str = str.substring(0, str.length() - 1) + "%2E"; + } + str = "." + str + "."; + for (String inv : invalidFilenamesParts) { + str = Pattern.compile("\\." + Pattern.quote(inv) + "\\.", Pattern.CASE_INSENSITIVE).matcher(str).replaceAll("._" + inv + "."); + } + str = str.substring(1, str.length() - 1); //remove dots + if (str.isEmpty()) { + str = "unnamed"; + } + return str; + } + + public static String strToHex(String s) { + byte[] bs = Utf8Helper.getBytes(s); + String sn = ""; + for (int i = 0; i < bs.length; i++) { + sn += "0x" + Integer.toHexString(bs[i] & 0xff) + " "; + } + return sn; + } + + public static void emptyObject(Object obj) { + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field f : fields) { + try { + f.setAccessible(true); + Object v = f.get(obj); + if (v != null) { + if (v instanceof Collection) { + ((Collection) v).clear(); + } + if (v instanceof Component) { + if (((Component) v).getParent() != null) { + ((Component) v).getParent().remove((Component) v); + } + } + if (v instanceof Freed) { + Freed freed = ((Freed) v); + if (!freed.isFreeing()) { + ((Freed) v).free(); + } + } + f.set(obj, null); + } + } catch (UnsupportedOperationException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + } + } + } + + public static String formatTimeSec(long timeMs) { + long timeS = timeMs / 1000; + timeMs %= 1000; + long timeM = timeS / 60; + timeS %= 60; + long timeH = timeM / 60; + timeM %= 60; + String timeStr = ""; + if (timeH > 0) { + timeStr += Helper.padZeros(timeH, 2) + ":"; + } + timeStr += Helper.padZeros(timeM, 2) + ":"; + timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3); + return timeStr; + } + + public static String formatFileSize(long fileSizeLong) { + double fileSize = fileSizeLong; + if (fileSize < 1024) { + return String.format("%d bytes", fileSizeLong); + } + fileSize /= 1024; + if (fileSize < 1024) { + return String.format("%.2f KB", fileSize); + } + fileSize /= 1024; + return String.format("%.2f MB", fileSize); + } + + public static void freeMem() { + Cache.clearAll(); + System.gc(); + } + + public static String formatTimeToText(int timeS) { + long timeM = timeS / 60; + timeS %= 60; + long timeH = timeM / 60; + timeM %= 60; + + String timeStr = ""; + String strAnd = AppStrings.translate("timeFormat.and"); + String strHour = AppStrings.translate("timeFormat.hour"); + String strHours = AppStrings.translate("timeFormat.hours"); + String strMinute = AppStrings.translate("timeFormat.minute"); + String strMinutes = AppStrings.translate("timeFormat.minutes"); + String strSecond = AppStrings.translate("timeFormat.second"); + String strSeconds = AppStrings.translate("timeFormat.seconds"); + + if (timeH > 0) { + timeStr += timeH + " " + (timeH > 1 ? strHours : strHour); + } + if (timeM > 0) { + if (timeStr.length() > 0) { + timeStr += " " + strAnd + " "; + } + timeStr += timeM + " " + (timeM > 1 ? strMinutes : strMinute); + } + if (timeS > 0) { + if (timeStr.length() > 0) { + timeStr += " " + strAnd + " "; + } + timeStr += timeS + " " + (timeS > 1 ? strSeconds : strSecond); + } + + // (currently) used only in log, so no localization is required + return timeStr; + } + + public static GraphTextWriter byteArrayToHexWithHeader(GraphTextWriter writer, byte[] data) { + writer.appendNoHilight("#hexdata").newLine().newLine(); + return byteArrayToHex(writer, data, 8, 8, -1, false, false); + } + + public static GraphTextWriter byteArrayToHex(GraphTextWriter writer, byte[] data, int bytesPerRow, int groupSize, int limit, boolean addChars, boolean showAddress) { + + /* // hex data from decompiled actions + Scanner scanner = new Scanner(srcWithHex); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + result.append(line.substring(1).trim()).append(nl); + } else { + result.append(";").append(line).append(nl); + } + }*/ + int length = data.length; + if (limit != -1 && length > limit) { + length = limit; + } + + int rowCount = length / bytesPerRow; + if (length % bytesPerRow > 0) { + rowCount++; + } + + long address = 0; + for (int row = 0; row < rowCount; row++) { + if (row > 0) { + writer.newLine(); + } + + if (showAddress) { + writer.appendNoHilight("0x" + String.format("%08x ", address)); + } + + for (int i = 0; i < bytesPerRow; i++) { + int idx = row * bytesPerRow + i; + if (length > idx) { + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + writer.appendNoHilight(String.format("%02x ", data[idx])); + } else { + if (addChars) { + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + writer.appendNoHilight(" "); + } + } + address += bytesPerRow; + } + + if (addChars) { + writer.appendNoHilight(" "); + for (int i = 0; i < bytesPerRow; i++) { + int idx = row * bytesPerRow + i; + if (length == idx) { + break; + } + if (i > 0 && i % groupSize == 0) { + writer.appendNoHilight(" "); + } + byte ch = data[idx]; + if (ch >= 0 && ch < 32) { + ch = '.'; + } + writer.appendNoHilight((char) ch + ""); + } + } + } + + writer.newLine(); + if (limit != -1 && data.length > limit) { + writer.appendNoHilight(AppStrings.translate("binaryData.truncateWarning").replace("%count%", Integer.toString(data.length - limit))); + } + + return writer; + } + + public static String byteArrayToHex(byte[] data, int bytesPerRow, int limit) { + HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); + byteArrayToHex(writer, data, bytesPerRow, 8, limit, true, true); + return writer.toString(); + } + + public static byte[] getBytesFromHexaText(String text) { + Scanner scanner = new Scanner(text); + scanner.nextLine(); // ignore first line + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + if (line.startsWith(";")) { + continue; + } + line = line.replace(" ", ""); + for (int i = 0; i < line.length() / 2; i++) { + String hexStr = line.substring(i * 2, (i + 1) * 2); + byte b = (byte) Integer.parseInt(hexStr, 16); + baos.write(b); + } + } + byte[] data = baos.toByteArray(); + return data; + } + + public static boolean contains(int[] array, int value) { + for (int i : array) { + if (i == value) { + return true; + } + } + return false; + } + + public static void saveStream(InputStream is, File output) throws IOException { + byte[] buf = new byte[1024]; + int cnt; + try (FileOutputStream fos = new FileOutputStream(output)) { + while ((cnt = is.read(buf)) > 0) { + fos.write(buf, 0, cnt); + fos.flush(); + } + } + } + + public static void appendTimeoutComment(GraphTextWriter writer, int timeout) { + writer.appendNoHilight("/*").newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError")).newLine(); + writer.appendNoHilight(" * ").appendNoHilight(MessageFormat.format(AppStrings.translate("decompilationError.timeout"), Helper.formatTimeToText(timeout))).newLine(); + writer.appendNoHilight(" */").newLine(); + writer.appendNoHilight("throw new IllegalOperationError(\""). + appendNoHilight(AppStrings.translate("decompilationError.timeout.description")). + appendNoHilight("\");").newLine(); + } + + public static void appendErrorComment(GraphTextWriter writer, Throwable ex) { + writer.appendNoHilight("/*").newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError")).newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError.obfuscated")).newLine(); + writer.appendNoHilight(" * ").appendNoHilight(AppStrings.translate("decompilationError.errorType")). + appendNoHilight(": " + ex.getClass().getSimpleName()).newLine(); + writer.appendNoHilight(" */").newLine(); + writer.appendNoHilight("throw new IllegalOperationError(\""). + appendNoHilight(AppStrings.translate("decompilationError.error.description")). + appendNoHilight("\");").newLine(); + } + + public static String escapeHTML(String text) { + String from[] = new String[]{"&", "<", ">", "\"", "'", "/"}; + String to[] = new String[]{"&", "<", ">", """, "'", "/"}; + for (int i = 0; i < from.length; i++) { + text = text.replace(from[i], to[i]); + } + return text; + } + + public static Shape imageToShape(BufferedImage image) { + Area area = new Area(); + Rectangle rectangle = new Rectangle(); + int y1, y2; + for (int x = 0; x < image.getWidth(); x++) { + y1 = Integer.MAX_VALUE; + y2 = -1; + for (int y = 0; y < image.getHeight(); y++) { + int rgb = image.getRGB(x, y); + rgb = rgb >>> 24; + if (rgb > 0) { + if (y1 == Integer.MAX_VALUE) { + y1 = y; + y2 = y; + } + if (y > (y2 + 1)) { + rectangle.setBounds(x, y1, 1, y2 - y1 + 1); + area.add(new Area(rectangle)); + y1 = y; + } + y2 = y; + } + } + if ((y2 - y1) >= 0) { + rectangle.setBounds(x, y1, 1, y2 - y1 + 1); + area.add(new Area(rectangle)); + } + } + return area; + } + + /** + * Formats double value (removes .0 from end) + * + * @param d + * @return String + */ + public static String doubleStr(double d) { + String ret = "" + d; + if (ret.endsWith(".0")) { + ret = ret.substring(0, ret.length() - 2); + } + return ret; + } +} diff --git a/src/com/jpexs/helpers/resource/canvas.js b/src/com/jpexs/helpers/resource/canvas.js index f76105099..ab7699d62 100644 --- a/src/com/jpexs/helpers/resource/canvas.js +++ b/src/com/jpexs/helpers/resource/canvas.js @@ -1,908 +1,917 @@ /** * JPEXS Free Flash Decompiler Filters - */ - -Filters = {}; - -var createCanvas = function(width,height){ - var c = document.createElement("canvas"); - c.width = width; - c.height = height; - c.style.display="none"; - //temporary add to document to get this work (getImageData, etc.) - document.body.appendChild(c); - document.body.removeChild(c); - return c; -}; - -Filters._premultiply = function(data){ - var len = data.length; - for (var i = 0; i < len; i+=4) { - var f = data[i+3] * 0.003921569; - data[i] = Math.round(data[i] * f); - data[i+1] = Math.round(data[i+1] * f); - data[i+2] = Math.round(data[i+2] * f); - } + */ + +Filters = {}; + +var createCanvas = function(width, height) { + var c = document.createElement("canvas"); + c.width = width; + c.height = height; + c.style.display = "none"; + //temporary add to document to get this work (getImageData, etc.) + document.body.appendChild(c); + document.body.removeChild(c); + return c; }; -Filters._unpremultiply = function(data){ - var len = data.length; - for (var i = 0; i < len; i+=4) { - var a = data[i+3]; - if (a == 0 || a == 255) { - continue; - } - var f = 255/a; - var r = (data[i] * f); - var g = (data[i+1] * f); - var b = (data[i+2] * f); - if (r > 255) { - r = 255; - } - if (g > 255) { - g = 255; - } - if (b > 255) { - b = 255; - } - - data[i] = r; - data[i+1] = g; - data[i+2] = b; - } +Filters._premultiply = function(data) { + var len = data.length; + for (var i = 0; i < len; i += 4) { + var f = data[i + 3] * 0.003921569; + data[i] = Math.round(data[i] * f); + data[i + 1] = Math.round(data[i + 1] * f); + data[i + 2] = Math.round(data[i + 2] * f); + } +}; + +Filters._unpremultiply = function(data) { + var len = data.length; + for (var i = 0; i < len; i += 4) { + var a = data[i + 3]; + if (a == 0 || a == 255) { + continue; + } + var f = 255 / a; + var r = (data[i] * f); + var g = (data[i + 1] * f); + var b = (data[i + 2] * f); + if (r > 255) { + r = 255; + } + if (g > 255) { + g = 255; + } + if (b > 255) { + b = 255; + } + + data[i] = r; + data[i + 1] = g; + data[i + 2] = b; + } }; Filters._boxBlurHorizontal = function(pixels, mask, w, h, radius) { - var index = 0; - var newColors = []; + var index = 0; + var newColors = []; - for (var y = 0; y < h; y++) { - var hits = 0; - var r = 0; - var g = 0; - var b = 0; - var a = 0; - for (var x = -radius*4; x < w*4; x+=4) { - var oldPixel = x - radius*4 - 4; - if (oldPixel >= 0) { - if ((mask == null) || (mask[index + oldPixel + 3] > 0)) { - a -= pixels[index + oldPixel + 3]; - r -= pixels[index + oldPixel]; - g -= pixels[index + oldPixel + 1]; - b -= pixels[index + oldPixel + 2]; - hits--; - } + for (var y = 0; y < h; y++) { + var hits = 0; + var r = 0; + var g = 0; + var b = 0; + var a = 0; + for (var x = -radius * 4; x < w * 4; x += 4) { + var oldPixel = x - radius * 4 - 4; + if (oldPixel >= 0) { + if ((mask == null) || (mask[index + oldPixel + 3] > 0)) { + a -= pixels[index + oldPixel + 3]; + r -= pixels[index + oldPixel]; + g -= pixels[index + oldPixel + 1]; + b -= pixels[index + oldPixel + 2]; + hits--; } + } - var newPixel = x + radius*4; - if (newPixel < w*4) { - if ((mask == null) || (mask[index + newPixel + 3] > 0)) { - a += pixels[index + newPixel + 3]; - r += pixels[index + newPixel]; - g += pixels[index + newPixel + 1]; - b += pixels[index + newPixel + 2]; - hits++; - } + var newPixel = x + radius * 4; + if (newPixel < w * 4) { + if ((mask == null) || (mask[index + newPixel + 3] > 0)) { + a += pixels[index + newPixel + 3]; + r += pixels[index + newPixel]; + g += pixels[index + newPixel + 1]; + b += pixels[index + newPixel + 2]; + hits++; } + } - if (x >= 0) { - if ((mask == null) || (mask[index + x + 3] > 0)) { - if (hits == 0) { - newColors[x] = 0; - newColors[x+1] = 0; - newColors[x+2] = 0; - newColors[x+3] = 0; - } else { - newColors[x] = Math.round(r / hits); - newColors[x+1] = Math.round(g / hits); - newColors[x+2] = Math.round(b / hits); - newColors[x+3] = Math.round(a / hits); - - } - } else { + if (x >= 0) { + if ((mask == null) || (mask[index + x + 3] > 0)) { + if (hits == 0) { newColors[x] = 0; - newColors[x+1] = 0; - newColors[x+2] = 0; - newColors[x+3] = 0; + newColors[x + 1] = 0; + newColors[x + 2] = 0; + newColors[x + 3] = 0; + } else { + newColors[x] = Math.round(r / hits); + newColors[x + 1] = Math.round(g / hits); + newColors[x + 2] = Math.round(b / hits); + newColors[x + 3] = Math.round(a / hits); + } + } else { + newColors[x] = 0; + newColors[x + 1] = 0; + newColors[x + 2] = 0; + newColors[x + 3] = 0; } } - for(var p=0;p= 0) { - if ((mask == null) || (mask[index + oldPixelOffset + 3] > 0)) { - a -= pixels[index + oldPixelOffset + 3]; - r -= pixels[index + oldPixelOffset]; - g -= pixels[index + oldPixelOffset + 1]; - b -= pixels[index + oldPixelOffset + 2]; - hits--; - } + var newColors = []; + var oldPixelOffset = -(radius + 1) * w * 4; + var newPixelOffset = (radius) * w * 4; + for (var x = 0; x < w * 4; x += 4) { + var hits = 0; + var r = 0; + var g = 0; + var b = 0; + var a = 0; + var index = -radius * w * 4 + x; + for (var y = -radius; y < h; y++) { + var oldPixel = y - radius - 1; + if (oldPixel >= 0) { + if ((mask == null) || (mask[index + oldPixelOffset + 3] > 0)) { + a -= pixels[index + oldPixelOffset + 3]; + r -= pixels[index + oldPixelOffset]; + g -= pixels[index + oldPixelOffset + 1]; + b -= pixels[index + oldPixelOffset + 2]; + hits--; } - var newPixel = y + radius; - if (newPixel < h) { - if ((mask == null) || (mask[index + newPixelOffset + 3] > 0)) { - a += pixels[index + newPixelOffset + 3]; - r += pixels[index + newPixelOffset]; - g += pixels[index + newPixelOffset + 1]; - b += pixels[index + newPixelOffset + 2]; - hits++; - } - } + } - if (y >= 0) { - if ((mask == null) || (mask[y * w * 4 + x + 3] > 0)) { - if (hits == 0) { - newColors[4*y] = 0; - newColors[4*y + 1] = 0; - newColors[4*y + 2] = 0; - newColors[4*y + 3] = 0; - } else { - newColors[4*y] = Math.round(r / hits); - newColors[4*y + 1] = Math.round(g / hits); - newColors[4*y + 2] = Math.round(b / hits); - newColors[4*y + 3] = Math.round(a / hits); - } + var newPixel = y + radius; + if (newPixel < h) { + if ((mask == null) || (mask[index + newPixelOffset + 3] > 0)) { + a += pixels[index + newPixelOffset + 3]; + r += pixels[index + newPixelOffset]; + g += pixels[index + newPixelOffset + 1]; + b += pixels[index + newPixelOffset + 2]; + hits++; + } + } + + if (y >= 0) { + if ((mask == null) || (mask[y * w * 4 + x + 3] > 0)) { + if (hits == 0) { + newColors[4 * y] = 0; + newColors[4 * y + 1] = 0; + newColors[4 * y + 2] = 0; + newColors[4 * y + 3] = 0; } else { - newColors[4*y] = 0; - newColors[4*y + 1] = 0; - newColors[4*y + 2] = 0; - newColors[4*y + 3] = 0; + newColors[4 * y] = Math.round(r / hits); + newColors[4 * y + 1] = Math.round(g / hits); + newColors[4 * y + 2] = Math.round(b / hits); + newColors[4 * y + 3] = Math.round(a / hits); } + } else { + newColors[4 * y] = 0; + newColors[4 * y + 1] = 0; + newColors[4 * y + 2] = 0; + newColors[4 * y + 3] = 0; } - - index += w * 4; } - for (var y = 0; y < h; y++) { - pixels[y * w * 4 + x] = newColors[4*y]; - pixels[y * w * 4 + x + 1] = newColors[4*y + 1]; - pixels[y * w * 4 + x + 2] = newColors[4*y + 2]; - pixels[y * w * 4 + x + 3] = newColors[4*y + 3]; - } + index += w * 4; } - }; + + for (var y = 0; y < h; y++) { + pixels[y * w * 4 + x] = newColors[4 * y]; + pixels[y * w * 4 + x + 1] = newColors[4 * y + 1]; + pixels[y * w * 4 + x + 2] = newColors[4 * y + 2]; + pixels[y * w * 4 + x + 3] = newColors[4 * y + 3]; + } + } +}; -Filters.blur = function(canvas,ctx, hRadius, vRadius, iterations, mask){ - var imgData=ctx.getImageData(0,0,canvas.width,canvas.height); - var data = imgData.data; - Filters._premultiply(data); - for (var i = 0; i < iterations; i++) { - Filters._boxBlurHorizontal(data, mask, canvas.width, canvas.height, Math.floor(hRadius / 2)); - Filters._boxBlurVertical(data, mask, canvas.width, canvas.height, Math.floor(vRadius / 2)); - } - - Filters._unpremultiply(data); - - var width = canvas.width; - var height = canvas.height; - var retCanvas = createCanvas(width,height); - var retImg = retCanvas.getContext("2d"); - retImg.putImageData(imgData,0,0); - return retCanvas; +Filters.blur = function(canvas, ctx, hRadius, vRadius, iterations, mask) { + var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var data = imgData.data; + Filters._premultiply(data); + for (var i = 0; i < iterations; i++) { + Filters._boxBlurHorizontal(data, mask, canvas.width, canvas.height, Math.floor(hRadius / 2)); + Filters._boxBlurVertical(data, mask, canvas.width, canvas.height, Math.floor(vRadius / 2)); + } + + Filters._unpremultiply(data); + + var width = canvas.width; + var height = canvas.height; + var retCanvas = createCanvas(width, height); + var retImg = retCanvas.getContext("2d"); + retImg.putImageData(imgData, 0, 0); + return retCanvas; } Filters._moveRGB = function(width, height, rgb, deltaX, deltaY, fill) { - var img = createCanvas(width,height); + var img = createCanvas(width, height); - var ig=img.getContext("2d"); - - Filters._setRGB(ig,0,0,width,height,rgb); - var retImg = createCanvas(width,height); - retImg.width = width; - retImg.heigth = height; - var g = retImg.getContext("2d"); - g.fillStyle = fill; - g.globalCompositeOperation = "copy"; - g.fillRect(0, 0, width, height); - g.drawImage(img, deltaX,deltaY); - return g.getImageData(0, 0, width, height).data; - }; + var ig = img.getContext("2d"); + + Filters._setRGB(ig, 0, 0, width, height, rgb); + var retImg = createCanvas(width, height); + retImg.width = width; + retImg.heigth = height; + var g = retImg.getContext("2d"); + g.fillStyle = fill; + g.globalCompositeOperation = "copy"; + g.fillRect(0, 0, width, height); + g.drawImage(img, deltaX, deltaY); + return g.getImageData(0, 0, width, height).data; +}; Filters.FULL = 1; Filters.INNER = 2; Filters.OUTER = 3; -Filters._setRGB = function(ctx, x, y, width, height, data){ - var id = ctx.createImageData(width,height); - for(var i=0;i255) sa = 255; - shadow[i+3] = Math.round(sa); + if (sa > 255) + sa = 255; + shadow[i + 3] = Math.round(sa); } var colorFirst = "#000000"; - var colorAlpha = "rgba(0,0,0,0)"; + var colorAlpha = "rgba(0,0,0,0)"; var angleRad = angle / 180 * Math.PI; var moveX = (distance * Math.cos(angleRad)); - var moveY = (distance * Math.sin(angleRad)); + var moveY = (distance * Math.sin(angleRad)); shadow = Filters._moveRGB(width, height, shadow, moveX, moveY, inner ? colorFirst : colorAlpha); - - - var retCanvas = createCanvas(canvas.width,canvas.height); - Filters._setRGB(retCanvas.getContext("2d"),0,0,width,height,shadow); - if(blurX>0 || blurY > 0){ - retCanvas = Filters.blur(retCanvas,retCanvas.getContext("2d"), blurX, blurY, iterations,null); + + + var retCanvas = createCanvas(canvas.width, canvas.height); + Filters._setRGB(retCanvas.getContext("2d"), 0, 0, width, height, shadow); + if (blurX > 0 || blurY > 0) { + retCanvas = Filters.blur(retCanvas, retCanvas.getContext("2d"), blurX, blurY, iterations, null); } - shadow = retCanvas.getContext("2d").getImageData(0,0,width,height).data; - + shadow = retCanvas.getContext("2d").getImageData(0, 0, width, height).data; + var srcPixels = src.getImageData(0, 0, width, height).data; - for (var i = 0; i < shadow.length; i+=4) { - var mask = srcPixels[i+3]; + for (var i = 0; i < shadow.length; i += 4) { + var mask = srcPixels[i + 3]; if (!inner) { mask = 255 - mask; } shadow[i + 3] = mask * shadow[i + 3] / 255; } - Filters._setRGB(retCanvas.getContext("2d"),0,0,width,height,shadow); - + Filters._setRGB(retCanvas.getContext("2d"), 0, 0, width, height, shadow); + if (!knockout) { var g = retCanvas.getContext("2d"); g.globalCompositeOperation = "destination-over"; g.drawImage(canvas, 0, 0); } - + return retCanvas; }; -Filters._cut = function(a,min,max){ - if(a > max) a = max; - if(a < min) a = min; +Filters._cut = function(a, min, max) { + if (a > max) + a = max; + if (a < min) + a = min; return a; } Filters.gradientBevel = function(canvas, src, colors, ratios, blurX, blurY, strength, type, angle, distance, knockout, iterations) { - var width = canvas.width; - var height = canvas.height; - var retImg = createCanvas(width,height); - var srcPixels = src.getImageData(0, 0, width, height).data; + var width = canvas.width; + var height = canvas.height; + var retImg = createCanvas(width, height); + var srcPixels = src.getImageData(0, 0, width, height).data; - var revPixels = []; - for (var i = 0; i < srcPixels.length; i+=4) { - revPixels[i] = srcPixels[i]; - revPixels[i+1] = srcPixels[i+1]; - revPixels[i+2] = srcPixels[i+2]; - revPixels[i+3] = 255-srcPixels[i+3]; - } + var revPixels = []; + for (var i = 0; i < srcPixels.length; i += 4) { + revPixels[i] = srcPixels[i]; + revPixels[i + 1] = srcPixels[i + 1]; + revPixels[i + 2] = srcPixels[i + 2]; + revPixels[i + 3] = 255 - srcPixels[i + 3]; + } - var gradient = createCanvas(512, 1); - var gg = gradient.getContext("2d"); + var gradient = createCanvas(512, 1); + var gg = gradient.getContext("2d"); - var grd=ctx.createLinearGradient(0,0,511,0); - for(var s=0;s= 0 && scy < sh && scx >= 0 && scx < sw) { - var srcOff = (scy*sw+scx)*4; - var wt = weights[cy*side+cx]; - r += src[srcOff] * wt; - g += src[srcOff+1] * wt; - b += src[srcOff+2] * wt; - a += src[srcOff+3] * wt; - } +Filters.convolution = function(canvas, ctx, weights, opaque) { + var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height); + var side = Math.round(Math.sqrt(weights.length)); + var halfSide = Math.floor(side / 2); + var src = pixels.data; + var sw = pixels.width; + var sh = pixels.height; + // pad output by the convolution matrix + var w = sw; + var h = sh; + var outCanvas = createCanvas(w, h); + var outCtx = outCanvas.getContext("2d"); + var output = outCtx.getImageData(0, 0, w, h); + var dst = output.data; + // go through the destination image pixels + var alphaFac = opaque ? 1 : 0; + for (var y = 0; y < h; y++) { + for (var x = 0; x < w; x++) { + var sy = y; + var sx = x; + var dstOff = (y * w + x) * 4; + // calculate the weighed sum of the source image pixels that + // fall under the convolution matrix + var r = 0, g = 0, b = 0, a = 0; + for (var cy = 0; cy < side; cy++) { + for (var cx = 0; cx < side; cx++) { + var scy = sy + cy - halfSide; + var scx = sx + cx - halfSide; + if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) { + var srcOff = (scy * sw + scx) * 4; + var wt = weights[cy * side + cx]; + r += src[srcOff] * wt; + g += src[srcOff + 1] * wt; + b += src[srcOff + 2] * wt; + a += src[srcOff + 3] * wt; + } + } + } + dst[dstOff] = r; + dst[dstOff + 1] = g; + dst[dstOff + 2] = b; + dst[dstOff + 3] = a + alphaFac * (255 - a); } - } - dst[dstOff] = r; - dst[dstOff+1] = g; - dst[dstOff+2] = b; - dst[dstOff+3] = a + alphaFac*(255-a); } - } - outCtx.putImageData(output,0,0); - return outCanvas; + outCtx.putImageData(output, 0, 0); + return outCanvas; }; -Filters.colorMatrix = function(canvas,ctx,m){ - var pixels = ctx.getImageData(0,0,canvas.width,canvas.height); +Filters.colorMatrix = function(canvas, ctx, m) { + var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height); - var data=pixels.data; - for(var i=0;i255) v = 255; return v;}; - -BlendModes.normal = function(src,dst,result,pos){ - var am = (255-src[pos + 3])/255; - result[pos] = this._cut(src[pos]*src[pos + 3]/255 + dst[pos]*dst[pos+3]/255*am); - result[pos + 1] = this._cut(src[pos+1]*src[pos + 3]/255 + dst[pos+1]*dst[pos+3]/255*am); - result[pos + 2] = this._cut(src[pos+2]*src[pos + 3]/255 + dst[pos+2]*dst[pos+3]/255*am); - result[pos + 3] = this._cut(src[pos + 3] + dst[pos+3]*am); +BlendModes._cut = function(v) { + if (v < 0) + v = 0; + if (v > 255) + v = 255; + return v; }; -BlendModes.layer = function(src,dst,result,pos){ - BlendModes.normal(src,dst,result,pos); +BlendModes.normal = function(src, dst, result, pos) { + var am = (255 - src[pos + 3]) / 255; + result[pos] = this._cut(src[pos] * src[pos + 3] / 255 + dst[pos] * dst[pos + 3] / 255 * am); + result[pos + 1] = this._cut(src[pos + 1] * src[pos + 3] / 255 + dst[pos + 1] * dst[pos + 3] / 255 * am); + result[pos + 2] = this._cut(src[pos + 2] * src[pos + 3] / 255 + dst[pos + 2] * dst[pos + 3] / 255 * am); + result[pos + 3] = this._cut(src[pos + 3] + dst[pos + 3] * am); }; -BlendModes.multiply = function(src,dst,result,pos){ - result[pos+0] = (src[pos+0] * dst[pos+0]) >> 8; - result[pos+1] = (src[pos+1] * dst[pos+1]) >> 8; - result[pos+2] = (src[pos+2] * dst[pos+2]) >> 8; - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.layer = function(src, dst, result, pos) { + BlendModes.normal(src, dst, result, pos); }; -BlendModes.screen = function(src,dst,result,pos){ - result[pos+0] = 255 - ((255 - src[pos+0]) * (255 - dst[pos+0]) >> 8); - result[pos+1] = 255 - ((255 - src[pos+1]) * (255 - dst[pos+1]) >> 8); - result[pos+2] = 255 - ((255 - src[pos+2]) * (255 - dst[pos+2]) >> 8); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.multiply = function(src, dst, result, pos) { + result[pos + 0] = (src[pos + 0] * dst[pos + 0]) >> 8; + result[pos + 1] = (src[pos + 1] * dst[pos + 1]) >> 8; + result[pos + 2] = (src[pos + 2] * dst[pos + 2]) >> 8; + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.lighten = function(src,dst,result,pos){ - result[pos+0] = Math.max(src[pos+0], dst[pos+0]); - result[pos+1] = Math.max(src[pos+1], dst[pos+1]); - result[pos+2] = Math.max(src[pos+2], dst[pos+2]); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.screen = function(src, dst, result, pos) { + result[pos + 0] = 255 - ((255 - src[pos + 0]) * (255 - dst[pos + 0]) >> 8); + result[pos + 1] = 255 - ((255 - src[pos + 1]) * (255 - dst[pos + 1]) >> 8); + result[pos + 2] = 255 - ((255 - src[pos + 2]) * (255 - dst[pos + 2]) >> 8); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.darken = function(src,dst,result,pos){ - result[pos+0] = Math.min(src[pos+0], dst[pos+0]); - result[pos+1] = Math.min(src[pos+1], dst[pos+1]); - result[pos+2] = Math.min(src[pos+2], dst[pos+2]); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.lighten = function(src, dst, result, pos) { + result[pos + 0] = Math.max(src[pos + 0], dst[pos + 0]); + result[pos + 1] = Math.max(src[pos + 1], dst[pos + 1]); + result[pos + 2] = Math.max(src[pos + 2], dst[pos + 2]); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.difference = function(src,dst,result,pos){ - result[pos+0] = Math.abs(dst[pos+0] - src[pos+0]); - result[pos+1] = Math.abs(dst[pos+1] - src[pos+1]); - result[pos+2] = Math.abs(dst[pos+2] - src[pos+2]); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.darken = function(src, dst, result, pos) { + result[pos + 0] = Math.min(src[pos + 0], dst[pos + 0]); + result[pos + 1] = Math.min(src[pos + 1], dst[pos + 1]); + result[pos + 2] = Math.min(src[pos + 2], dst[pos + 2]); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.add = function(src,dst,result,pos){ - result[pos+0] = Math.min(255, src[pos+0] + dst[pos+0]); - result[pos+1] = Math.min(255, src[pos+1] + dst[pos+1]); - result[pos+2] = Math.min(255, src[pos+2] + dst[pos+2]); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3]); +BlendModes.difference = function(src, dst, result, pos) { + result[pos + 0] = Math.abs(dst[pos + 0] - src[pos + 0]); + result[pos + 1] = Math.abs(dst[pos + 1] - src[pos + 1]); + result[pos + 2] = Math.abs(dst[pos + 2] - src[pos + 2]); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.subtract = function(src,dst,result,pos){ - result[pos+0] = Math.max(0, src[pos+0] + dst[pos+0] - 256); - result[pos+1] = Math.max(0, src[pos+1] + dst[pos+1] - 256); - result[pos+2] = Math.max(0, src[pos+2] + dst[pos+2] - 256); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.add = function(src, dst, result, pos) { + result[pos + 0] = Math.min(255, src[pos + 0] + dst[pos + 0]); + result[pos + 1] = Math.min(255, src[pos + 1] + dst[pos + 1]); + result[pos + 2] = Math.min(255, src[pos + 2] + dst[pos + 2]); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3]); }; -BlendModes.invert = function(src,dst,result,pos){ - result[pos+0] = 255 - dst[pos+0]; - result[pos+1] = 255 - dst[pos+1]; - result[pos+2] = 255 - dst[pos+2]; - result[pos+3] = src[pos+3]; +BlendModes.subtract = function(src, dst, result, pos) { + result[pos + 0] = Math.max(0, src[pos + 0] + dst[pos + 0] - 256); + result[pos + 1] = Math.max(0, src[pos + 1] + dst[pos + 1] - 256); + result[pos + 2] = Math.max(0, src[pos + 2] + dst[pos + 2] - 256); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; -BlendModes.alpha = function(src,dst,result,pos){ - result[pos+0] = src[pos+0]; - result[pos+1] = src[pos+1]; - result[pos+2] = src[pos+2]; - result[pos+3] = dst[pos+3]; //? +BlendModes.invert = function(src, dst, result, pos) { + result[pos + 0] = 255 - dst[pos + 0]; + result[pos + 1] = 255 - dst[pos + 1]; + result[pos + 2] = 255 - dst[pos + 2]; + result[pos + 3] = src[pos + 3]; }; -BlendModes.erase = function(src,dst,result,pos){ - result[pos+0] = src[pos+0]; - result[pos+1] = src[pos+1]; - result[pos+2] = src[pos+2]; - result[pos+3] = 255 - dst[pos+3]; //? +BlendModes.alpha = function(src, dst, result, pos) { + result[pos + 0] = src[pos + 0]; + result[pos + 1] = src[pos + 1]; + result[pos + 2] = src[pos + 2]; + result[pos + 3] = dst[pos + 3]; //? }; -BlendModes.overlay = function(src,dst,result,pos){ - result[pos+0] = dst[pos+0] < 128 ? dst[pos+0] * src[pos+0] >> 7 - : 255 - ((255 - dst[pos+0]) * (255 - src[pos+0]) >> 7); - result[pos+1] = dst[pos+1] < 128 ? dst[pos+1] * src[pos+1] >> 7 - : 255 - ((255 - dst[pos+1]) * (255 - src[pos+1]) >> 7); - result[pos+2] = dst[pos+2] < 128 ? dst[pos+2] * src[pos+2] >> 7 - : 255 - ((255 - dst[pos+2]) * (255 - src[pos+2]) >> 7); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.erase = function(src, dst, result, pos) { + result[pos + 0] = src[pos + 0]; + result[pos + 1] = src[pos + 1]; + result[pos + 2] = src[pos + 2]; + result[pos + 3] = 255 - dst[pos + 3]; //? }; -BlendModes.hardlight = function(src,dst,result,pos){ - result[pos+0] = src[pos+0] < 128 ? dst[pos+0] * src[pos+0] >> 7 - : 255 - ((255 - src[pos+0]) * (255 - dst[pos+0]) >> 7); - result[pos+1] = src[pos+1] < 128 ? dst[pos+1] * src[pos+1] >> 7 - : 255 - ((255 - src[pos+1]) * (255 - dst[pos+1]) >> 7); - result[pos+2] = src[pos+2] < 128 ? dst[pos+2] * src[pos+2] >> 7 - : 255 - ((255 - src[pos+2]) * (255 - dst[pos+2]) >> 7); - result[pos+3] = Math.min(255, src[pos+3] + dst[pos+3] - (src[pos+3] * dst[pos+3]) / 255); +BlendModes.overlay = function(src, dst, result, pos) { + result[pos + 0] = dst[pos + 0] < 128 ? dst[pos + 0] * src[pos + 0] >> 7 + : 255 - ((255 - dst[pos + 0]) * (255 - src[pos + 0]) >> 7); + result[pos + 1] = dst[pos + 1] < 128 ? dst[pos + 1] * src[pos + 1] >> 7 + : 255 - ((255 - dst[pos + 1]) * (255 - src[pos + 1]) >> 7); + result[pos + 2] = dst[pos + 2] < 128 ? dst[pos + 2] * src[pos + 2] >> 7 + : 255 - ((255 - dst[pos + 2]) * (255 - src[pos + 2]) >> 7); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); +}; + +BlendModes.hardlight = function(src, dst, result, pos) { + result[pos + 0] = src[pos + 0] < 128 ? dst[pos + 0] * src[pos + 0] >> 7 + : 255 - ((255 - src[pos + 0]) * (255 - dst[pos + 0]) >> 7); + result[pos + 1] = src[pos + 1] < 128 ? dst[pos + 1] * src[pos + 1] >> 7 + : 255 - ((255 - src[pos + 1]) * (255 - dst[pos + 1]) >> 7); + result[pos + 2] = src[pos + 2] < 128 ? dst[pos + 2] * src[pos + 2] >> 7 + : 255 - ((255 - src[pos + 2]) * (255 - dst[pos + 2]) >> 7); + result[pos + 3] = Math.min(255, src[pos + 3] + dst[pos + 3] - (src[pos + 3] * dst[pos + 3]) / 255); }; BlendModes._list = [ - BlendModes.normal, - BlendModes.normal, - BlendModes.layer, - BlendModes.multiply, - BlendModes.screen, - BlendModes.lighten, - BlendModes.darken, - BlendModes.difference, - BlendModes.add, - BlendModes.subtract, - BlendModes.invert, - BlendModes.alpha, - BlendModes.erase, - BlendModes.overlay, - BlendModes.hardlight - ]; - -BlendModes.blendData = function(srcPixel,dstPixel,retData,modeIndex){ - var result = []; - var retPixel = []; - var alpha = 1.0; - for(var i=0;imax) v = max; - return v; - }; - this.apply = function(c){ - var d=c; - d[0] = this._cut(d[0]*this.r_mult/255+this.r_add,0,255); - d[1] = this._cut(d[1]*this.g_mult/255+this.g_add,0,255); - d[2] = this._cut(d[2]*this.b_mult/255+this.b_add,0,255); - d[3] = this._cut(d[3]*this.a_mult/255+this.a_add/255,0,1); - return d; - }; - this.applyToImage=function(fimg){ - if(this.isEmpty()){ - return fimg - }; - var icanvas = createCanvas(fimg.width,fimg.height); - var ictx = icanvas.getContext("2d"); - ictx.drawImage(fimg,0,0); - var imdata=ictx.getImageData(0,0,icanvas.width,icanvas.height); - var idata=imdata.data; - for(var i=0;i max) + v = max; + return v; + }; + this.apply = function(c) { + var d = c; + d[0] = this._cut(d[0] * this.r_mult / 255 + this.r_add, 0, 255); + d[1] = this._cut(d[1] * this.g_mult / 255 + this.g_add, 0, 255); + d[2] = this._cut(d[2] * this.b_mult / 255 + this.b_add, 0, 255); + d[3] = this._cut(d[3] * this.a_mult / 255 + this.a_add / 255, 0, 1); + return d; + }; + this.applyToImage = function(fimg) { + if (this.isEmpty()) { + return fimg + } + ; + var icanvas = createCanvas(fimg.width, fimg.height); + var ictx = icanvas.getContext("2d"); + ictx.drawImage(fimg, 0, 0); + var imdata = ictx.getImageData(0, 0, icanvas.width, icanvas.height); + var idata = imdata.data; + for (var i = 0; i < idata.length; i += 4) { + var c = this.apply([idata[i], idata[i + 1], idata[i + 2], idata[i + 3] / 255]); + idata[i] = c[0]; + idata[i + 1] = c[1]; + idata[i + 2] = c[2]; + idata[i + 3] = Math.round(c[3] * 255); + } + ictx.putImageData(imdata, 0, 0); + return icanvas; + }; + this.merge = function(cx) { + return new cxform(this.r_add + cx.r_add, this.g_add + cx.g_add, this.b_add + cx.b_add, this.a_add + cx.a_add, this.r_mult * cx.r_mult / 255, this.g_mult * cx.g_mult / 255, this.b_mult * cx.b_mult / 255, this.a_mult * cx.a_mult / 255); + }; + this.isEmpty = function() { + return this.r_add == 0 && this.g_add == 0 && this.b_add == 0 && this.a_add == 0 && this.r_mult == 255 && this.g_mult == 255 && this.b_mult == 255 && this.a_mult == 255; + }; }; -var place = function(obj,canvas,ctx,matrix,ctrans,blendMode,frame,ratio,time){ - ctx.save(); - ctx.transform(matrix[0],matrix[1],matrix[2],matrix[3],matrix[4],matrix[5]); - if(blendMode>1){ - var oldctx = ctx; - var ncanvas = createCanvas(canvas.width,canvas.height); - ctx = ncanvas.getContext("2d"); - enhanceContext(ctx); - ctx.applyTransforms(oldctx._matrices); - } - if(blendMode > 1){ - eval(obj+"(ctx,new cxform(0,0,0,0,255,255,255,255),frame,ratio,time);"); - }else{ - eval(obj+"(ctx,ctrans,frame,ratio,time);"); - } - if(blendMode > 1){ - BlendModes.blendCanvas(ctrans.applyToImage(ncanvas),canvas,canvas,blendMode); - ctx = oldctx; - } - ctx.restore(); +var place = function(obj, canvas, ctx, matrix, ctrans, blendMode, frame, ratio, time) { + ctx.save(); + ctx.transform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); + if (blendMode > 1) { + var oldctx = ctx; + var ncanvas = createCanvas(canvas.width, canvas.height); + ctx = ncanvas.getContext("2d"); + enhanceContext(ctx); + ctx.applyTransforms(oldctx._matrices); + } + if (blendMode > 1) { + eval(obj + "(ctx,new cxform(0,0,0,0,255,255,255,255),frame,ratio,time);"); + } else { + eval(obj + "(ctx,ctrans,frame,ratio,time);"); + } + if (blendMode > 1) { + BlendModes.blendCanvas(ctrans.applyToImage(ncanvas), canvas, canvas, blendMode); + ctx = oldctx; + } + ctx.restore(); } -var tocolor = function(c){var r= "rgba("+c[0]+","+c[1]+","+c[2]+","+c[3]+")"; return r;}; +var tocolor = function(c) { + var r = "rgba(" + c[0] + "," + c[1] + "," + c[2] + "," + c[3] + ")"; + return r; +}; -window.addEventListener('load',function(){ - - var wsize = document.getElementById("width_size"); - var hsize = document.getElementById("height_size"); - //var bsize = document.getElementById("both_size"); - //bsize.addEventListener('mousedown', initDragBoth, false); - wsize.addEventListener('mousedown', initDragWidth, false); - hsize.addEventListener('mousedown', initDragHeight, false); +window.addEventListener('load', function() { + + var wsize = document.getElementById("width_size"); + var hsize = document.getElementById("height_size"); + //var bsize = document.getElementById("both_size"); + //bsize.addEventListener('mousedown', initDragBoth, false); + wsize.addEventListener('mousedown', initDragWidth, false); + hsize.addEventListener('mousedown', initDragHeight, false); }); var startWidth = 0; @@ -929,24 +938,24 @@ function initDragBoth(e) { } function initDrag(e) { - startX = e.clientX; - startY = e.clientY; - startWidth = canvas.width; - startHeight = canvas.height; - document.documentElement.addEventListener('mousemove', doDrag, false); - document.documentElement.addEventListener('mouseup', stopDrag, false); + startX = e.clientX; + startY = e.clientY; + startWidth = canvas.width; + startHeight = canvas.height; + document.documentElement.addEventListener('mousemove', doDrag, false); + document.documentElement.addEventListener('mouseup', stopDrag, false); } -function doDrag(e) { - if(dragWidth){ +function doDrag(e) { + if (dragWidth) { canvas.width = (startWidth + e.clientX - startX); - canvas.height = canvas.width*originalHeight/originalWidth; - } - else if(dragHeight){ + canvas.height = canvas.width * originalHeight / originalWidth; + } + else if (dragHeight) { canvas.height = (startHeight + e.clientY - startY); - canvas.width = canvas.height*originalWidth/originalHeight; - } - drawFrame(); + canvas.width = canvas.height * originalWidth / originalHeight; + } + drawFrame(); } function stopDrag(e) { @@ -955,34 +964,34 @@ function stopDrag(e) { } -function drawPath(ctx,p){ +function drawPath(ctx, p) { ctx.beginPath(); var parts = p.split(" "); var len = parts.length; var drawCommand = ""; - for(var i=0;i