diff --git a/src/com/jpexs/decompiler/flash/SWF.java b/src/com/jpexs/decompiler/flash/SWF.java index 8f09a092d..3d0d469e2 100644 --- a/src/com/jpexs/decompiler/flash/SWF.java +++ b/src/com/jpexs/decompiler/flash/SWF.java @@ -52,7 +52,7 @@ 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.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; import com.jpexs.decompiler.flash.ecma.Null; import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; import com.jpexs.decompiler.flash.exporters.FontExporter; @@ -271,7 +271,7 @@ public final class SWF implements TreeItem, Timelined { private Timeline timeline; - public DumpInfo dumpInfo; + public DumpInfoSwfNode dumpInfo; public void updateCharacters() { characters.clear(); @@ -308,7 +308,7 @@ public final class SWF implements TreeItem, Timelined { Tag t = tags.get(i); if (t instanceof DefineSpriteTag) { if (!isSpriteValid((DefineSpriteTag) t, new ArrayList())) { - tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getPos(), t.getOriginalLength(), null)); + tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getOriginalRange(), null)); } } } @@ -385,9 +385,7 @@ public final class SWF implements TreeItem, Timelined { sos.writeUI8(frameRate); sos.writeUI16(frameCount); - Map tagPositions = new HashMap<>(); - Map tagLengths = new HashMap<>(); - sos.writeTags(tags, tagPositions, tagLengths); + sos.writeTags(tags); if (hasEndTag) { sos.writeUI16(0); } @@ -512,7 +510,7 @@ public final class SWF implements TreeItem, Timelined { uncompressedData = baos.toByteArray(); SWFInputStream sis = new SWFInputStream(this, uncompressedData); - dumpInfo = new DumpInfo("rootswf", "", null, 0, 0); + dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); sis.dumpInfo = dumpInfo; sis.readBytesEx(3, "signature"); // skip siganture version = sis.readUI8("version"); @@ -543,7 +541,7 @@ public final class SWF implements TreeItem, Timelined { } else { boolean hasNonUnknownTag = false; for (Tag tag : tags) { - if (tag.getOriginalLength() > 2 && Tag.getRequiredTags().contains(tag.getId())) { + if (tag.getOriginalDataLength() > 0 && Tag.getRequiredTags().contains(tag.getId())) { hasNonUnknownTag = true; } } @@ -2120,7 +2118,7 @@ public final class SWF implements TreeItem, Timelined { } } for (ASMSource src : actionsMap.keySet()) { - actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, 0, ""/*FIXME path*/)); + actionsMap.put(src, Action.removeNops(0, actionsMap.get(src), version, ""/*FIXME path*/)); src.setActions(actionsMap.get(src)); src.setModified(); } diff --git a/src/com/jpexs/decompiler/flash/SWFInputStream.java b/src/com/jpexs/decompiler/flash/SWFInputStream.java index 95c1a877e..0219a9174 100644 --- a/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -262,6 +262,7 @@ 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.ByteArrayRange; import com.jpexs.helpers.Helper; import com.jpexs.helpers.MemoryInputStream; import com.jpexs.helpers.ProgressListener; @@ -1100,8 +1101,7 @@ public class SWFInputStream implements AutoCloseable { return tag; } - long pos = tag.getPos(); - int length = tag.getOriginalLength(); + ByteArrayRange data = tag.getOriginalRange(); SWF swf = tag.getSwf(); TagStub tagStub = (TagStub) tag; SWFInputStream sis = tagStub.getDataStream(); @@ -1109,127 +1109,127 @@ public class SWFInputStream implements AutoCloseable { try { switch (tag.getId()) { case 0: - ret = new EndTag(swf, pos, length); + ret = new EndTag(swf, data); break; case 1: - ret = new ShowFrameTag(swf, pos, length); + ret = new ShowFrameTag(swf, data); break; case 2: - ret = new DefineShapeTag(sis, pos, length); + ret = new DefineShapeTag(sis, data); break; //case 3: FreeCharacter case 4: - ret = new PlaceObjectTag(sis, pos, length); + ret = new PlaceObjectTag(sis, data); break; case 5: - ret = new RemoveObjectTag(sis, pos, length); + ret = new RemoveObjectTag(sis, data); break; case 6: - ret = new DefineBitsTag(sis, pos, length); + ret = new DefineBitsTag(sis, data); break; case 7: - ret = new DefineButtonTag(sis, pos, length); + ret = new DefineButtonTag(sis, data); break; case 8: - ret = new JPEGTablesTag(sis, pos, length); + ret = new JPEGTablesTag(sis, data); break; case 9: - ret = new SetBackgroundColorTag(sis, pos, length); + ret = new SetBackgroundColorTag(sis, data); break; case 10: - ret = new DefineFontTag(sis, pos, length); + ret = new DefineFontTag(sis, data); break; case 11: - ret = new DefineTextTag(sis, pos, length); + ret = new DefineTextTag(sis, data); break; case 12: - ret = new DoActionTag(sis, pos, length); + ret = new DoActionTag(sis, data); break; case 13: - ret = new DefineFontInfoTag(sis, pos, length); + ret = new DefineFontInfoTag(sis, data); break; case 14: - ret = new DefineSoundTag(sis, pos, length); + ret = new DefineSoundTag(sis, data); break; case 15: - ret = new StartSoundTag(sis, pos, length); + ret = new StartSoundTag(sis, data); break; //case 16: case 17: - ret = new DefineButtonSoundTag(sis, pos, length); + ret = new DefineButtonSoundTag(sis, data); break; case 18: - ret = new SoundStreamHeadTag(sis, pos, length); + ret = new SoundStreamHeadTag(sis, data); break; case 19: - ret = new SoundStreamBlockTag(sis, pos, length); + ret = new SoundStreamBlockTag(sis, data); break; case 21: - ret = new DefineBitsJPEG2Tag(sis, pos, length); + ret = new DefineBitsJPEG2Tag(sis, data); break; case 20: - ret = new DefineBitsLosslessTag(sis, pos, length); + ret = new DefineBitsLosslessTag(sis, data); break; case 22: - ret = new DefineShape2Tag(sis, pos, length); + ret = new DefineShape2Tag(sis, data); break; case 23: - ret = new DefineButtonCxformTag(sis, pos, length); + ret = new DefineButtonCxformTag(sis, data); break; case 24: - ret = new ProtectTag(sis, pos, length); + ret = new ProtectTag(sis, data); break; //case 25: PathsArePostscript case 26: - ret = new PlaceObject2Tag(sis, pos, length); + ret = new PlaceObject2Tag(sis, data); break; //case 27: case 28: - ret = new RemoveObject2Tag(sis, pos, length); + ret = new RemoveObject2Tag(sis, data); break; //case 29: SyncFrame //case 30: //case 31: FreeAll case 32: - ret = new DefineShape3Tag(sis, pos, length); + ret = new DefineShape3Tag(sis, data); break; case 33: - ret = new DefineText2Tag(sis, pos, length); + ret = new DefineText2Tag(sis, data); break; case 34: - ret = new DefineButton2Tag(sis, pos, length); + ret = new DefineButton2Tag(sis, data); break; case 35: - ret = new DefineBitsJPEG3Tag(sis, pos, length); + ret = new DefineBitsJPEG3Tag(sis, data); break; case 36: - ret = new DefineBitsLossless2Tag(sis, pos, length); + ret = new DefineBitsLossless2Tag(sis, data); break; case 37: - ret = new DefineEditTextTag(sis, pos, length); + ret = new DefineEditTextTag(sis, data); break; //case 38: DefineVideo case 39: - ret = new DefineSpriteTag(sis, level, pos, length, parallel, skipUnusualTags); + ret = new DefineSpriteTag(sis, level, data, parallel, skipUnusualTags); break; //case 40: NameCharacter case 41: - ret = new ProductInfoTag(sis, pos, length); + ret = new ProductInfoTag(sis, data); break; //case 42: DefineTextFormat case 43: - ret = new FrameLabelTag(sis, pos, length); + ret = new FrameLabelTag(sis, data); break; //case 44: case 45: - ret = new SoundStreamHead2Tag(sis, pos, length); + ret = new SoundStreamHead2Tag(sis, data); break; case 46: - ret = new DefineMorphShapeTag(sis, pos, length); + ret = new DefineMorphShapeTag(sis, data); break; //case 47: GenerateFrame case 48: - ret = new DefineFont2Tag(sis, pos, length); + ret = new DefineFont2Tag(sis, data); break; //case 49: GeneratorCommand //case 50: DefineCommandObject @@ -1237,148 +1237,148 @@ public class SWFInputStream implements AutoCloseable { //case 52: ExternalFont //case 53-55 case 56: - ret = new ExportAssetsTag(sis, pos, length); + ret = new ExportAssetsTag(sis, data); break; case 57: - ret = new ImportAssetsTag(sis, pos, length); + ret = new ImportAssetsTag(sis, data); break; case 58: - ret = new EnableDebuggerTag(sis, pos, length); + ret = new EnableDebuggerTag(sis, data); break; case 59: - ret = new DoInitActionTag(sis, pos, length); + ret = new DoInitActionTag(sis, data); break; case 60: - ret = new DefineVideoStreamTag(sis, pos, length); + ret = new DefineVideoStreamTag(sis, data); break; case 61: - ret = new VideoFrameTag(sis, pos, length); + ret = new VideoFrameTag(sis, data); break; case 62: - ret = new DefineFontInfo2Tag(sis, pos, length); + ret = new DefineFontInfo2Tag(sis, data); break; case 63: - ret = new DebugIDTag(sis, pos, length); + ret = new DebugIDTag(sis, data); break; case 64: - ret = new EnableDebugger2Tag(sis, pos, length); + ret = new EnableDebugger2Tag(sis, data); break; case 65: - ret = new ScriptLimitsTag(sis, pos, length); + ret = new ScriptLimitsTag(sis, data); break; case 66: - ret = new SetTabIndexTag(sis, pos, length); + ret = new SetTabIndexTag(sis, data); break; //case 67-68: case 69: - ret = new FileAttributesTag(sis, pos, length); + ret = new FileAttributesTag(sis, data); break; case 70: - ret = new PlaceObject3Tag(sis, pos, length); + ret = new PlaceObject3Tag(sis, data); break; case 71: - ret = new ImportAssets2Tag(sis, pos, length); + ret = new ImportAssets2Tag(sis, data); break; case 72: - ret = new DoABCTag(sis, pos, length); + ret = new DoABCTag(sis, data); break; case 73: - ret = new DefineFontAlignZonesTag(sis, pos, length); + ret = new DefineFontAlignZonesTag(sis, data); break; case 74: - ret = new CSMTextSettingsTag(sis, pos, length); + ret = new CSMTextSettingsTag(sis, data); break; case 75: - ret = new DefineFont3Tag(sis, pos, length); + ret = new DefineFont3Tag(sis, data); break; case 76: - ret = new SymbolClassTag(sis, pos, length); + ret = new SymbolClassTag(sis, data); break; case 77: - ret = new MetadataTag(sis, pos, length); + ret = new MetadataTag(sis, data); break; case 78: - ret = new DefineScalingGridTag(sis, pos, length); + ret = new DefineScalingGridTag(sis, data); break; //case 79-81: case 82: - ret = new DoABCDefineTag(sis, pos, length); + ret = new DoABCDefineTag(sis, data); break; case 83: - ret = new DefineShape4Tag(sis, pos, length); + ret = new DefineShape4Tag(sis, data); break; case 84: - ret = new DefineMorphShape2Tag(sis, pos, length); + ret = new DefineMorphShape2Tag(sis, data); break; //case 85: case 86: - ret = new DefineSceneAndFrameLabelDataTag(sis, pos, length); + ret = new DefineSceneAndFrameLabelDataTag(sis, data); break; case 87: - ret = new DefineBinaryDataTag(sis, pos, length); + ret = new DefineBinaryDataTag(sis, data); break; case 88: - ret = new DefineFontNameTag(sis, pos, length); + ret = new DefineFontNameTag(sis, data); break; case 89: - ret = new StartSound2Tag(sis, pos, length); + ret = new StartSound2Tag(sis, data); break; case 90: - ret = new DefineBitsJPEG4Tag(sis, pos, length); + ret = new DefineBitsJPEG4Tag(sis, data); break; case 91: - ret = new DefineFont4Tag(sis, pos, length); + ret = new DefineFont4Tag(sis, data); break; //case 92: certificate case 93: - ret = new EnableTelemetryTag(sis, pos, length); + ret = new EnableTelemetryTag(sis, data); break; case 94: - ret = new PlaceObject4Tag(sis, pos, length); + ret = new PlaceObject4Tag(sis, data); break; default: if (gfx) { //GFX tags only in GFX files. There may be incorrect GFX tags in non GFX files switch (tag.getId()) { case 1000: - ret = new ExporterInfoTag(sis, pos, length); + ret = new ExporterInfoTag(sis, data); break; case 1001: - ret = new DefineExternalImage(sis, pos, length); + ret = new DefineExternalImage(sis, data); break; case 1002: - ret = new FontTextureInfo(sis, pos, length); + ret = new FontTextureInfo(sis, data); break; case 1003: - ret = new DefineExternalGradient(sis, pos, length); + ret = new DefineExternalGradient(sis, data); break; case 1004: - ret = new DefineGradientMap(sis, pos, length); + ret = new DefineGradientMap(sis, data); break; case 1005: - ret = new DefineCompactedFont(sis, pos, length); + ret = new DefineCompactedFont(sis, data); break; case 1006: - ret = new DefineExternalSound(sis, pos, length); + ret = new DefineExternalSound(sis, data); break; case 1007: - ret = new DefineExternalStreamSound(sis, pos, length); + ret = new DefineExternalStreamSound(sis, data); break; case 1008: - ret = new DefineSubImage(sis, pos, length); + ret = new DefineSubImage(sis, data); break; case 1009: - ret = new DefineExternalImage2(sis, pos, length); + ret = new DefineExternalImage2(sis, data); break; default: - ret = new UnknownTag(swf, tag.getId(), pos, length); + ret = new UnknownTag(swf, tag.getId(), data); } } else { - ret = new UnknownTag(swf, tag.getId(), pos, length); + ret = new UnknownTag(swf, tag.getId(), data); } } } catch (IOException ex) { logger.log(Level.SEVERE, "Error during tag reading", ex); - ret = new TagStub(swf, tag.getId(), "ErrorTag", pos, length, null); + ret = new TagStub(swf, tag.getId(), "ErrorTag", data, null); } ret.forceWriteAsLong = tag.forceWriteAsLong; ret.setTimelined(tag.getTimelined()); @@ -1413,7 +1413,8 @@ public class SWFInputStream implements AutoCloseable { } int headerLength = readLong ? 6 : 2; SWFInputStream tagDataStream = getLimitedStream((int) tagLength); - TagStub tagStub = new TagStub(swf, tagID, "Unresolved", pos, (int) (tagLength + headerLength), tagDataStream); + ByteArrayRange dataRange = new ByteArrayRange(swf.uncompressedData, (int) pos, (int) (tagLength + headerLength)); + TagStub tagStub = new TagStub(swf, tagID, "Unresolved", dataRange, tagDataStream); Tag ret = tagStub; ret.forceWriteAsLong = readLong; skipBytes((int) tagLength); diff --git a/src/com/jpexs/decompiler/flash/SWFOutputStream.java b/src/com/jpexs/decompiler/flash/SWFOutputStream.java index e660f16b2..22a6848e9 100644 --- a/src/com/jpexs/decompiler/flash/SWFOutputStream.java +++ b/src/com/jpexs/decompiler/flash/SWFOutputStream.java @@ -79,7 +79,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; -import java.util.Map; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; @@ -415,20 +414,12 @@ public class SWFOutputStream extends OutputStream { * Writes list of Tag values to the stream * * @param tags List of tag values - * @param tagPositions - * @param tagLengths * @throws IOException */ - public void writeTags(List tags, Map tagPositions, Map tagLengths) throws IOException { + public void writeTags(List tags) throws IOException { for (Tag tag : tags) { - long pos = getPos(); tag.writeTag(this); - int length = (int) (getPos() - pos); - tagPositions.put(tag, pos); - tagLengths.put(tag, length); } - - // todo: honfika: update tag position and lengths. Currently the 2nd save fails } /** diff --git a/src/com/jpexs/decompiler/flash/action/Action.java b/src/com/jpexs/decompiler/flash/action/Action.java index 2b4063f67..2b733dafe 100644 --- a/src/com/jpexs/decompiler/flash/action/Action.java +++ b/src/com/jpexs/decompiler/flash/action/Action.java @@ -1,1329 +1,1324 @@ -/* - * 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.AppStrings; -import com.jpexs.decompiler.flash.BaseLocalData; -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.model.ActionItem; -import com.jpexs.decompiler.flash.action.model.ConstantPool; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.ExtendsActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetPropertyActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.ImplementsOpActionItem; -import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; -import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.SetPropertyActionItem; -import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; -import com.jpexs.decompiler.flash.action.model.TemporaryRegister; -import com.jpexs.decompiler.flash.action.model.UnsupportedActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.parser.ParseException; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol; -import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; -import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; -import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem; -import com.jpexs.decompiler.flash.action.special.ActionEnd; -import com.jpexs.decompiler.flash.action.special.ActionStore; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -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.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -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.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.helpers.GraphTextWriter; -import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphSource; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.TranslateException; -import com.jpexs.decompiler.graph.model.CommentItem; -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.ScriptEndItem; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.List; -import java.util.Stack; -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; - -/** - * Represents one ACTIONRECORD, also has some static method to work with Actions - */ -public class Action implements GraphSourceItem { - - public Action replaceWith; - private boolean ignored = false; - /** - * Action type identifier - */ - public int actionCode; - /** - * Length of action data - */ - public int actionLength; - public long containerSWFOffset; - private long address; - - public static final String[] reservedWords = { - "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", - "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", - "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", - "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", - "with", "dynamic", "default", "final", "in"}; - - public static boolean isReservedWord(String s) { - if (s == null) { - return false; - } - for (String rw : reservedWords) { - if (rw.equals(s.trim())) { - return true; - } - } - return false; - } - - public long getFileAddress() { - return containerSWFOffset + getAddress(); - } - /** - * Names of ActionScript properties - */ - public static final String[] propertyNames = new String[]{ - "_X", - "_Y", - "_xscale", - "_yscale", - "_currentframe", - "_totalframes", - "_alpha", - "_visible", - "_width", - "_height", - "_rotation", - "_target", - "_framesloaded", - "_name", - "_droptarget", - "_url", - "_highquality", - "_focusrect", - "_soundbuftime", - "_quality", - "_xmouse", - "_ymouse" - }; - public static final List propertyNamesList = Arrays.asList(propertyNames); - private static final Logger logger = Logger.getLogger(Action.class.getName()); - - /** - * Constructor - * - * @param actionCode Action type identifier - * @param actionLength Length of action data - */ - public Action(int actionCode, int actionLength) { - this.actionCode = actionCode; - this.actionLength = actionLength; - } - - public Action() { - } - - /** - * Returns address of this action - * - * @return address of this action - */ - public long getAddress() { - return address; - } - - /** - * Gets all addresses which are referenced from this action and/or - * subactions - * - * @param version SWF version - * @return List of addresses - */ - public List getAllRefs(int version) { - List ret = new ArrayList<>(); - return ret; - } - - /** - * Gets all ActionIf or ActionJump actions from subactions - * - * @return List of actions - */ - public List getAllIfsOrJumps() { - List ret = new ArrayList<>(); - return ret; - } - - /** - * Gets all ActionIf or ActionJump actions from list of actions - * - * @param list List of actions - * @return List of actions - */ - public static List getActionsAllIfsOrJumps(List list) { - List ret = new ArrayList<>(); - for (Action a : list) { - List part = a.getAllIfsOrJumps(); - ret.addAll(part); - } - return ret; - } - - /** - * Gets all addresses which are referenced from the list of actions - * - * @param list List of actions - * @param version SWF version - * @return List of addresses - */ - public static List getActionsAllRefs(List list, int version) { - List ret = new ArrayList<>(); - for (Action a : list) { - if (a.replaceWith != null) { - a.replaceWith.setAddress(a.getAddress(), version, false); - ret.addAll(a.replaceWith.getAllRefs(version)); - } - List part = a.getAllRefs(version); - ret.addAll(part); - } - return ret; - } - - /** - * Sets address of this instruction - * - * @param address Address - * @param version SWF version - */ - public final void setAddress(long address, int version) { - setAddress(address, version, true); - } - - public void setAddress(long address, int version, boolean recursive) { - this.address = address; - } - - /** - * Returns a string representation of the object - * - * @return a string representation of the object. - */ - @Override - public String toString() { - return "Action" + actionCode; - } - - /** - * Reads String from FlasmLexer - * - * @param lex FlasmLexer - * @return String value - * @throws IOException - * @throws ParseException When read object is not String - */ - protected String lexString(FlasmLexer lex) throws IOException, ParseException { - ASMParsedSymbol symb = lex.yylex(); - if (symb.type != ASMParsedSymbol.TYPE_STRING) { - throw new ParseException("String expected", lex.yyline()); - } - return (String) symb.value; - } - - /** - * Reads Block startServer from FlasmLexer - * - * @param lex FlasmLexer - * @throws IOException - * @throws ParseException When read object is not Block startServer - */ - protected void lexBlockOpen(FlasmLexer lex) throws IOException, ParseException { - ASMParsedSymbol symb = lex.yylex(); - if (symb.type != ASMParsedSymbol.TYPE_BLOCK_START) { - throw new ParseException("Block startServer ", lex.yyline()); - } - } - - /** - * Reads Identifier from FlasmLexer - * - * @param lex FlasmLexer - * @return Identifier name - * @throws IOException - * @throws ParseException When read object is not Identifier - */ - protected String lexIdentifier(FlasmLexer lex) throws IOException, ParseException { - ASMParsedSymbol symb = lex.yylex(); - if (symb.type != ASMParsedSymbol.TYPE_IDENTIFIER) { - throw new ParseException("Identifier expected", lex.yyline()); - } - return (String) symb.value; - } - - /** - * Reads long value from FlasmLexer - * - * @param lex FlasmLexer - * @return long value - * @throws IOException - * @throws ParseException When read object is not long value - */ - protected long lexLong(FlasmLexer lex) throws IOException, ParseException { - ASMParsedSymbol symb = lex.yylex(); - if (symb.type != ASMParsedSymbol.TYPE_INTEGER) { - throw new ParseException("Integer expected", lex.yyline()); - } - return (Long) symb.value; - } - - /** - * Reads boolean value from FlasmLexer - * - * @param lex FlasmLexer - * @return boolean value - * @throws IOException - * @throws ParseException When read object is not boolean value - */ - protected boolean lexBoolean(FlasmLexer lex) throws IOException, ParseException { - ASMParsedSymbol symb = lex.yylex(); - if (symb.type != ASMParsedSymbol.TYPE_BOOLEAN) { - throw new ParseException("Boolean expected", lex.yyline()); - } - return (Boolean) symb.value; - } - - /** - * Gets action converted to bytes - * - * @param version SWF version - * @return Array of bytes - */ - public byte[] getBytes(int version) { - return surroundWithAction(new byte[0], version); - } - - /** - * Surrounds byte array with Action header - * - * @param data Byte array - * @param version SWF version - * @return Byte array - */ - protected byte[] surroundWithAction(byte[] data, int version) { - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SWFOutputStream sos2 = new SWFOutputStream(baos2, version); - try { - sos2.writeUI8(actionCode); - if (actionCode >= 0x80) { - sos2.writeUI16(data.length); - } - sos2.write(data); - sos2.close(); - } catch (IOException e) { - } - return baos2.toByteArray(); - } - - /** - * Converts list of Actions to bytes - * - * @param list List of actions - * @param addZero Whether or not to add 0 UI8 value to the end - * @param version SWF version - * @return Array of bytes - */ - public static byte[] actionsToBytes(List list, boolean addZero, int version) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Action lastAction = null; - for (Action a : list) { - try { - lastAction = a; - baos.write(a.getBytes(version)); - } catch (IOException e) { - } - } - if (addZero && (lastAction == null || !(lastAction instanceof ActionEnd))) { - baos.write(0); - } - return baos.toByteArray(); - } - - /** - * Set addresses of actions in the list - * - * @param list List of actions - * @param baseAddress Address of first action in the list - * @param version SWF version - */ - public static void setActionsAddresses(List list, long baseAddress, int version) { - long offset = baseAddress; - for (Action a : list) { - a.setAddress(offset, version); - offset += a.getBytes(version).length; - } - } - - /** - * Converts list of actions to ASM source - * - * @param listeners - * @param address - * @param list List of actions - * @param importantOffsets List of important offsets to mark as labels - * @param version SWF version - * @param exportMode PCode or hex? - * @param writer - * @param swfPos - * @param path - * @return HilightedTextWriter - */ - public static GraphTextWriter actionsToString(List listeners, long address, List list, List importantOffsets, int version, ScriptExportMode exportMode, GraphTextWriter writer, long swfPos, String path) { - return actionsToString(listeners, address, list, importantOffsets, new ArrayList(), version, exportMode, writer, swfPos, path); - } - - /** - * Converts list of actions to ASM source - * - * @param listeners - * @param address - * @param list List of actions - * @param importantOffsets List of important offsets to mark as labels - * @param constantPool Constant pool - * @param version SWF version - * @param hex Add hexadecimal? - * @param swfPos - * @param path - * @return HilightedTextWriter - */ - private static GraphTextWriter actionsToString(List listeners, long address, List list, List importantOffsets, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer, long swfPos, String path) { - long offset; - if (importantOffsets == null) { - //setActionsAddresses(list, 0, version); - importantOffsets = getActionsAllRefs(list, version); - } - /*List cps = SWFInputStream.getConstantPool(new ArrayList(), new ActionGraphSource(list, version, new HashMap(), new HashMap(), new HashMap()), 0, version, path); - if (!cps.isEmpty()) { - setConstantPool(list, cps.get(cps.size() - 1)); - }*/ - HashMap> containers = new HashMap<>(); - HashMap containersPos = new HashMap<>(); - offset = address; - int pos = -1; - boolean lastPush = false; - for (GraphSourceItem s : list) { - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).progress(AppStrings.translate("disassemblingProgress.toString"), pos + 2, list.size()); - } - Action a = null; - if (s instanceof Action) { - a = (Action) s; - } - pos++; - if (exportMode == ScriptExportMode.PCODE_HEX) { - if (lastPush) { - writer.newLine(); - lastPush = false; - } - writer.appendNoHilight("; "); - writer.appendNoHilight(Helper.bytesToHexString(a.getBytes(version))); - writer.newLine(); - } - offset = a.getAddress(); - - if ((!(a.isIgnored())) && (a instanceof GraphSourceItemContainer)) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; - containersPos.put(cnt, 0); - List sizes = cnt.getContainerSizes(); - long addr = ((Action) cnt).getAddress() + cnt.getHeaderSize(); - for (Long size : sizes) { - addr += size; - if (size == 0) { - continue; - } - if (!containers.containsKey(addr)) { - containers.put(addr, new ArrayList()); - } - containers.get(addr).add(cnt); - } - } - - if (containers.containsKey(offset)) { - for (int i = 0; i < containers.get(offset).size(); i++) { - writer.appendNoHilight("}").newLine(); - GraphSourceItemContainer cnt = containers.get(offset).get(i); - int cntPos = containersPos.get(cnt); - writer.appendNoHilight(cnt.getASMSourceBetween(cntPos)); - cntPos++; - containersPos.put(cnt, cntPos); - } - } - - if (Configuration.showAllAddresses.get() || importantOffsets.contains(offset)) { - if (lastPush) { - writer.newLine(); - lastPush = false; - } - writer.appendNoHilight("loc"); - writer.appendNoHilight(Helper.formatAddress(offset)); - writer.appendNoHilight(":"); - } - - if (a.replaceWith != null) { - if (lastPush) { - writer.newLine(); - lastPush = false; - } - writer.append("", offset); - writer.appendNoHilight(a.replaceWith.getASMSource(list, importantOffsets, constantPool, version, exportMode)); - writer.newLine(); - } else if (a.isIgnored()) { - if (lastPush) { - writer.newLine(); - lastPush = false; - } - int len = 0; - if (pos + 1 < list.size()) { - len = (int) (((Action) (list.get(pos + 1))).getAddress() - a.getAddress()); - } else { - len = a.getBytes(version).length; - } - if (!(a instanceof ActionEnd)) { - for (int i = 0; i < len; i++) { - writer.appendNoHilight("Nop").newLine(); - } - } - } else { - //if (!(a instanceof ActionNop)) { - String add = ""; - if (a instanceof ActionIf) { - add = " change: " + ((ActionIf) a).getJumpOffset(); - } - if (a instanceof ActionJump) { - add = " change: " + ((ActionJump) a).getJumpOffset(); - } - add = "; ofs" + Helper.formatAddress(offset) + add; - add = ""; - if ((a instanceof ActionPush) && lastPush) { - writer.appendNoHilight(" "); - ((ActionPush) a).paramsToStringReplaced(list, importantOffsets, constantPool, version, exportMode, writer); - } else { - if (lastPush) { - writer.newLine(); - lastPush = false; - } - - writer.append("", offset); - - int fixBranch = -1; - if (a instanceof ActionIf) { - ActionIf aif = (ActionIf) a; - if (aif.jumpUsed && !aif.ignoreUsed) { - fixBranch = 0; - } - if (!aif.jumpUsed && aif.ignoreUsed) { - fixBranch = 1; - } - } - - if (fixBranch > -1) { - writer.appendNoHilight("FFDec_DeobfuscatePop").newLine(); - if (fixBranch == 0) { //jump - writer.appendNoHilight("Jump loc"); - writer.appendNoHilight(Helper.formatAddress(a.getAddress() + a.getBytes(version).length + ((ActionIf) a).getJumpOffset())); - } else { - //nojump, ignore - } - } else { - a.getASMSourceReplaced(list, importantOffsets, constantPool, version, exportMode, writer); - } - writer.appendNoHilight(a.isIgnored() ? "; ignored" : ""); - writer.appendNoHilight(add); - if (!(a instanceof ActionPush)) { - writer.newLine(); - } - } - if (a instanceof ActionPush) { - lastPush = true; - } else { - lastPush = false; - } - //} - } - offset += a.getBytes(version).length; - } - if (lastPush) { - writer.newLine(); - } - if (containers.containsKey(offset)) { - for (int i = 0; i < containers.get(offset).size(); i++) { - writer.appendNoHilight("}"); - writer.newLine(); - GraphSourceItemContainer cnt = containers.get(offset).get(i); - int cntPos = containersPos.get(cnt); - writer.appendNoHilight(cnt.getASMSourceBetween(cntPos)); - cntPos++; - containersPos.put(cnt, cntPos); - } - } - if (importantOffsets.contains(offset)) { - writer.appendNoHilight("loc"); - writer.appendNoHilight(Helper.formatAddress(offset)); - writer.appendNoHilight(":"); - writer.newLine(); - } - return writer; - } - - /** - * Convert action to ASM source - * - * @param container - * @param knownAddreses List of important offsets to mark as labels - * @param constantPool Constant pool - * @param version SWF version - * @param exportMode PCode or hex? - * @return String of P-code source - */ - public String getASMSource(List container, List knownAddreses, List constantPool, int version, ScriptExportMode exportMode) { - return toString(); - } - - /** - * Translates this function to stack and output. - * - * @param stack Stack - * @param output Output - * @param regNames Register names - * @param variables Variables - * @param functions Functions - * @param staticOperation the value of staticOperation - * @param path the value of path - * @throws java.lang.InterruptedException - */ - public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) throws InterruptedException { - } - - /** - * Pops long value off the stack - * - * @param stack Stack - * @return long value - */ - protected long popLong(Stack stack) { - GraphTargetItem item = stack.pop(); - if (item instanceof DirectValueActionItem) { - if (((DirectValueActionItem) item).value instanceof Long) { - return (long) (Long) ((DirectValueActionItem) item).value; - } - } - return 0; - } - - /** - * Converts action index to address in the specified list of actions - * - * @param actions List of actions - * @param ip Action index - * @param version SWF version - * @return address - */ - public static long ip2adr(List actions, int ip, int version) { - /* List actions=new ArrayList(); - for(GraphSourceItem s:sources){ - if(s instanceof Action){ - actions.add((Action)s); - } - }*/ - if (ip >= actions.size()) { - if (actions.isEmpty()) { - return 0; - } - return actions.get(actions.size() - 1).getAddress() + actions.get(actions.size() - 1).getBytes(version).length; - } - if (ip == -1) { - return 0; - } - return actions.get(ip).getAddress(); - } - - /** - * Converts address to action index in the specified list of actions - * - * @param actions List of actions - * @param addr Address - * @param version SWF version - * @return action index - */ - public static int adr2ip(List actions, long addr, int version) { - for (int ip = 0; ip < actions.size(); ip++) { - if (actions.get(ip).getAddress() == addr) { - return ip; - } - } - if (actions.size() > 0) { - long outpos = actions.get(actions.size() - 1).getAddress() + actions.get(actions.size() - 1).getBytes(version).length; - if (addr == outpos) { - return actions.size(); - } - } - return -1; - } - - public static List actionsToTree(List actions, int version, int staticOperation, String path) throws InterruptedException { - return actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, version, staticOperation, path); - } - - /** - * Converts list of actions to ActionScript source code - * - * @param asm - * @param actions List of actions - * @param path - * @param writer - * @throws java.lang.InterruptedException - */ - public static void actionsToSource(final ASMSource asm, final List actions, final String path, GraphTextWriter writer) throws InterruptedException { - writer.suspendMeasure(); - List tree = null; - Throwable convertException = null; - int timeout = Configuration.decompilationTimeoutSingleMethod.get(); - try { - tree = CancellableWorker.call(new Callable>() { - @Override - public List call() throws Exception { - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List tree = actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, asm.getSwf().version, staticOperation, path); - Graph.graphToString(tree, new NulWriter(), new LocalData()); - return tree; - } - }, timeout, TimeUnit.SECONDS); - } catch (TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) { - Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex); - convertException = ex; - if (ex instanceof ExecutionException && ex.getCause() instanceof Exception) { - convertException = (Exception) ex.getCause(); - } - } - writer.continueMeasure(); - - asm.getActionSourcePrefix(writer); - if (convertException == null) { - Graph.graphToString(tree, writer, new LocalData()); - } else if (convertException instanceof TimeoutException) { - Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", convertException); - Helper.appendTimeoutComment(writer, timeout); - } else { - Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", convertException); - Helper.appendErrorComment(writer, convertException); - } - asm.getActionSourceSuffix(writer); - } - - /** - * Converts list of actions to List of treeItems - * - * @param regNames Register names - * @param variables - * @param functions - * @param actions List of actions - * @param version SWF version - * @param staticOperation - * @param path - * @return List of treeItems - * @throws java.lang.InterruptedException - */ - public static List actionsToTree(HashMap regNames, HashMap variables, HashMap functions, List actions, int version, int staticOperation, String path) throws InterruptedException { - //Stack stack = new Stack(); - return ActionGraph.translateViaGraph(regNames, variables, functions, actions, version, staticOperation, path); - //return actionsToTree(regNames, stack, actions, 0, actions.size() - 1, version); - } - - @Override - public void translate(BaseLocalData localData, Stack stack, List output, int staticOperation, String path) throws InterruptedException { - ActionLocalData aLocalData = (ActionLocalData) localData; - translate(stack, output, aLocalData.regNames, aLocalData.variables, aLocalData.functions, staticOperation, path); - } - - @Override - public boolean isJump() { - return false; - } - - @Override - public boolean isBranch() { - return false; - } - - @Override - public boolean isExit() { - return false; - } - - @Override - public long getOffset() { - return getAddress(); - } - - @Override - public List getBranches(GraphSource code) { - return new ArrayList<>(); - } - - @Override - public boolean isIgnored() { - return ignored; - } - - @Override - public void setIgnored(boolean ignored, int pos) { - this.ignored = ignored; - } - - private static class Loop { - - public long loopContinue; - public long loopBreak; - public int continueCount = 0; - public int breakCount = 0; - - public Loop(long loopContinue, long loopBreak) { - this.loopContinue = loopContinue; - this.loopBreak = loopBreak; - } - - @Override - public String toString() { - return "[Loop continue:" + loopContinue + ", break:" + loopBreak + "]"; - } - } - - private static void log(String s) { - logger.fine(s); - } - - public static List actionsPartToTree(HashMap registerNames, HashMap variables, HashMap functions, Stack stack, List actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException { - if (start < actions.size() && (end > 0) && (start > 0)) { - log("Entering " + start + "-" + end + (actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : "")); - } - ActionLocalData localData = new ActionLocalData(registerNames, variables, functions); - List output = new ArrayList<>(); - int ip = start; - boolean isWhile = false; - boolean isForIn = false; - GraphTargetItem inItem = null; - int loopStart = 0; - loopip: - while (ip <= end) { - - long addr = ip2adr(actions, ip, version); - if (ip > end) { - break; - } - if (ip >= actions.size()) { - output.add(new ScriptEndItem()); - break; - } - Action action = actions.get(ip); - if (action.isIgnored()) { - ip++; - continue; - } - if (action instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) action; - //List out=actionsPartToTree(new HashMap(), new HashMap(),new HashMap(), new Stack(), src, ip+1,endip-1 , version); - long endAddr = action.getAddress() + cnt.getHeaderSize(); - String cntName = cnt.getName(); - List> outs = new ArrayList<>(); - HashMap variables2 = Helper.deepCopy(variables); - if (cnt instanceof ActionDefineFunction || cnt instanceof ActionDefineFunction2) { - for (int r = 0; r < 256; r++) { - if (variables2.containsKey("__register" + r)) { - variables2.remove("__register" + r); - } - } - } - for (long size : cnt.getContainerSizes()) { - if (size == 0) { - outs.add(new ArrayList()); - continue; - } - List out; - try { - out = ActionGraph.translateViaGraph(cnt.getRegNames(), variables2, functions, actions.subList(adr2ip(actions, endAddr, version), adr2ip(actions, endAddr + size, version)), version, staticOperation, path + (cntName == null ? "" : "/" + cntName)); - } catch (OutOfMemoryError | TranslateException | StackOverflowError ex2) { - Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error in: " + path, ex2); - if (ex2 instanceof OutOfMemoryError) { - System.gc(); - } - out = new ArrayList<>(); - out.add(new CommentItem(new String[]{ - "", - " * " + AppStrings.translate("decompilationError"), - " * " + AppStrings.translate("decompilationError.obfuscated"), - " * " + AppStrings.translate("decompilationError.errorType") + ": " - + ex2.getClass().getSimpleName(), - ""})); - } - outs.add(out); - endAddr += size; - } - ((GraphSourceItemContainer) action).translateContainer(outs, stack, output, registerNames, variables, functions); - ip = adr2ip(actions, endAddr, version); - continue; - } - - //return in for..in - if ((action instanceof ActionPush) && (((ActionPush) action).values.size() == 1) && (((ActionPush) action).values.get(0) instanceof Null)) { - if (ip + 3 <= end) { - if ((actions.get(ip + 1) instanceof ActionEquals) || (actions.get(ip + 1) instanceof ActionEquals2)) { - if (actions.get(ip + 2) instanceof ActionNot) { - if (actions.get(ip + 3) instanceof ActionIf) { - ActionIf aif = (ActionIf) actions.get(ip + 3); - if (adr2ip(actions, ip2adr(actions, ip + 4, version) + aif.getJumpOffset(), version) == ip) { - ip += 4; - continue; - } - } - } - } - } - } - - /*ActionJump && ActionIf removed*/ - /*if ((action instanceof ActionEnumerate2) || (action instanceof ActionEnumerate)) { - loopStart = ip + 1; - isForIn = true; - ip += 4; - action.translate(localData, stack, output); - EnumerateActionItem en = (EnumerateActionItem) stack.peek(); - inItem = en.object; - continue; - } else*/ /*if (action instanceof ActionTry) { - ActionTry atry = (ActionTry) action; - List tryCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.tryBody, version); - ActionItem catchName; - if (atry.catchInRegisterFlag) { - catchName = new DirectValueActionItem(atry, -1, new RegisterNumber(atry.catchRegister), new ArrayList()); - } else { - catchName = new DirectValueActionItem(atry, -1, atry.catchName, new ArrayList()); - } - List catchExceptions = new ArrayList(); - catchExceptions.add(catchName); - List> catchCommands = new ArrayList>(); - catchCommands.add(ActionGraph.translateViaGraph(registerNames, variables, functions, atry.catchBody, version)); - List finallyCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.finallyBody, version); - output.add(new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands)); - } else if (action instanceof ActionWith) { - ActionWith awith = (ActionWith) action; - List withCommands = ActionGraph.translateViaGraph(registerNames, variables, functions,new ArrayList() , version); //TODO:parse with actions - output.add(new WithActionItem(action, stack.pop(), withCommands)); - } else */ if (false) { - } /*if (action instanceof ActionStoreRegister) { - if ((ip + 1 <= end) && (actions.get(ip + 1) instanceof ActionPop)) { - action.translate(localData, stack, output); - stack.pop(); - ip++; - } else { - try { - action.translate(localData, stack, output); - } catch (Exception ex) { - //ignore - } - } - } */ /*else if (action instanceof ActionStrictEquals) { - if ((ip + 1 < actions.size()) && (actions.get(ip + 1) instanceof ActionIf)) { - List caseValues = new ArrayList(); - List> caseCommands = new ArrayList>(); - caseValues.add(stack.pop()); - ActionItem switchedObject = stack.pop(); - if (output.size() > 0) { - if (output.get(output.size() - 1) instanceof StoreRegisterActionItem) { - output.remove(output.size() - 1); - } - } - int caseStart = ip + 2; - List caseBodyIps = new ArrayList(); - long defaultAddr = 0; - caseBodyIps.add(adr2ip(actions, ((ActionIf) actions.get(ip + 1)).getRef(version), version)); - ip++; - do { - ip++; - if ((actions.get(ip - 1) instanceof ActionStrictEquals) && (actions.get(ip) instanceof ActionIf)) { - caseValues.add(actionsToStackTree(registerNames, jumpsOrIfs, actions, constants, caseStart, ip - 2, version).pop()); - caseStart = ip + 1; - caseBodyIps.add(adr2ip(actions, ((ActionIf) actions.get(ip)).getRef(version), version)); - if (actions.get(ip + 1) instanceof ActionJump) { - defaultAddr = ((ActionJump) actions.get(ip + 1)).getRef(version); - ip = adr2ip(actions, defaultAddr, version); - break; - } - } - } while (ip < end); - - for (int i = 0; i < caseBodyIps.size(); i++) { - int caseEnd = ip - 1; - if (i < caseBodyIps.size() - 1) { - caseEnd = caseBodyIps.get(i + 1) - 1; - } - caseCommands.add(actionsToTree(registerNames, unknownJumps, loopList, jumpsOrIfs, stack, constants, actions, caseBodyIps.get(i), caseEnd, version)); - } - output.add(new SwitchActionItem(action, defaultAddr, switchedObject, caseValues, caseCommands, null)); - continue; - } else { - action.translate(stack, constants, output, registerNames); - } - } */ else { - - if (action instanceof ActionStore) { - ActionStore store = (ActionStore) action; - store.setStore(actions.subList(ip + 1, ip + 1 + store.getStoreSize())); - ip = ip + 1 + store.getStoreSize() - 1/*ip++ will be next*/; - } - - try { - action.translate(localData, stack, output, staticOperation, path); - } catch (EmptyStackException ese) { - Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error in: " + path, ese); - output.add(new UnsupportedActionItem(action, "Empty stack")); - } - - } - - ip++; - } - //output = checkClass(output); - log("Leaving " + start + "-" + end); - return output; - } - - public static GraphTargetItem getWithoutGlobal(GraphTargetItem ti) { - GraphTargetItem t = ti; - if (!(t instanceof GetMemberActionItem)) { - return ti; - } - GetMemberActionItem lastMember = null; - while (((GetMemberActionItem) t).object instanceof GetMemberActionItem) { - lastMember = (GetMemberActionItem) t; - t = ((GetMemberActionItem) t).object; - } - if (((GetMemberActionItem) t).object instanceof GetVariableActionItem) { - GetVariableActionItem v = (GetVariableActionItem) ((GetMemberActionItem) t).object; - if (v.name instanceof DirectValueActionItem) { - if (((DirectValueActionItem) v.name).value instanceof String) { - if (((DirectValueActionItem) v.name).value.equals("_global")) { - GetVariableActionItem gvt = new GetVariableActionItem(null, ((GetMemberActionItem) t).memberName); - if (lastMember == null) { - return gvt; - } else { - lastMember.object = gvt; - } - } - } - } - } - return ti; - } - - public static List checkClass(List output) { - if (true) { - //return output; - } - List ret = new ArrayList<>(); - List functions = new ArrayList<>(); - List staticFunctions = new ArrayList<>(); - List> vars = new ArrayList<>(); - List> staticVars = new ArrayList<>(); - GraphTargetItem className; - GraphTargetItem extendsOp = null; - List implementsOp = new ArrayList<>(); - boolean ok = true; - int prevCount = 0; - for (GraphTargetItem t : output) { - if (t instanceof IfItem) { - IfItem it = (IfItem) t; - if (it.expression instanceof NotItem) { - NotItem nti = (NotItem) it.expression; - if ((nti.value instanceof GetMemberActionItem) || (nti.value instanceof GetVariableActionItem)) { - if (true) { //it.onFalse.isEmpty()){ //||(it.onFalse.get(0) instanceof UnsupportedActionItem)) { - if ((it.onTrue.size() == 1) && (it.onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) it.onTrue.get(0)).value instanceof NewObjectActionItem)) { - //ignore - } else { - List parts = it.onTrue; - className = getWithoutGlobal(nti.value); - if (parts.size() >= 1) { - int ipos = 0; - while ((parts.get(ipos) instanceof IfItem) - && ((((IfItem) parts.get(ipos)).onTrue.size() == 1) && (((IfItem) parts.get(ipos)).onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) ((IfItem) parts.get(ipos)).onTrue.get(0)).value instanceof NewObjectActionItem))) { - - ipos++; - } - if (parts.get(ipos) instanceof ExtendsActionItem) { - ExtendsActionItem et = (ExtendsActionItem) parts.get(ipos); - extendsOp = getWithoutGlobal(et.superclass); - ipos++; - } - if (parts.get(ipos) instanceof StoreRegisterActionItem) { - StoreRegisterActionItem sr = (StoreRegisterActionItem) parts.get(ipos); - int instanceReg = sr.register.number; - if (sr.value instanceof GetMemberActionItem) { - GetMemberActionItem gm = (GetMemberActionItem) sr.value; - //gm.memberName should be "prototype" - if (gm.object instanceof TemporaryRegister) { - TemporaryRegister tm = (TemporaryRegister) gm.object; - int classReg = tm.getRegId(); - if (tm.value instanceof SetMemberActionItem) { - SetMemberActionItem sm = (SetMemberActionItem) tm.value; - if (sm.value instanceof StoreRegisterActionItem) { - sr = (StoreRegisterActionItem) sm.value; - if (sr.value instanceof FunctionActionItem) { - ((FunctionActionItem) (sr.value)).calculatedFunctionName = (className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className; - functions.add((FunctionActionItem) sr.value); - - for (; ipos < parts.size(); ipos++) { - if (parts.get(ipos) instanceof ImplementsOpActionItem) { - ImplementsOpActionItem io = (ImplementsOpActionItem) parts.get(ipos); - implementsOp = io.superclasses; - continue; - } - if (parts.get(ipos) instanceof SetMemberActionItem) { - sm = (SetMemberActionItem) parts.get(ipos); - int rnum = -1; - if (sm.object instanceof DirectValueActionItem) { - DirectValueActionItem dv = (DirectValueActionItem) sm.object; - if (dv.value instanceof RegisterNumber) { - RegisterNumber rn = (RegisterNumber) dv.value; - rnum = rn.number; - } - } - if (sm.object instanceof TemporaryRegister) { - rnum = ((TemporaryRegister) sm.object).getRegId(); - } - if (rnum == instanceReg) { - if (sm.value instanceof FunctionActionItem) { - ((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName; - functions.add((FunctionActionItem) sm.value); - } else { - vars.add(new MyEntry<>(sm.objectName, sm.value)); - } - } else if (rnum == classReg) { - if (sm.value instanceof FunctionActionItem) { - ((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName; - staticFunctions.add((FunctionActionItem) sm.value); - } else { - staticVars.add(new MyEntry<>(sm.objectName, sm.value)); - } - } - - } - } - - } - - } - } - List output2 = new ArrayList<>(); - for (int i = 0; i < prevCount; i++) { - output2.add(output.get(i)); - } - output2.add(new ClassActionItem(className, extendsOp, implementsOp, null/*FIXME*/, functions, vars, staticFunctions, staticVars)); - return output2; - } - } - } else if (parts.get(ipos) instanceof SetMemberActionItem) { - SetMemberActionItem sm = (SetMemberActionItem) parts.get(0); - if (sm.value instanceof FunctionActionItem) { - FunctionActionItem f = (FunctionActionItem) sm.value; - if (f.actions.isEmpty()) { - if (parts.size() == 2) { - if (parts.get(1) instanceof ImplementsOpActionItem) { - ImplementsOpActionItem iot = (ImplementsOpActionItem) parts.get(1); - implementsOp = iot.superclasses; - } else { - ok = false; - break; - } - } - List output2 = new ArrayList<>(); - for (int i = 0; i < prevCount; i++) { - output2.add(output.get(i)); - } - output2.add(new InterfaceActionItem(sm.objectName, implementsOp)); - return output2; - } - } - } - } - } - } else { - ok = false; - } - } else { - ok = false; - } - } else { - ok = false; - } - } else { - prevCount++; - //ok = false; - } - if (!ok) { - break; - } - } - return output; - } - - @Override - public boolean ignoredLoops() { - return false; - } - - public static List removeNops(long address, List actions, int version, long swfPos, String path) { - List ret = actions; - if (true) { - //return ret; - } - String s = null; - try { - HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); - Action.actionsToString(new ArrayList(), address, ret, null, version, ScriptExportMode.PCODE, writer, swfPos, path); - s = writer.toString(); - ret = ASMParser.parse(address, swfPos, true, s, SWF.DEFAULT_VERSION, false); - } catch (IOException | ParseException ex) { - Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error. path: " + path, ex); - } - return ret; - } - - public static void setConstantPool(List actions, ConstantPool cpool) { - for (GraphSourceItem a : actions) { - if (a instanceof ActionPush) { - if (cpool != null) { - ((ActionPush) a).constantPool = cpool.constants; - } - } - if (a instanceof ActionDefineFunction) { - if (cpool != null) { - //((ActionDefineFunction) a).setConstantPool(cpool.constants,actions); - } - } - if (a instanceof ActionDefineFunction2) { - if (cpool != null) { - //((ActionDefineFunction2) a).setConstantPool(cpool.constants,actions); - } - } - } - } - - public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) { - writer.appendNoHilight(getASMSource(container, knownAddreses, constantPool, version, exportMode)); - return writer; - } - - public static double toFloatPoint(Object o) { - if (o instanceof Double) { - return (Double) o; - } - if (o instanceof Integer) { - return (Integer) o; - } - if (o instanceof Long) { - return (Long) o; - } - if (o instanceof String) { - try { - return Double.parseDouble((String) o); - } catch (NumberFormatException nfe) { - return 0; - } - } - return 0; - } - - public static GraphTargetItem gettoset(GraphTargetItem get, GraphTargetItem value, List variables) { - GraphTargetItem ret = get; - boolean boxed = false; - if (get instanceof VariableActionItem) { - boxed = true; - ret = ((VariableActionItem) ret).getBoxedValue(); - } - if (ret instanceof GetVariableActionItem) { - GetVariableActionItem gv = (GetVariableActionItem) ret; - ret = new SetVariableActionItem(null, gv.name, value); - } else if (ret instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) ret; - ret = new SetMemberActionItem(null, mem.object, mem.memberName, value); - } else if ((ret instanceof DirectValueActionItem) && ((DirectValueActionItem) ret).value instanceof RegisterNumber) { - ret = new StoreRegisterActionItem(null, (RegisterNumber) ((DirectValueActionItem) ret).value, value, false); - } else if (ret instanceof GetPropertyActionItem) { - GetPropertyActionItem gp = (GetPropertyActionItem) ret; - ret = new SetPropertyActionItem(null, gp.target, gp.propertyIndex, value); - } - if (boxed) { - GraphTargetItem b = ret; - ret = new VariableActionItem(((VariableActionItem) get).getVariableName(), value, ((VariableActionItem) get).isDefinition()); - ((VariableActionItem) ret).setBoxedValue((ActionItem) b); - variables.remove((VariableActionItem) get); - variables.add((VariableActionItem) ret); - } - return ret; - } - - @Override - public boolean isDeobfuscatePop() { - 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; + +import com.jpexs.decompiler.flash.AppStrings; +import com.jpexs.decompiler.flash.BaseLocalData; +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.model.ActionItem; +import com.jpexs.decompiler.flash.action.model.ConstantPool; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.ExtendsActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetPropertyActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.ImplementsOpActionItem; +import com.jpexs.decompiler.flash.action.model.NewObjectActionItem; +import com.jpexs.decompiler.flash.action.model.SetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.SetPropertyActionItem; +import com.jpexs.decompiler.flash.action.model.SetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem; +import com.jpexs.decompiler.flash.action.model.TemporaryRegister; +import com.jpexs.decompiler.flash.action.model.UnsupportedActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.parser.ParseException; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParsedSymbol; +import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; +import com.jpexs.decompiler.flash.action.parser.pcode.FlasmLexer; +import com.jpexs.decompiler.flash.action.parser.script.VariableActionItem; +import com.jpexs.decompiler.flash.action.special.ActionEnd; +import com.jpexs.decompiler.flash.action.special.ActionStore; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +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.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.RegisterNumber; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +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.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.helpers.HilightedTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSource; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.TranslateException; +import com.jpexs.decompiler.graph.model.CommentItem; +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.ScriptEndItem; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; +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; + +/** + * Represents one ACTIONRECORD, also has some static method to work with Actions + */ +public class Action implements GraphSourceItem { + + public Action replaceWith; + private boolean ignored = false; + /** + * Action type identifier + */ + public int actionCode; + /** + * Length of action data + */ + public int actionLength; + private long address; + + public static final String[] reservedWords = { + "as", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "each", "else", + "extends", "false", "finally", "for", "function", "get", "if", "implements", "import", "in", "instanceof", + "interface", "internal", "is", "native", "new", "null", "override", "package", "private", "protected", "public", + "return", "set", "super", "switch", "this", "throw", "true", "try", "typeof", "use", "var", /*"void",*/ "while", + "with", "dynamic", "default", "final", "in"}; + + public static boolean isReservedWord(String s) { + if (s == null) { + return false; + } + for (String rw : reservedWords) { + if (rw.equals(s.trim())) { + return true; + } + } + return false; + } + + + /** + * Names of ActionScript properties + */ + public static final String[] propertyNames = new String[]{ + "_X", + "_Y", + "_xscale", + "_yscale", + "_currentframe", + "_totalframes", + "_alpha", + "_visible", + "_width", + "_height", + "_rotation", + "_target", + "_framesloaded", + "_name", + "_droptarget", + "_url", + "_highquality", + "_focusrect", + "_soundbuftime", + "_quality", + "_xmouse", + "_ymouse" + }; + public static final List propertyNamesList = Arrays.asList(propertyNames); + private static final Logger logger = Logger.getLogger(Action.class.getName()); + + /** + * Constructor + * + * @param actionCode Action type identifier + * @param actionLength Length of action data + */ + public Action(int actionCode, int actionLength) { + this.actionCode = actionCode; + this.actionLength = actionLength; + } + + public Action() { + } + + /** + * Returns address of this action + * + * @return address of this action + */ + public long getAddress() { + return address; + } + + /** + * Gets all addresses which are referenced from this action and/or + * subactions + * + * @param version SWF version + * @return List of addresses + */ + public List getAllRefs(int version) { + List ret = new ArrayList<>(); + return ret; + } + + /** + * Gets all ActionIf or ActionJump actions from subactions + * + * @return List of actions + */ + public List getAllIfsOrJumps() { + List ret = new ArrayList<>(); + return ret; + } + + /** + * Gets all ActionIf or ActionJump actions from list of actions + * + * @param list List of actions + * @return List of actions + */ + public static List getActionsAllIfsOrJumps(List list) { + List ret = new ArrayList<>(); + for (Action a : list) { + List part = a.getAllIfsOrJumps(); + ret.addAll(part); + } + return ret; + } + + /** + * Gets all addresses which are referenced from the list of actions + * + * @param list List of actions + * @param version SWF version + * @return List of addresses + */ + public static List getActionsAllRefs(List list, int version) { + List ret = new ArrayList<>(); + for (Action a : list) { + if (a.replaceWith != null) { + a.replaceWith.setAddress(a.getAddress(), version, false); + ret.addAll(a.replaceWith.getAllRefs(version)); + } + List part = a.getAllRefs(version); + ret.addAll(part); + } + return ret; + } + + /** + * Sets address of this instruction + * + * @param address Address + * @param version SWF version + */ + public final void setAddress(long address, int version) { + setAddress(address, version, true); + } + + public void setAddress(long address, int version, boolean recursive) { + this.address = address; + } + + /** + * Returns a string representation of the object + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return "Action" + actionCode; + } + + /** + * Reads String from FlasmLexer + * + * @param lex FlasmLexer + * @return String value + * @throws IOException + * @throws ParseException When read object is not String + */ + protected String lexString(FlasmLexer lex) throws IOException, ParseException { + ASMParsedSymbol symb = lex.yylex(); + if (symb.type != ASMParsedSymbol.TYPE_STRING) { + throw new ParseException("String expected", lex.yyline()); + } + return (String) symb.value; + } + + /** + * Reads Block startServer from FlasmLexer + * + * @param lex FlasmLexer + * @throws IOException + * @throws ParseException When read object is not Block startServer + */ + protected void lexBlockOpen(FlasmLexer lex) throws IOException, ParseException { + ASMParsedSymbol symb = lex.yylex(); + if (symb.type != ASMParsedSymbol.TYPE_BLOCK_START) { + throw new ParseException("Block startServer ", lex.yyline()); + } + } + + /** + * Reads Identifier from FlasmLexer + * + * @param lex FlasmLexer + * @return Identifier name + * @throws IOException + * @throws ParseException When read object is not Identifier + */ + protected String lexIdentifier(FlasmLexer lex) throws IOException, ParseException { + ASMParsedSymbol symb = lex.yylex(); + if (symb.type != ASMParsedSymbol.TYPE_IDENTIFIER) { + throw new ParseException("Identifier expected", lex.yyline()); + } + return (String) symb.value; + } + + /** + * Reads long value from FlasmLexer + * + * @param lex FlasmLexer + * @return long value + * @throws IOException + * @throws ParseException When read object is not long value + */ + protected long lexLong(FlasmLexer lex) throws IOException, ParseException { + ASMParsedSymbol symb = lex.yylex(); + if (symb.type != ASMParsedSymbol.TYPE_INTEGER) { + throw new ParseException("Integer expected", lex.yyline()); + } + return (Long) symb.value; + } + + /** + * Reads boolean value from FlasmLexer + * + * @param lex FlasmLexer + * @return boolean value + * @throws IOException + * @throws ParseException When read object is not boolean value + */ + protected boolean lexBoolean(FlasmLexer lex) throws IOException, ParseException { + ASMParsedSymbol symb = lex.yylex(); + if (symb.type != ASMParsedSymbol.TYPE_BOOLEAN) { + throw new ParseException("Boolean expected", lex.yyline()); + } + return (Boolean) symb.value; + } + + /** + * Gets action converted to bytes + * + * @param version SWF version + * @return Array of bytes + */ + public byte[] getBytes(int version) { + return surroundWithAction(new byte[0], version); + } + + /** + * Surrounds byte array with Action header + * + * @param data Byte array + * @param version SWF version + * @return Byte array + */ + protected byte[] surroundWithAction(byte[] data, int version) { + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + SWFOutputStream sos2 = new SWFOutputStream(baos2, version); + try { + sos2.writeUI8(actionCode); + if (actionCode >= 0x80) { + sos2.writeUI16(data.length); + } + sos2.write(data); + sos2.close(); + } catch (IOException e) { + } + return baos2.toByteArray(); + } + + /** + * Converts list of Actions to bytes + * + * @param list List of actions + * @param addZero Whether or not to add 0 UI8 value to the end + * @param version SWF version + * @return Array of bytes + */ + public static byte[] actionsToBytes(List list, boolean addZero, int version) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Action lastAction = null; + for (Action a : list) { + try { + lastAction = a; + baos.write(a.getBytes(version)); + } catch (IOException e) { + } + } + if (addZero && (lastAction == null || !(lastAction instanceof ActionEnd))) { + baos.write(0); + } + return baos.toByteArray(); + } + + /** + * Set addresses of actions in the list + * + * @param list List of actions + * @param baseAddress Address of first action in the list + * @param version SWF version + */ + public static void setActionsAddresses(List list, long baseAddress, int version) { + long offset = baseAddress; + for (Action a : list) { + a.setAddress(offset, version); + offset += a.getBytes(version).length; + } + } + + /** + * Converts list of actions to ASM source + * + * @param listeners + * @param address + * @param list List of actions + * @param importantOffsets List of important offsets to mark as labels + * @param version SWF version + * @param exportMode PCode or hex? + * @param writer + * @param path + * @return HilightedTextWriter + */ + public static GraphTextWriter actionsToString(List listeners, long address, List list, List importantOffsets, int version, ScriptExportMode exportMode, GraphTextWriter writer, String path) { + return actionsToString(listeners, address, list, importantOffsets, new ArrayList(), version, exportMode, writer, path); + } + + /** + * Converts list of actions to ASM source + * + * @param listeners + * @param address + * @param list List of actions + * @param importantOffsets List of important offsets to mark as labels + * @param constantPool Constant pool + * @param version SWF version + * @param hex Add hexadecimal? + * @param path + * @return HilightedTextWriter + */ + private static GraphTextWriter actionsToString(List listeners, long address, List list, List importantOffsets, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer, String path) { + long offset; + if (importantOffsets == null) { + //setActionsAddresses(list, 0, version); + importantOffsets = getActionsAllRefs(list, version); + } + /*List cps = SWFInputStream.getConstantPool(new ArrayList(), new ActionGraphSource(list, version, new HashMap(), new HashMap(), new HashMap()), 0, version, path); + if (!cps.isEmpty()) { + setConstantPool(list, cps.get(cps.size() - 1)); + }*/ + HashMap> containers = new HashMap<>(); + HashMap containersPos = new HashMap<>(); + offset = address; + int pos = -1; + boolean lastPush = false; + for (GraphSourceItem s : list) { + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress(AppStrings.translate("disassemblingProgress.toString"), pos + 2, list.size()); + } + Action a = null; + if (s instanceof Action) { + a = (Action) s; + } + pos++; + if (exportMode == ScriptExportMode.PCODE_HEX) { + if (lastPush) { + writer.newLine(); + lastPush = false; + } + writer.appendNoHilight("; "); + writer.appendNoHilight(Helper.bytesToHexString(a.getBytes(version))); + writer.newLine(); + } + offset = a.getAddress(); + + if ((!(a.isIgnored())) && (a instanceof GraphSourceItemContainer)) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; + containersPos.put(cnt, 0); + List sizes = cnt.getContainerSizes(); + long addr = ((Action) cnt).getAddress() + cnt.getHeaderSize(); + for (Long size : sizes) { + addr += size; + if (size == 0) { + continue; + } + if (!containers.containsKey(addr)) { + containers.put(addr, new ArrayList()); + } + containers.get(addr).add(cnt); + } + } + + if (containers.containsKey(offset)) { + for (int i = 0; i < containers.get(offset).size(); i++) { + writer.appendNoHilight("}").newLine(); + GraphSourceItemContainer cnt = containers.get(offset).get(i); + int cntPos = containersPos.get(cnt); + writer.appendNoHilight(cnt.getASMSourceBetween(cntPos)); + cntPos++; + containersPos.put(cnt, cntPos); + } + } + + if (Configuration.showAllAddresses.get() || importantOffsets.contains(offset)) { + if (lastPush) { + writer.newLine(); + lastPush = false; + } + writer.appendNoHilight("loc"); + writer.appendNoHilight(Helper.formatAddress(offset)); + writer.appendNoHilight(":"); + } + + if (a.replaceWith != null) { + if (lastPush) { + writer.newLine(); + lastPush = false; + } + writer.append("", offset); + writer.appendNoHilight(a.replaceWith.getASMSource(list, importantOffsets, constantPool, version, exportMode)); + writer.newLine(); + } else if (a.isIgnored()) { + if (lastPush) { + writer.newLine(); + lastPush = false; + } + int len = 0; + if (pos + 1 < list.size()) { + len = (int) (((Action) (list.get(pos + 1))).getAddress() - a.getAddress()); + } else { + len = a.getBytes(version).length; + } + if (!(a instanceof ActionEnd)) { + for (int i = 0; i < len; i++) { + writer.appendNoHilight("Nop").newLine(); + } + } + } else { + //if (!(a instanceof ActionNop)) { + String add = ""; + if (a instanceof ActionIf) { + add = " change: " + ((ActionIf) a).getJumpOffset(); + } + if (a instanceof ActionJump) { + add = " change: " + ((ActionJump) a).getJumpOffset(); + } + add = "; ofs" + Helper.formatAddress(offset) + add; + add = ""; + if ((a instanceof ActionPush) && lastPush) { + writer.appendNoHilight(" "); + ((ActionPush) a).paramsToStringReplaced(list, importantOffsets, constantPool, version, exportMode, writer); + } else { + if (lastPush) { + writer.newLine(); + lastPush = false; + } + + writer.append("", offset); + + int fixBranch = -1; + if (a instanceof ActionIf) { + ActionIf aif = (ActionIf) a; + if (aif.jumpUsed && !aif.ignoreUsed) { + fixBranch = 0; + } + if (!aif.jumpUsed && aif.ignoreUsed) { + fixBranch = 1; + } + } + + if (fixBranch > -1) { + writer.appendNoHilight("FFDec_DeobfuscatePop").newLine(); + if (fixBranch == 0) { //jump + writer.appendNoHilight("Jump loc"); + writer.appendNoHilight(Helper.formatAddress(a.getAddress() + a.getBytes(version).length + ((ActionIf) a).getJumpOffset())); + } else { + //nojump, ignore + } + } else { + a.getASMSourceReplaced(list, importantOffsets, constantPool, version, exportMode, writer); + } + writer.appendNoHilight(a.isIgnored() ? "; ignored" : ""); + writer.appendNoHilight(add); + if (!(a instanceof ActionPush)) { + writer.newLine(); + } + } + if (a instanceof ActionPush) { + lastPush = true; + } else { + lastPush = false; + } + //} + } + offset += a.getBytes(version).length; + } + if (lastPush) { + writer.newLine(); + } + if (containers.containsKey(offset)) { + for (int i = 0; i < containers.get(offset).size(); i++) { + writer.appendNoHilight("}"); + writer.newLine(); + GraphSourceItemContainer cnt = containers.get(offset).get(i); + int cntPos = containersPos.get(cnt); + writer.appendNoHilight(cnt.getASMSourceBetween(cntPos)); + cntPos++; + containersPos.put(cnt, cntPos); + } + } + if (importantOffsets.contains(offset)) { + writer.appendNoHilight("loc"); + writer.appendNoHilight(Helper.formatAddress(offset)); + writer.appendNoHilight(":"); + writer.newLine(); + } + return writer; + } + + /** + * Convert action to ASM source + * + * @param container + * @param knownAddreses List of important offsets to mark as labels + * @param constantPool Constant pool + * @param version SWF version + * @param exportMode PCode or hex? + * @return String of P-code source + */ + public String getASMSource(List container, List knownAddreses, List constantPool, int version, ScriptExportMode exportMode) { + return toString(); + } + + /** + * Translates this function to stack and output. + * + * @param stack Stack + * @param output Output + * @param regNames Register names + * @param variables Variables + * @param functions Functions + * @param staticOperation the value of staticOperation + * @param path the value of path + * @throws java.lang.InterruptedException + */ + public void translate(Stack stack, List output, java.util.HashMap regNames, HashMap variables, HashMap functions, int staticOperation, String path) throws InterruptedException { + } + + /** + * Pops long value off the stack + * + * @param stack Stack + * @return long value + */ + protected long popLong(Stack stack) { + GraphTargetItem item = stack.pop(); + if (item instanceof DirectValueActionItem) { + if (((DirectValueActionItem) item).value instanceof Long) { + return (long) (Long) ((DirectValueActionItem) item).value; + } + } + return 0; + } + + /** + * Converts action index to address in the specified list of actions + * + * @param actions List of actions + * @param ip Action index + * @param version SWF version + * @return address + */ + public static long ip2adr(List actions, int ip, int version) { + /* List actions=new ArrayList(); + for(GraphSourceItem s:sources){ + if(s instanceof Action){ + actions.add((Action)s); + } + }*/ + if (ip >= actions.size()) { + if (actions.isEmpty()) { + return 0; + } + return actions.get(actions.size() - 1).getAddress() + actions.get(actions.size() - 1).getBytes(version).length; + } + if (ip == -1) { + return 0; + } + return actions.get(ip).getAddress(); + } + + /** + * Converts address to action index in the specified list of actions + * + * @param actions List of actions + * @param addr Address + * @param version SWF version + * @return action index + */ + public static int adr2ip(List actions, long addr, int version) { + for (int ip = 0; ip < actions.size(); ip++) { + if (actions.get(ip).getAddress() == addr) { + return ip; + } + } + if (actions.size() > 0) { + long outpos = actions.get(actions.size() - 1).getAddress() + actions.get(actions.size() - 1).getBytes(version).length; + if (addr == outpos) { + return actions.size(); + } + } + return -1; + } + + public static List actionsToTree(List actions, int version, int staticOperation, String path) throws InterruptedException { + return actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, version, staticOperation, path); + } + + /** + * Converts list of actions to ActionScript source code + * + * @param asm + * @param actions List of actions + * @param path + * @param writer + * @throws java.lang.InterruptedException + */ + public static void actionsToSource(final ASMSource asm, final List actions, final String path, GraphTextWriter writer) throws InterruptedException { + writer.suspendMeasure(); + List tree = null; + Throwable convertException = null; + int timeout = Configuration.decompilationTimeoutSingleMethod.get(); + try { + tree = CancellableWorker.call(new Callable>() { + @Override + public List call() throws Exception { + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + List tree = actionsToTree(new HashMap(), new HashMap(), new HashMap(), actions, asm.getSwf().version, staticOperation, path); + Graph.graphToString(tree, new NulWriter(), new LocalData()); + return tree; + } + }, timeout, TimeUnit.SECONDS); + } catch (TimeoutException | ExecutionException | OutOfMemoryError | TranslateException | StackOverflowError ex) { + Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", ex); + convertException = ex; + if (ex instanceof ExecutionException && ex.getCause() instanceof Exception) { + convertException = (Exception) ex.getCause(); + } + } + writer.continueMeasure(); + + asm.getActionSourcePrefix(writer); + if (convertException == null) { + Graph.graphToString(tree, writer, new LocalData()); + } else if (convertException instanceof TimeoutException) { + Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", convertException); + Helper.appendTimeoutComment(writer, timeout); + } else { + Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error", convertException); + Helper.appendErrorComment(writer, convertException); + } + asm.getActionSourceSuffix(writer); + } + + /** + * Converts list of actions to List of treeItems + * + * @param regNames Register names + * @param variables + * @param functions + * @param actions List of actions + * @param version SWF version + * @param staticOperation + * @param path + * @return List of treeItems + * @throws java.lang.InterruptedException + */ + public static List actionsToTree(HashMap regNames, HashMap variables, HashMap functions, List actions, int version, int staticOperation, String path) throws InterruptedException { + //Stack stack = new Stack(); + return ActionGraph.translateViaGraph(regNames, variables, functions, actions, version, staticOperation, path); + //return actionsToTree(regNames, stack, actions, 0, actions.size() - 1, version); + } + + @Override + public void translate(BaseLocalData localData, Stack stack, List output, int staticOperation, String path) throws InterruptedException { + ActionLocalData aLocalData = (ActionLocalData) localData; + translate(stack, output, aLocalData.regNames, aLocalData.variables, aLocalData.functions, staticOperation, path); + } + + @Override + public boolean isJump() { + return false; + } + + @Override + public boolean isBranch() { + return false; + } + + @Override + public boolean isExit() { + return false; + } + + @Override + public long getOffset() { + return getAddress(); + } + + @Override + public List getBranches(GraphSource code) { + return new ArrayList<>(); + } + + @Override + public boolean isIgnored() { + return ignored; + } + + @Override + public void setIgnored(boolean ignored, int pos) { + this.ignored = ignored; + } + + private static class Loop { + + public long loopContinue; + public long loopBreak; + public int continueCount = 0; + public int breakCount = 0; + + public Loop(long loopContinue, long loopBreak) { + this.loopContinue = loopContinue; + this.loopBreak = loopBreak; + } + + @Override + public String toString() { + return "[Loop continue:" + loopContinue + ", break:" + loopBreak + "]"; + } + } + + private static void log(String s) { + logger.fine(s); + } + + public static List actionsPartToTree(HashMap registerNames, HashMap variables, HashMap functions, Stack stack, List actions, int start, int end, int version, int staticOperation, String path) throws InterruptedException { + if (start < actions.size() && (end > 0) && (start > 0)) { + log("Entering " + start + "-" + end + (actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : "")); + } + ActionLocalData localData = new ActionLocalData(registerNames, variables, functions); + List output = new ArrayList<>(); + int ip = start; + boolean isWhile = false; + boolean isForIn = false; + GraphTargetItem inItem = null; + int loopStart = 0; + loopip: + while (ip <= end) { + + long addr = ip2adr(actions, ip, version); + if (ip > end) { + break; + } + if (ip >= actions.size()) { + output.add(new ScriptEndItem()); + break; + } + Action action = actions.get(ip); + if (action.isIgnored()) { + ip++; + continue; + } + if (action instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) action; + //List out=actionsPartToTree(new HashMap(), new HashMap(),new HashMap(), new Stack(), src, ip+1,endip-1 , version); + long endAddr = action.getAddress() + cnt.getHeaderSize(); + String cntName = cnt.getName(); + List> outs = new ArrayList<>(); + HashMap variables2 = Helper.deepCopy(variables); + if (cnt instanceof ActionDefineFunction || cnt instanceof ActionDefineFunction2) { + for (int r = 0; r < 256; r++) { + if (variables2.containsKey("__register" + r)) { + variables2.remove("__register" + r); + } + } + } + for (long size : cnt.getContainerSizes()) { + if (size == 0) { + outs.add(new ArrayList()); + continue; + } + List out; + try { + out = ActionGraph.translateViaGraph(cnt.getRegNames(), variables2, functions, actions.subList(adr2ip(actions, endAddr, version), adr2ip(actions, endAddr + size, version)), version, staticOperation, path + (cntName == null ? "" : "/" + cntName)); + } catch (OutOfMemoryError | TranslateException | StackOverflowError ex2) { + Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error in: " + path, ex2); + if (ex2 instanceof OutOfMemoryError) { + System.gc(); + } + out = new ArrayList<>(); + out.add(new CommentItem(new String[]{ + "", + " * " + AppStrings.translate("decompilationError"), + " * " + AppStrings.translate("decompilationError.obfuscated"), + " * " + AppStrings.translate("decompilationError.errorType") + ": " + + ex2.getClass().getSimpleName(), + ""})); + } + outs.add(out); + endAddr += size; + } + ((GraphSourceItemContainer) action).translateContainer(outs, stack, output, registerNames, variables, functions); + ip = adr2ip(actions, endAddr, version); + continue; + } + + //return in for..in + if ((action instanceof ActionPush) && (((ActionPush) action).values.size() == 1) && (((ActionPush) action).values.get(0) instanceof Null)) { + if (ip + 3 <= end) { + if ((actions.get(ip + 1) instanceof ActionEquals) || (actions.get(ip + 1) instanceof ActionEquals2)) { + if (actions.get(ip + 2) instanceof ActionNot) { + if (actions.get(ip + 3) instanceof ActionIf) { + ActionIf aif = (ActionIf) actions.get(ip + 3); + if (adr2ip(actions, ip2adr(actions, ip + 4, version) + aif.getJumpOffset(), version) == ip) { + ip += 4; + continue; + } + } + } + } + } + } + + /*ActionJump && ActionIf removed*/ + /*if ((action instanceof ActionEnumerate2) || (action instanceof ActionEnumerate)) { + loopStart = ip + 1; + isForIn = true; + ip += 4; + action.translate(localData, stack, output); + EnumerateActionItem en = (EnumerateActionItem) stack.peek(); + inItem = en.object; + continue; + } else*/ /*if (action instanceof ActionTry) { + ActionTry atry = (ActionTry) action; + List tryCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.tryBody, version); + ActionItem catchName; + if (atry.catchInRegisterFlag) { + catchName = new DirectValueActionItem(atry, -1, new RegisterNumber(atry.catchRegister), new ArrayList()); + } else { + catchName = new DirectValueActionItem(atry, -1, atry.catchName, new ArrayList()); + } + List catchExceptions = new ArrayList(); + catchExceptions.add(catchName); + List> catchCommands = new ArrayList>(); + catchCommands.add(ActionGraph.translateViaGraph(registerNames, variables, functions, atry.catchBody, version)); + List finallyCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.finallyBody, version); + output.add(new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands)); + } else if (action instanceof ActionWith) { + ActionWith awith = (ActionWith) action; + List withCommands = ActionGraph.translateViaGraph(registerNames, variables, functions,new ArrayList() , version); //TODO:parse with actions + output.add(new WithActionItem(action, stack.pop(), withCommands)); + } else */ if (false) { + } /*if (action instanceof ActionStoreRegister) { + if ((ip + 1 <= end) && (actions.get(ip + 1) instanceof ActionPop)) { + action.translate(localData, stack, output); + stack.pop(); + ip++; + } else { + try { + action.translate(localData, stack, output); + } catch (Exception ex) { + //ignore + } + } + } */ /*else if (action instanceof ActionStrictEquals) { + if ((ip + 1 < actions.size()) && (actions.get(ip + 1) instanceof ActionIf)) { + List caseValues = new ArrayList(); + List> caseCommands = new ArrayList>(); + caseValues.add(stack.pop()); + ActionItem switchedObject = stack.pop(); + if (output.size() > 0) { + if (output.get(output.size() - 1) instanceof StoreRegisterActionItem) { + output.remove(output.size() - 1); + } + } + int caseStart = ip + 2; + List caseBodyIps = new ArrayList(); + long defaultAddr = 0; + caseBodyIps.add(adr2ip(actions, ((ActionIf) actions.get(ip + 1)).getRef(version), version)); + ip++; + do { + ip++; + if ((actions.get(ip - 1) instanceof ActionStrictEquals) && (actions.get(ip) instanceof ActionIf)) { + caseValues.add(actionsToStackTree(registerNames, jumpsOrIfs, actions, constants, caseStart, ip - 2, version).pop()); + caseStart = ip + 1; + caseBodyIps.add(adr2ip(actions, ((ActionIf) actions.get(ip)).getRef(version), version)); + if (actions.get(ip + 1) instanceof ActionJump) { + defaultAddr = ((ActionJump) actions.get(ip + 1)).getRef(version); + ip = adr2ip(actions, defaultAddr, version); + break; + } + } + } while (ip < end); + + for (int i = 0; i < caseBodyIps.size(); i++) { + int caseEnd = ip - 1; + if (i < caseBodyIps.size() - 1) { + caseEnd = caseBodyIps.get(i + 1) - 1; + } + caseCommands.add(actionsToTree(registerNames, unknownJumps, loopList, jumpsOrIfs, stack, constants, actions, caseBodyIps.get(i), caseEnd, version)); + } + output.add(new SwitchActionItem(action, defaultAddr, switchedObject, caseValues, caseCommands, null)); + continue; + } else { + action.translate(stack, constants, output, registerNames); + } + } */ else { + + if (action instanceof ActionStore) { + ActionStore store = (ActionStore) action; + store.setStore(actions.subList(ip + 1, ip + 1 + store.getStoreSize())); + ip = ip + 1 + store.getStoreSize() - 1/*ip++ will be next*/; + } + + try { + action.translate(localData, stack, output, staticOperation, path); + } catch (EmptyStackException ese) { + Logger.getLogger(Action.class.getName()).log(Level.SEVERE, "Decompilation error in: " + path, ese); + output.add(new UnsupportedActionItem(action, "Empty stack")); + } + + } + + ip++; + } + //output = checkClass(output); + log("Leaving " + start + "-" + end); + return output; + } + + public static GraphTargetItem getWithoutGlobal(GraphTargetItem ti) { + GraphTargetItem t = ti; + if (!(t instanceof GetMemberActionItem)) { + return ti; + } + GetMemberActionItem lastMember = null; + while (((GetMemberActionItem) t).object instanceof GetMemberActionItem) { + lastMember = (GetMemberActionItem) t; + t = ((GetMemberActionItem) t).object; + } + if (((GetMemberActionItem) t).object instanceof GetVariableActionItem) { + GetVariableActionItem v = (GetVariableActionItem) ((GetMemberActionItem) t).object; + if (v.name instanceof DirectValueActionItem) { + if (((DirectValueActionItem) v.name).value instanceof String) { + if (((DirectValueActionItem) v.name).value.equals("_global")) { + GetVariableActionItem gvt = new GetVariableActionItem(null, ((GetMemberActionItem) t).memberName); + if (lastMember == null) { + return gvt; + } else { + lastMember.object = gvt; + } + } + } + } + } + return ti; + } + + public static List checkClass(List output) { + if (true) { + //return output; + } + List ret = new ArrayList<>(); + List functions = new ArrayList<>(); + List staticFunctions = new ArrayList<>(); + List> vars = new ArrayList<>(); + List> staticVars = new ArrayList<>(); + GraphTargetItem className; + GraphTargetItem extendsOp = null; + List implementsOp = new ArrayList<>(); + boolean ok = true; + int prevCount = 0; + for (GraphTargetItem t : output) { + if (t instanceof IfItem) { + IfItem it = (IfItem) t; + if (it.expression instanceof NotItem) { + NotItem nti = (NotItem) it.expression; + if ((nti.value instanceof GetMemberActionItem) || (nti.value instanceof GetVariableActionItem)) { + if (true) { //it.onFalse.isEmpty()){ //||(it.onFalse.get(0) instanceof UnsupportedActionItem)) { + if ((it.onTrue.size() == 1) && (it.onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) it.onTrue.get(0)).value instanceof NewObjectActionItem)) { + //ignore + } else { + List parts = it.onTrue; + className = getWithoutGlobal(nti.value); + if (parts.size() >= 1) { + int ipos = 0; + while ((parts.get(ipos) instanceof IfItem) + && ((((IfItem) parts.get(ipos)).onTrue.size() == 1) && (((IfItem) parts.get(ipos)).onTrue.get(0) instanceof SetMemberActionItem) && (((SetMemberActionItem) ((IfItem) parts.get(ipos)).onTrue.get(0)).value instanceof NewObjectActionItem))) { + + ipos++; + } + if (parts.get(ipos) instanceof ExtendsActionItem) { + ExtendsActionItem et = (ExtendsActionItem) parts.get(ipos); + extendsOp = getWithoutGlobal(et.superclass); + ipos++; + } + if (parts.get(ipos) instanceof StoreRegisterActionItem) { + StoreRegisterActionItem sr = (StoreRegisterActionItem) parts.get(ipos); + int instanceReg = sr.register.number; + if (sr.value instanceof GetMemberActionItem) { + GetMemberActionItem gm = (GetMemberActionItem) sr.value; + //gm.memberName should be "prototype" + if (gm.object instanceof TemporaryRegister) { + TemporaryRegister tm = (TemporaryRegister) gm.object; + int classReg = tm.getRegId(); + if (tm.value instanceof SetMemberActionItem) { + SetMemberActionItem sm = (SetMemberActionItem) tm.value; + if (sm.value instanceof StoreRegisterActionItem) { + sr = (StoreRegisterActionItem) sm.value; + if (sr.value instanceof FunctionActionItem) { + ((FunctionActionItem) (sr.value)).calculatedFunctionName = (className instanceof GetMemberActionItem) ? ((GetMemberActionItem) className).memberName : className; + functions.add((FunctionActionItem) sr.value); + + for (; ipos < parts.size(); ipos++) { + if (parts.get(ipos) instanceof ImplementsOpActionItem) { + ImplementsOpActionItem io = (ImplementsOpActionItem) parts.get(ipos); + implementsOp = io.superclasses; + continue; + } + if (parts.get(ipos) instanceof SetMemberActionItem) { + sm = (SetMemberActionItem) parts.get(ipos); + int rnum = -1; + if (sm.object instanceof DirectValueActionItem) { + DirectValueActionItem dv = (DirectValueActionItem) sm.object; + if (dv.value instanceof RegisterNumber) { + RegisterNumber rn = (RegisterNumber) dv.value; + rnum = rn.number; + } + } + if (sm.object instanceof TemporaryRegister) { + rnum = ((TemporaryRegister) sm.object).getRegId(); + } + if (rnum == instanceReg) { + if (sm.value instanceof FunctionActionItem) { + ((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName; + functions.add((FunctionActionItem) sm.value); + } else { + vars.add(new MyEntry<>(sm.objectName, sm.value)); + } + } else if (rnum == classReg) { + if (sm.value instanceof FunctionActionItem) { + ((FunctionActionItem) sm.value).calculatedFunctionName = sm.objectName; + staticFunctions.add((FunctionActionItem) sm.value); + } else { + staticVars.add(new MyEntry<>(sm.objectName, sm.value)); + } + } + + } + } + + } + + } + } + List output2 = new ArrayList<>(); + for (int i = 0; i < prevCount; i++) { + output2.add(output.get(i)); + } + output2.add(new ClassActionItem(className, extendsOp, implementsOp, null/*FIXME*/, functions, vars, staticFunctions, staticVars)); + return output2; + } + } + } else if (parts.get(ipos) instanceof SetMemberActionItem) { + SetMemberActionItem sm = (SetMemberActionItem) parts.get(0); + if (sm.value instanceof FunctionActionItem) { + FunctionActionItem f = (FunctionActionItem) sm.value; + if (f.actions.isEmpty()) { + if (parts.size() == 2) { + if (parts.get(1) instanceof ImplementsOpActionItem) { + ImplementsOpActionItem iot = (ImplementsOpActionItem) parts.get(1); + implementsOp = iot.superclasses; + } else { + ok = false; + break; + } + } + List output2 = new ArrayList<>(); + for (int i = 0; i < prevCount; i++) { + output2.add(output.get(i)); + } + output2.add(new InterfaceActionItem(sm.objectName, implementsOp)); + return output2; + } + } + } + } + } + } else { + ok = false; + } + } else { + ok = false; + } + } else { + ok = false; + } + } else { + prevCount++; + //ok = false; + } + if (!ok) { + break; + } + } + return output; + } + + @Override + public boolean ignoredLoops() { + return false; + } + + public static List removeNops(long address, List actions, int version, String path) { + List ret = actions; + if (true) { + //return ret; + } + String s = null; + try { + HilightedTextWriter writer = new HilightedTextWriter(Configuration.getCodeFormatting(), false); + Action.actionsToString(new ArrayList(), address, ret, null, version, ScriptExportMode.PCODE, writer, path); + s = writer.toString(); + ret = ASMParser.parse(address, true, s, SWF.DEFAULT_VERSION, false); + } catch (IOException | ParseException ex) { + Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error. path: " + path, ex); + } + return ret; + } + + public static void setConstantPool(List actions, ConstantPool cpool) { + for (GraphSourceItem a : actions) { + if (a instanceof ActionPush) { + if (cpool != null) { + ((ActionPush) a).constantPool = cpool.constants; + } + } + if (a instanceof ActionDefineFunction) { + if (cpool != null) { + //((ActionDefineFunction) a).setConstantPool(cpool.constants,actions); + } + } + if (a instanceof ActionDefineFunction2) { + if (cpool != null) { + //((ActionDefineFunction2) a).setConstantPool(cpool.constants,actions); + } + } + } + } + + public GraphTextWriter getASMSourceReplaced(List container, List knownAddreses, List constantPool, int version, ScriptExportMode exportMode, GraphTextWriter writer) { + writer.appendNoHilight(getASMSource(container, knownAddreses, constantPool, version, exportMode)); + return writer; + } + + public static double toFloatPoint(Object o) { + if (o instanceof Double) { + return (Double) o; + } + if (o instanceof Integer) { + return (Integer) o; + } + if (o instanceof Long) { + return (Long) o; + } + if (o instanceof String) { + try { + return Double.parseDouble((String) o); + } catch (NumberFormatException nfe) { + return 0; + } + } + return 0; + } + + public static GraphTargetItem gettoset(GraphTargetItem get, GraphTargetItem value, List variables) { + GraphTargetItem ret = get; + boolean boxed = false; + if (get instanceof VariableActionItem) { + boxed = true; + ret = ((VariableActionItem) ret).getBoxedValue(); + } + if (ret instanceof GetVariableActionItem) { + GetVariableActionItem gv = (GetVariableActionItem) ret; + ret = new SetVariableActionItem(null, gv.name, value); + } else if (ret instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) ret; + ret = new SetMemberActionItem(null, mem.object, mem.memberName, value); + } else if ((ret instanceof DirectValueActionItem) && ((DirectValueActionItem) ret).value instanceof RegisterNumber) { + ret = new StoreRegisterActionItem(null, (RegisterNumber) ((DirectValueActionItem) ret).value, value, false); + } else if (ret instanceof GetPropertyActionItem) { + GetPropertyActionItem gp = (GetPropertyActionItem) ret; + ret = new SetPropertyActionItem(null, gp.target, gp.propertyIndex, value); + } + if (boxed) { + GraphTargetItem b = ret; + ret = new VariableActionItem(((VariableActionItem) get).getVariableName(), value, ((VariableActionItem) get).isDefinition()); + ((VariableActionItem) ret).setBoxedValue((ActionItem) b); + variables.remove((VariableActionItem) get); + variables.add((VariableActionItem) ret); + } + return ret; + } + + @Override + public boolean isDeobfuscatePop() { + return false; + } +} diff --git a/src/com/jpexs/decompiler/flash/action/ActionListReader.java b/src/com/jpexs/decompiler/flash/action/ActionListReader.java index b863dd152..7defc12d4 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionListReader.java +++ b/src/com/jpexs/decompiler/flash/action/ActionListReader.java @@ -76,7 +76,6 @@ public class ActionListReader { * ActionEndFlag(=0) or end of the stream. * * @param listeners - * @param containerSWFOffset * @param sis * @param version * @param ip @@ -87,13 +86,13 @@ public class ActionListReader { * @throws java.lang.InterruptedException * @throws java.util.concurrent.TimeoutException */ - public static List readActionListTimeout(final List listeners, final long containerSWFOffset, final SWFInputStream sis, final int version, final int ip, final int endIp, final String path) throws IOException, InterruptedException, TimeoutException { + public static List readActionListTimeout(final List listeners, final SWFInputStream sis, final int version, final int ip, final int endIp, final String path) throws IOException, InterruptedException, TimeoutException { try { return CancellableWorker.call(new Callable>() { @Override public List call() throws IOException, InterruptedException { - return readActionList(listeners, containerSWFOffset, sis, version, ip, endIp, path); + return readActionList(listeners, sis, version, ip, endIp, path); } }, Configuration.decompilationTimeoutSingleMethod.get(), TimeUnit.SECONDS); } catch (ExecutionException ex) { @@ -114,7 +113,6 @@ public class ActionListReader { * ActionEndFlag(=0) or end of the stream. * * @param listeners - * @param containerSWFOffset * @param sis * @param version * @param ip @@ -124,7 +122,7 @@ public class ActionListReader { * @throws IOException * @throws java.lang.InterruptedException */ - public static List readActionList(List listeners, long containerSWFOffset, SWFInputStream sis, int version, int ip, int endIp, String path) throws IOException, InterruptedException { + public static List readActionList(List listeners, SWFInputStream sis, int version, int ip, int endIp, String path) throws IOException, InterruptedException { boolean deobfuscate = Configuration.autoDeobfuscate.get(); ConstantPool cpool = new ConstantPool(); @@ -132,7 +130,7 @@ public class ActionListReader { // List of the actions. N. item contains the action which starts in offset N. List actionMap = new ArrayList<>(); List nextOffsets = new ArrayList<>(); - Action entryAction = readActionListAtPos(listeners, containerSWFOffset, cpool, + Action entryAction = readActionListAtPos(listeners, cpool, sis, actionMap, nextOffsets, ip, ip, endIp, version, path, false, new ArrayList()); @@ -193,7 +191,7 @@ public class ActionListReader { if (deobfuscate) { try { - actions = deobfuscateActionList(listeners, containerSWFOffset, actions, version, ip, path); + actions = deobfuscateActionList(listeners, actions, version, ip, path); updateActionLengths(actions, version); removeZeroJumps(actions, version); } catch (OutOfMemoryError | StackOverflowError | TranslateException ex) { @@ -210,7 +208,6 @@ public class ActionListReader { * ActionEndFlag(=0) or end of the stream. * * @param listeners - * @param containerSWFOffset * @param actions * @param version * @param ip @@ -219,7 +216,7 @@ public class ActionListReader { * @throws IOException * @throws java.lang.InterruptedException */ - public static List deobfuscateActionList(List listeners, long containerSWFOffset, List actions, int version, int ip, String path) throws IOException, InterruptedException { + public static List deobfuscateActionList(List listeners, List actions, int version, int ip, String path) throws IOException, InterruptedException { if (actions.isEmpty()) { return actions; } @@ -260,7 +257,7 @@ public class ActionListReader { } } - deobfustaceActionListAtPosRecursive(listeners, new ArrayList(), new HashMap>(), containerSWFOffset, localData, stack, cpool, actionMap, ip, retdups, ip, endIp, path, new HashMap(), false, new HashMap>(), version, 0, maxRecursionLevel); + deobfustaceActionListAtPosRecursive(listeners, new ArrayList(), new HashMap>(), localData, stack, cpool, actionMap, ip, retdups, ip, endIp, path, new HashMap(), false, new HashMap>(), version, 0, maxRecursionLevel); if (!retdups.isEmpty()) { for (int i = 0; i < ip; i++) { @@ -286,7 +283,7 @@ public class ActionListReader { } } - ret = Action.removeNops(0, ret, version, 0, path); + ret = Action.removeNops(0, ret, version, path); List reta = new ArrayList<>(); for (Object o : ret) { if (o instanceof Action) { @@ -591,7 +588,7 @@ public class ActionListReader { return true; } - private static Action readActionListAtPos(List listeners, long containerSWFOffset, ConstantPool cpool, + private static Action readActionListAtPos(List listeners, ConstantPool cpool, SWFInputStream sis, List actions, List nextOffsets, long ip, long startIp, long endIp, int version, String path, boolean indeterminate, List visitedContainers) throws IOException { @@ -646,7 +643,6 @@ public class ActionListReader { listeners.get(i).progress(AppStrings.translate("disassemblingProgress.reading"), pos, length); } - a.containerSWFOffset = containerSWFOffset; a.setAddress(ip, version, false); if (a instanceof ActionPush && cpool != null) { @@ -677,7 +673,7 @@ public class ActionListReader { if (size != 0) { long ip2 = ip + actionLengthWithHeader; //long endIp2 = ip + actionLengthWithHeader + size; - readActionListAtPos(listeners, containerSWFOffset, cpool, + readActionListAtPos(listeners, cpool, sis, actions, nextOffsets, ip2, startIp, endIp, version, newPath, indeterminate, visitedContainers); actionLengthWithHeader += size; @@ -706,7 +702,7 @@ public class ActionListReader { } } - private static void deobfustaceActionListAtPosRecursive(List listeners, List output, HashMap> containers, long containerSWFOffset, ActionLocalData localData, Stack stack, ConstantPool cpool, List actions, int ip, List ret, int startIp, int endip, String path, Map visited, boolean indeterminate, Map> decisionStates, int version, int recursionLevel, int maxRecursionLevel) throws IOException, InterruptedException { + private static void deobfustaceActionListAtPosRecursive(List listeners, List output, HashMap> containers, ActionLocalData localData, Stack stack, ConstantPool cpool, List actions, int ip, List ret, int startIp, int endip, String path, Map visited, boolean indeterminate, Map> decisionStates, int version, int recursionLevel, int maxRecursionLevel) throws IOException, InterruptedException { boolean debugMode = false; boolean decideBranch = false; @@ -879,7 +875,7 @@ public class ActionListReader { } else { localData2 = localData; } - deobfustaceActionListAtPosRecursive(listeners, output2, containers, containerSWFOffset, localData2, new Stack(), cpool, actions, (int) endAddr, ret, startIp, (int) (endAddr + size), path + (cntName == null ? "" : "/" + cntName), visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); + deobfustaceActionListAtPosRecursive(listeners, output2, containers, localData2, new Stack(), cpool, actions, (int) endAddr, ret, startIp, (int) (endAddr + size), path + (cntName == null ? "" : "/" + cntName), visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); output2s.add(output2); endAddr += size; } @@ -939,7 +935,7 @@ public class ActionListReader { @SuppressWarnings("unchecked") Stack substack = (Stack) stack.clone(); - deobfustaceActionListAtPosRecursive(listeners, output, containers, containerSWFOffset, prepareLocalBranch(localData), substack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); + deobfustaceActionListAtPosRecursive(listeners, output, containers, prepareLocalBranch(localData), substack, cpool, actions, ip + actionLen + aif.getJumpOffset(), ret, startIp, endip, path, visited, indeterminate, decisionStates, version, recursionLevel + 1, maxRecursionLevel); } if (newip > -1) { diff --git a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java index d658675f5..4fabeb816 100644 --- a/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java +++ b/src/com/jpexs/decompiler/flash/action/parser/pcode/ASMParser.java @@ -1,503 +1,500 @@ -/* - * 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.pcode; - -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.flashlite.ActionFSCommand2; -import com.jpexs.decompiler.flash.action.flashlite.ActionStrictMode; -import com.jpexs.decompiler.flash.action.parser.ParseException; -import com.jpexs.decompiler.flash.action.special.ActionDeobfuscatePop; -import com.jpexs.decompiler.flash.action.special.ActionNop; -import com.jpexs.decompiler.flash.action.swf3.ActionGetURL; -import com.jpexs.decompiler.flash.action.swf3.ActionGoToLabel; -import com.jpexs.decompiler.flash.action.swf3.ActionGotoFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionNextFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionPlay; -import com.jpexs.decompiler.flash.action.swf3.ActionPrevFrame; -import com.jpexs.decompiler.flash.action.swf3.ActionSetTarget; -import com.jpexs.decompiler.flash.action.swf3.ActionStop; -import com.jpexs.decompiler.flash.action.swf3.ActionStopSounds; -import com.jpexs.decompiler.flash.action.swf3.ActionToggleQuality; -import com.jpexs.decompiler.flash.action.swf3.ActionWaitForFrame; -import com.jpexs.decompiler.flash.action.swf4.ActionAdd; -import com.jpexs.decompiler.flash.action.swf4.ActionAnd; -import com.jpexs.decompiler.flash.action.swf4.ActionAsciiToChar; -import com.jpexs.decompiler.flash.action.swf4.ActionCall; -import com.jpexs.decompiler.flash.action.swf4.ActionCharToAscii; -import com.jpexs.decompiler.flash.action.swf4.ActionCloneSprite; -import com.jpexs.decompiler.flash.action.swf4.ActionDivide; -import com.jpexs.decompiler.flash.action.swf4.ActionEndDrag; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionGetProperty; -import com.jpexs.decompiler.flash.action.swf4.ActionGetTime; -import com.jpexs.decompiler.flash.action.swf4.ActionGetURL2; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionGotoFrame2; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionJump; -import com.jpexs.decompiler.flash.action.swf4.ActionLess; -import com.jpexs.decompiler.flash.action.swf4.ActionMBAsciiToChar; -import com.jpexs.decompiler.flash.action.swf4.ActionMBCharToAscii; -import com.jpexs.decompiler.flash.action.swf4.ActionMBStringExtract; -import com.jpexs.decompiler.flash.action.swf4.ActionMBStringLength; -import com.jpexs.decompiler.flash.action.swf4.ActionMultiply; -import com.jpexs.decompiler.flash.action.swf4.ActionNot; -import com.jpexs.decompiler.flash.action.swf4.ActionOr; -import com.jpexs.decompiler.flash.action.swf4.ActionPop; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ActionRandomNumber; -import com.jpexs.decompiler.flash.action.swf4.ActionRemoveSprite; -import com.jpexs.decompiler.flash.action.swf4.ActionSetProperty; -import com.jpexs.decompiler.flash.action.swf4.ActionSetTarget2; -import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionStartDrag; -import com.jpexs.decompiler.flash.action.swf4.ActionStringAdd; -import com.jpexs.decompiler.flash.action.swf4.ActionStringEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionStringExtract; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLength; -import com.jpexs.decompiler.flash.action.swf4.ActionStringLess; -import com.jpexs.decompiler.flash.action.swf4.ActionSubtract; -import com.jpexs.decompiler.flash.action.swf4.ActionToInteger; -import com.jpexs.decompiler.flash.action.swf4.ActionTrace; -import com.jpexs.decompiler.flash.action.swf4.ActionWaitForFrame2; -import com.jpexs.decompiler.flash.action.swf5.ActionAdd2; -import com.jpexs.decompiler.flash.action.swf5.ActionBitAnd; -import com.jpexs.decompiler.flash.action.swf5.ActionBitLShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitOr; -import com.jpexs.decompiler.flash.action.swf5.ActionBitRShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitURShift; -import com.jpexs.decompiler.flash.action.swf5.ActionBitXor; -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.ActionDecrement; -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.ActionDelete; -import com.jpexs.decompiler.flash.action.swf5.ActionDelete2; -import com.jpexs.decompiler.flash.action.swf5.ActionEnumerate; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionIncrement; -import com.jpexs.decompiler.flash.action.swf5.ActionInitArray; -import com.jpexs.decompiler.flash.action.swf5.ActionInitObject; -import com.jpexs.decompiler.flash.action.swf5.ActionLess2; -import com.jpexs.decompiler.flash.action.swf5.ActionModulo; -import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionPushDuplicate; -import com.jpexs.decompiler.flash.action.swf5.ActionReturn; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionStackSwap; -import com.jpexs.decompiler.flash.action.swf5.ActionStoreRegister; -import com.jpexs.decompiler.flash.action.swf5.ActionTargetPath; -import com.jpexs.decompiler.flash.action.swf5.ActionToNumber; -import com.jpexs.decompiler.flash.action.swf5.ActionToString; -import com.jpexs.decompiler.flash.action.swf5.ActionTypeOf; -import com.jpexs.decompiler.flash.action.swf5.ActionWith; -import com.jpexs.decompiler.flash.action.swf6.ActionEnumerate2; -import com.jpexs.decompiler.flash.action.swf6.ActionGreater; -import com.jpexs.decompiler.flash.action.swf6.ActionInstanceOf; -import com.jpexs.decompiler.flash.action.swf6.ActionStrictEquals; -import com.jpexs.decompiler.flash.action.swf6.ActionStringGreater; -import com.jpexs.decompiler.flash.action.swf7.ActionCastOp; -import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; -import com.jpexs.decompiler.flash.action.swf7.ActionExtends; -import com.jpexs.decompiler.flash.action.swf7.ActionImplementsOp; -import com.jpexs.decompiler.flash.action.swf7.ActionThrow; -import com.jpexs.decompiler.flash.action.swf7.ActionTry; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import com.jpexs.helpers.Helper; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ASMParser { - - public static List parse(long containerSWFOffset, boolean ignoreNops, List